Espamática
ZX SpectrumRetroZ80 Assembly

ZX Spectrum Assembly, Pong – 0x0B Sound and 16K

In this ZX Spectrum Assembly chapter, we will implement the sound effects.

Sound

We have implemented sound effects when the ball hits the borders, the paddles and when a point is scored.

We create the folder Step11 and copy from the folder Step10: controls.asm, game.asm, main.asm, sprite.asm and video.asm. We create sound.asm to add the necessary counters and routines for our sound effects.

We will define three different sounds:

  • When a point is marked.
  • When the ball hits a paddle.
  • When the ball hits the border.

For each sound, we need to define the note and the frequency. The frequency defines how long the note lasts; we identify it with the suffix FQ.

; Point
C_3:    EQU $0D07
C_3_FQ: EQU $0082 / $10

; Paddle
C_4:    EQU $066E
C_4_FQ: EQU $0105 / $10

; Border
C_5:    EQU $0326
C_5_FQ: EQU $020B / $10

All the sounds we are going to use are C, but in different scales; the larger the scale, the higher the pitch.

The frequencies given are those that make the note last one second, so we’ll divide them by 16. If we multiply them by 2, the note would last 2 seconds.

Each note in each scale has its own frequency. Appendix one contains tables of frequencies and notes, in decimal and hexadecimal.

The next constant is the memory address where the ROM BEEPER routine is located:

BEEPER: EQU $03B5

This routine receives the note in HL and the duration in DE, and changes the value of registers AF, BC, DE, HL and IX, as well as another aspect that we will see later.

Because the ROM BEEPER routine modifies so many registers, it is advisable not to call it directly; we implement a routine that does this.

This routine receives in A the type of sound to be emitted and does not change the value of any register:

  • 1 = point
  • 2 = paddle
  • 3 = border

We preserve the value of the DE, PUSH DE, and HL, PUSH HL, registers.

cp   $01
jr   z, playSound_point

We check if the sound to be played is of type 1 (point), CP $01, and if so we skip, JR Z, playSound_point.

cp   $02
jr   z, playSound_paddle

If the sound is not type 1, we check if it is type 2 (paddle), CP $02, and if so we jump, JR Z, playSound_paddle.

If the sound is neither type 1 nor type 2, it is type 3 (border):

ld   hl, C_5
ld   de, C_5_FQ
jr   beep

We load the note in HL, LD HL, C_5, the duration in DE, LD DE, C_5_FQ, and play the sound, JR beep.

If the sound is of type 1 or 2, we do the same, with the values of each sound:

playSound_point:
ld   hl, C_3
ld   de, C_3_FQ
jr   beep

playSound_paddle:
ld   hl, C_4
ld   de, C_4_FQ

We are spared the last JR, as it is immediately followed by the routine that plays the sound:

beep:
push af
push bc
push ix
call BEEPER
pop  ix
pop  bc
pop  af

pop  hl
pop  de

ret

We keep AF, PUSH AF, BC, PUSH BC, and IX, PUSH IX. We then call the ROM routine, CALL BEEPER, and retrieve IX, POP IX, BC, POP BC, AF, POP AF, HL, POP HL, and DE, POP DE. HL and DE are retained at the beginning of the PlaySound routine. We exit with, RET.

The final appearance of the sound.asm file is as follows:

; -------------------------------------------------------------------
; Sound.asm
; File with the sounds
; -------------------------------------------------------------------
; Point
C_3:    EQU $0D07
C_3_FQ: EQU $0082 / $10

; Paddle
C_4:    EQU $066E
C_4_FQ: EQU $0105 / $10

; Rebound
C_5:    EQU $0326
C_5_FQ: EQU $020B / $10

; -------------------------------------------------------------------
; ROM beeper routine.
;
; Input: HL -> Note.
;        DE -> Duration.
;
; Alters the value of the AF, BC, DE, HL and IX registers.
; -------------------------------------------------------------------
BEEPER: EQU $03B5

; -------------------------------------------------------------------
; Reproduces the sound of bouncing.
; Input: A -> Sound type: 1. Dot
;                         2. Paddle
;                         3. Border
; -------------------------------------------------------------------
PlaySound:
; Preserves the value of records
push de
push hl

cp   $01                   ; Evaluates sound dot
jr   z, playSound_point    ; Sound point? Jump

cp   $02                   ; Evaluates sound paddle
jr   z, playSound_paddle   ; Sound paddle? Jump

; The edge sound is emitted
ld   hl, C_5               ; HL = note
ld   de, C_5_FQ            ; DE = duration (frequency)
jr   beep                  ; Jumps to beep

; The sound of Dot is emitted
playSound_point:
ld   hl, C_3               ; HL = note
ld   de, C_3_FQ            ; DE = duration (frequency)
jr   beep                  ; Jumps to beep

