Espamática
ZX SpectrumRetroZ80 Assembly

ZX Spectrum Assembly, Pong – 0x0C Optimisation part 2

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

We have already mentioned that Spirax pointed out several optimisations. We have left for the end one that he showed for the sound routine and another that was implemented after the one he showed for the ReprintPoints routine.

Create a folder called Step12 and copy all the .asm files from the Step11 folder into it.

Translation by Felipe Monge Corbalán

PlaySound

The first optimisation is in the sound routine, specifically in the way we evaluate the sound to be emitted.

Open sound.asm and locate the PlaySound tag, the first lines of which are:

ASM
PlaySound:
; Preserves the value of records
push de
push hl

cp   $01                   ; Sound of a dot?			
jr   z, playSound_point    ; Point sound, emits it

cp   $02                   ; Paddle sound?			
jr   z, playSound_paddle   ; Paddle sound, emits it

In this routine we use CP $01 and CP $02 to check which sound should be output. Each CP instruction occupies 2 bytes and takes seven clock cycles. We replace these instructions with DEC A, which occupies 1 byte and takes 4 clock cycles, so we save 2 bytes and 6 clock cycles. DEC does change the value of the A register, but since we are concerned with the type of sound to be emitted, it does not affect us.

Let’s see how the routine starts.

ASM
PlaySound:
; Preserves the value of records
push de
push hl

; Spirax
dec  a                     ; Decreasing A
jr   z, playSound_point    ; If 0, point sound
; Spirax
dec  a                     ; Decreasing A		
jr   z, playSound_paddle   ; If 0, paddle sound

We preserve DE, PUSH DE, HL, PUSH HL, and decrement A, DEC A. If A was one, the result of the operation is zero and jumps to play the sound, JR Z, playSound_point.

If A was not one, the checks continue. We decrement A, DEC A, and if the result of the operation is zero, it jumps to play the sound, JR Z, playSound_paddle. If it jumps, A was initially worth two, one with the first decrement and zero with this second.

If it does not jump, it remains as it was and emits the rim sound.

Compile, load into the emulator and check that it still works.

ReprintPoints

With this optimisation we save 20 bytes and one hundred and seven clock cycles. To achieve this, we are going to use the same method we used in step 10, following Spirax’s comments; the way he pointed out.

As you may recall from step 10, we added a label so that the painting of player two’s marker could be called independently; we will do the same for player one’s marker. With this modification, the markers will take a little longer to paint (they are only painted at the start of the game and when marking a point), but we will simplify the ReprintPoints routine, saving bytes and clock cycles by eliminating redundant code.

Let’s start by modifying the PrintPoints routine so that it can be called to print both players’ markers independently.

Open video.asm and locate the PrintPoints tag. Underneath it we add another tag, the one we will call to paint the marker for player two:

ASM
printPoint_1_print:

Between the PrintPoints and printPoint_1_print tags we add the calls to paint the marker for each player:

ASM
call printPoint_1_print    ; Paints the marker of player 1
jr   printPoint_2_print    ; Paints the marker of player 2

We call to print player one’s marker, CALL printPoint_1_print, and then jump to print player two’s marker, JR printPoint_1_print.

There is only one more change we need to make in PrintPoints. We add RET just before the printPoint_2_print tag so that CALL printPoint_1_print is output correctly; remember that the rest of the jumps are output by the PrintPoint RET.

We add RET before printPoint_2_print:

ASM
ret
printPoint_2_print:

We have finished the necessary modifications to PrintPoints, but we have not saved anything, we have added code by adding bytes and clock cycles.

Let’s start saving. We delete reprintPoint_1_print and the following lines until we reach the reprintPoint_2 tag; we do not delete it.

We locate the ReprintPoints tag and nine lines down we find the JR Z, reprintPoint_1_print statement. This tag no longer exists, so we need to change this line and leave it as follows:

ASM
jr   z, printPoint_1_print

We locate the reprintPoint_1 tag and make the final changes.

The code for this tag, after deleting the entire reprintPoint_1_print part, is as follows:

ASM
reprintPoint_1:
cp   POINTS_X1_R           ; Compare right boundary of marker 1
jr   c, reprintPoint_1_print ; Carry? Pass through marker 1, paint
jr   nz, reprintPoint_2    ; !=0? Passes right, jumps

We need to change the double check. As the reprintPoint_2 tag is now below the JR NZ, reprintPoint_2 line, this jump is no longer necessary, but we do need to check if it is zero, in which case we need to paint the player a marker, JR Z, printPoint_1_print, and change the jump from JR C, reprintPoint_1_print to JR C, printPoint_1_print, so the code would look like this:

ASM
reprintPoint_1:
cp   POINTS_X1_R           ; Compare with right boundary marker 1
jr   z, printPoint_1_print
jr   c, printPoint_1_print ; 0 or carry? Pass marker 1, paint

The final appearance of PrintPoints and ReprintPoints is as follows:

ASM
; -------------------------------------------------------------------
; Paint the scoreboard.
; Each number is 1 byte wide by 16 bytes high.
; Alters the value of the AF, BC, DE and HL registers.
; -------------------------------------------------------------------
PrintPoints:
call printPoint_1_print    ; Paints marker player 1
jr   printPoint_2_print    ; Paints marker player 2

printPoint_1_print:		
ld   a, (p1points)         ; A = points player 1
call GetPointSprite        ; Sprite to be painted on marker
; 1st digit of player 1
ld   e, (hl)               ; E = lower part 1st digit address
inc  hl                    ; HL = high side
ld   d, (hl)               ; D = upper part
push hl                    ; Preserves HL
ld   hl, POINTS_P1         ; HL = address where to paint digit
call PrintPoint            ; Paints 1st digit	
pop  hl                    ; Retrieves HL

; 2nd digit of player 1	
inc  hl                    ; HL = low part 2nd digit address
ld   e, (hl)               ; E = lower part 
inc  hl                    ; HL = high part
ld   d, (hl)               ; D = upper part
; Spirax
ld   hl, POINTS_P1 + 1     ; HL = address where to paint digit 
call PrintPoint            ; Paint2 2nd digit

ret

printPoint_2_print:	
; 1st digit of player 2
ld   a, (p2points)         ; A = points player 2
call GetPointSprite        ; Sprite to be painted on marker
ld   e, (hl)               ; E = low part 1st digit address
inc  hl                    ; HL = high part
ld   d, (hl)               ; D = upper part
push hl                    ; Preserves HL
ld   hl, POINTS_P2         ; HL = address where digit
call PrintPoint            ; Paints 1st digit
pop  hl                    ; Retrieves HL

; 2nd digit of player 2	
inc  hl                    ; HL = low part 2nd digit address
ld   e, (hl)               ; E = lower part
inc  hl                    ; HL = high part
ld   d, (hl)               ; D = upper part
; Spirax
ld   hl, POINTS_P2 + 1     ; HL address where to paint 2nd digit
; Paints the second digit of player 2's marker.

PrintPoint:
ld   b, $10                ; Each digit 1 byte by 16 (scanlines)

printPoint_printLoop:
ld   a, (de)               ; A = byte to be painted
ld   (hl), a               ; Paints the byte
inc  de                    ; DE = next byte
call NextScan              ; HL = next scanline
djnz printPoint_printLoop  ; Until B = 0

ret

; -------------------------------------------------------------------
; Repaint the scoreboard.
; Each number is 1 byte wide by 16 bytes high.
; Alters the value of the AF, BC, DE and HL registers.
; -------------------------------------------------------------------
ReprintPoints:
ld   hl, (ballPos)         ; HL = ball position
call GetPtrY               ; Third, line and scanline ball 	position
cp   POINTS_Y_B            ; Compare lower limit marker
ret  nc                    ; No Carry? Pass underneath
ld   a, l                  ; A = line and column ball position
and  $1f                   ; A = column
cp   POINTS_X1_L           ; Compare left boundary marker 1
ret  c                     ; Carry? Pass left
jr   z, printPoint_1_print ; 0? It's in left margin, paint

cp   POINTS_X2_R           ; Compare right boundary marker 2
jr   z, printPoint_2_print ; 0? It's in the right margin, paint
ret  nc                    ; No Carry? Pass on the right

reprintPoint_1:
cp   POINTS_X1_R           ; Compare right boundary marker 1
jr   z, printPoint_1_print	
jr   c, printPoint_1_print ; Z or Carry? Pass marker 1, paint
 
reprintPoint_2:					
cp   POINTS_X2_L           ; Compare right boundary marker 2
ret  c                     ; Carry? Pass on the left
; Spirax
jr   printPoint_2_print    ; Paint marker player 2

If we compare the implementation of ReprintPoints with the one we did in step 10, we can see that the routine is much simpler, being practically reduced to the built-in checks, so that the marker is repainted only when necessary.

All that’s left is to compile, load in the emulator and check that everything still works.

Or do you want to add a loading screen?

ZX Spectrum Assembly, Pong

In the next ZX Spectrum Assembly chapter, we will add a loading screen to ZX-Pong.

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