; The paddle sound is emitted
playSound_paddle:
ld   hl, C_4               ; HL = note
ld   de, C_4_FQ            ; DE = duration (frequency)

; Sounds the note
beep:
; Preserves registers; ROM BEEPER routine alters them.
push af
push bc
push ix

call BEEPER                ; Call BEEPER from ROM

; Retrieves the value of the registers
pop  ix
pop  bc
pop  af

pop  hl
pop  de

ret

Now we need to call our new routine to play the sounds of the ball bouncing.

Open game.asm and locate checkBallCross_right. We will add two lines between RET NZ and LD A, (ballSetting):

ld   a, $02
call PlaySound

We load the sound type in A, LD A, $02; we play it, CALL PlaySound.

We find the tag checkBallCross_left. Let’s add the same two lines between RET NZ and LD A, (ballSetting):

ld   a, $02
call PlaySound

We locate the moveBall_upChg tag. Below it, we add two lines, almost the same as above:

ld   a, $03
call PlaySound

Locate the moveBall_downChg tag and add the above two lines just below it:

ld   a, $03
call PlaySound

Locate the moveBall_rightChg tag below add:

ld   a, $01
call PlaySound

Five lines below that is CALL SetBallLeft; below that we add:

ld   a, $03
call PlaySound

Locate the moveBall_leftChg tag; below add:

ld   a, $01
call PlaySound

Five lines down is CALL SetBallRight, which we add just below:

ld   a, $03
call PlaySound

Finally, open main.asm, locate the Loop routine and add the following lines just above it:

ld   a, $03
call PlaySound

We go to the end of the file, in the «includes» part, we include the file sound.asm:

include "sound.asm"

If all goes well, we have reached the end. We compile, load into the emulator and…

ZX Spectrum Assembly, Pong
ZX Spectrum Assembly, Pong

What about the border, why is it white? We have already seen that the ROM’s BEEPER routine changes a lot of things, and one of them is the colour of the border, although it has a simple solution.

Fortunately, we have a system variable where we can store the border colour. The attributes of the bottom screen are also stored in this variable. The background of the bottom screen is the border colour.

We open video.asm and declare a constant at the top with the memory address of this system variable:

BORDCR: EQU $5c48

Locate the Cls routine, and add it just before the INC HL line:

ld    a, $07               ; Black background, white ink

We modify the line LD (HL), $07 and leave it as follows:

ld   (hl), a

Finally, before RET, we add:

ld   (BORDCR), a

Compile, load into the emulator, and you’re done – have we finished our ZX-Pong?

ZX Spectrum Assembly, Pong
ZX Spectrum Assembly, Pong

16K compatibility

Is our programme compatible with the 16K model? Not yet, but since we’re not using interrupts, it’s very easy to make it compatible.

We open main.asm, find the two directives ORG and END, and replace $8000 with $5dad in ORG. In END, we replace the $8000 address with Main, the program entry label.

If we compile and load in the 16K model, our programme is compatible.

If we look closely, we can see that we have lost some speed. This loss is due to the fact that the second 16K of the ZX Spectrum, where we are now loading the program, is the so-called contained memory, which is shared with the ULA. When the ULA is working, everything stops.

We are going to change the speed at which the ball moves again.

Open sprite.asm, find ballSetting, comment out the line or $21 and write just below it:

; or   $21
or   $19

Now the ball starts at speed 3, which is the slowest speed.

Open game.asm, find SetBallLeft, go to line 7, comment it out and write just below it:

; or   $21
or   $19

If we now set the ball to come out of the left side of the screen, it will start at speed 3.

Find SetBallRight, comment out line 7 and type just below it:

; db   $61
db   $59

If we now set the ball to come out of the right side of the screen, it will start at speed 3.

Find the tag checkCrossY_1_5, comment out line 7 and write just below it:

; or   $21
or   $19

Now the ball speed is 3 instead of 4.

Find the tag checkCrossY_2_5, comment out line 7 and write just below it:

; or   $1a
or   $12

Now the ball speed is 2 instead of 3.

Find the tag checkCrossY_3_5, comment out line 7 and write just below it:

; or   $17
or   $0f

Now the ball speed is 1 instead of 2.

Find the tag checkCrossY_4_5, comment out line 7 and write just below it:

; or   $9a
or   $92

Now the ball speed is 2 instead of 3.

Find the tag checkCrossY_5_5, comment out line 3 and write just below it:

; or   $a1
or   $99

Now the ball speed is 3 instead of 4.

We compiled it, tested it in the emulator and we’re almost done.

ZX Spectrum Assembly, Pong

In the next ZX Spectrum Assembly chapter, we will implement further optimisations.

Download the source code from here.

ZX Spectrum Assembly, Pong by Juan Antonio Rubio García.
Translation by Felipe Monge Corbalán.
This work is licensed to Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0).
Any comments are always welcome.



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies

Descubre más desde Espamática

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo