0x06 Ensamblador ZX Spectrum Pong – Campo, palas, bola y temporización

Llegamos a una nueva entrega de Ensamblador ZX Spectrum Pong, y con ella inauguramos la segunda parte del tutorial. En esta entrega vamos a dibujar el campo, las palas, la bola, lo vamos a mover todo y a temporizar.

Ensamblador ZX Spectrum Pong – Campo, palas, bola y temporización

Creamos la carpeta Paso06 y copiamos desde la carpeta Paso05 los archivos Game.asm, Sprite.asm y Video.asm, y desde la carpeta Paso03 el archivo Controls.asm. También creamos el archivo Main.asm.

Bucle principal

Empezamos editando el archivo Main.asm, indicando la posición de carga, poniendo el borde en negro, limpiando la pantalla, pintando la línea central y haciendo un bucle infinito para no volver al Basic.

También vamos a incluir el resto de ficheros e indicarle a PASMO dónde llamar al cargar el programa.

org		$8000

Main:
ld		a, $00
out		($fe), a
call	Cls
call	PrintLine

Loop:
jr		Loop

include "Controls.asm"
include "Game.asm"
include "Sprite.asm"
include "Video.asm"

end		$8000

Compilamos y vemos el resultado en el emulador.

Ensamblador ZX Spectrum Pong - Bucle principal
Ensamblador ZX Spectrum, bucle principal

Borde del campo

El siguiente paso es pintar el borde del campo.

Incluimos al inicio del fichero Sprite.asm una nueva constante.

FILL:	EQU	$ff

La rutina para pintar el borde la implementamos en el archivo Video.asm, antes de la rutina PrintLine.

PrintBorder:
ld		hl, $4100
ld		de, $56e0
ld		b, $20
ld		a, FILL

Cargamos en HL la dirección del tercio 0, línea 0, scanline 1, LD HL, $4100, cargamos en DE la dirección del tercio 2, línea 7, scanline 6, LD DE, $56E0, y en B las 32 columnas en las que pintar el borde, LD B, $20. Por último, cargamos el sprite del borde en A, LD A, FILL.

Implementamos el bucle para pintar el borde.

printBorder_loop:
ld		(hl), a
ld		(de), a
inc		l
inc		e
djnz	printBorder_loop
ret

Pintamos el sprite del borde en la dirección a la que apunta HL, LD (HL), A, y hacemos lo mismo con la dirección a la que apunta DE, LD (DE), A.

Apuntamos HL a la siguiente columna, INC L, y hacemos los mismo con DE, INC E. Repetimos hasta que B valga 0, DJNZ printBorder_loop, tras lo cual salimos de la rutina, RET.

El aspecto final de la rutina PrintBorder es el siguiente.

; -----------------------------------------------------------------------------
; Pinta el borde del campo.
; Altera el valor de los registros AD, B, DE y HL.
; -----------------------------------------------------------------------------
PrintBorder:
ld      hl, $4100			; Carga en HL la dirección del tercio 0, línea 0 y scanline 1
ld      de, $56e0			; Carga en DE la dirección del tercio 2, línea 7 y scanline 6
ld      b, $20				; Carga en B las 32 columnas en las que pintar
ld      a, FILL				; Carga en A el byte a pintar

printBorder_loop:
ld		(hl), a				; Pinta en la dirección apuntada por HL
ld		(de), a				; Pinta en la dirección apuntada por DE
inc     l					; Apunta HL a la siguiente columna
inc     e					; Apunta DE a la siguiente columna
djnz    printBorder_loop	; Bucle hasta que B llegue a 0
ret

Para probar esta rutina, volvemos al archivo Main.asm y tras la llamada a PrintLine ponemos la llamada a la nueva rutina.

call	PrintBorder

Compilamos y vemos los resultados en el emulador.

Ensamblador ZX Spectrum Pong - Borde del campo.png
Ensamblador ZX Spectrum, borde del campo

Ya tenemos dibujado el campo donde se va a desarrollar la acción.

Introducimos la bola

Vamos a introducir la bola en nuestro campo. Como la vamos a estar moviendo y pintando constantemente, vamos a introducir las llamadas a mover y pintar la bola dentro del bucle, entre Loop y JR Loop.

call	MoveBall
call	PrintBall

Compilamos y vemos los resultados en el emulador.

Ensamblador ZX Spectrum Pong - La bola borra el campo
Ensamblador ZX Spectrum, la bola borra el campo

Al ver el resultado, observamos dos problemas: la bola borra la línea central y el borde, y se mueve a una velocidad endiablada.

Velocidad de la bola

Lo primero que vamos a abordar es la velocidad a la que se mueve la bola.

En el paso anterior poníamos un HALT para esperar el refresco de la pantalla, pero esto hace que vaya demasiado lenta. Para reducir la velocidad de la bola, vamos a hacer que no se mueva cada vez que pase por el bucle; se va a mover una de cada N veces.

Seguimos en el archivo Main.asm y, antes de END $8000, declaramos la variable donde vamos a llevar la cuenta de las veces que se ha pasado por el bucle.

countLoopBall:	db	$00

Y ahora vamos a implementar la parte en la que comprobamos si ha pasado las veces suficientes para que movamos la bola, justo después de la etiqueta Loop.

ld		a, (countLoopBall)
inc		a
ld		(countLoopBall), a
cp		$0f
jr		nz, loop_continue
call	MoveBall

Cargamos el contador donde guardamos las veces que se ha pasado por el bucle sin mover la bola en A, LD A, (countLoopBall), lo incrementamos, INC A, y lo guardamos en memoria, LD (countLoopBall), A.

Comparamos si el contador ha llegado al número de veces necesarias para mover la bola, CP $0F, y si no ha llegado salta, JR NZ, loop_continue.

Si ya hemos llegado al número de veces necesarias de pasadas por el bucle para mover la bola, la movemos, CALL MoveBall.

La etiqueta loop_continue es nueva y la vamos a poner justo encima de la llamada a PrintBall.

loop_continue:
call	PrintBall

Tenemos que hacer una última cosa. Si el contador ha llegado al número de veces necesario para mover la bola, después de moverla hay que volver a poner el contador a cero, de lo contrario habría que esperar otras 255 veces, en lugar de las que hemos puesto.

Añadimos las siguientes líneas después de la llamada a MoveBall y antes de la etiqueta loop_continue para poner el contador a 0.

ld	a, ZERO
ld	(countLoopBall), a

La implementación de Main.asm quedaría así.

org		$8000
Main:
ld		a, $00
out		($fe), a			; Pone el borde en negro

call	Cls					; Limpia la pantalla
call	PrintLine			; Pinta la línea central
call	PrintBorder			; Pinta el borde

Loop:
ld		a, (countLoopBall)	; Carga en A el contador de la bola
inc		a					; Incrementa el contador
ld		(countLoopBall), a	; Carga el valor en memoria
cp		$0f					; Comprueba si el contador ha llegado a 15
jr		nz, loop_continue	; Si no ha llegado, salta
call	MoveBall			; Mueve la posición de la bola
ld		a, ZERO				; Pone A = 0
ld		(countLoopBall), a	; Carga el valor en memoria

loop_continue:
call	PrintBall			; Pinta la bola
jr		Loop				; Bucle infinito

include "Game.asm"
include "Controls.asm"
include "Sprite.asm"
include "Video.asm"

countLoopBall:	db $00		; Contador para controlar cuando se mueve la bola
end		$8000

Compilamos y vemos el resultado en el emulador. Esta vez sí vemos como se mueve la bola a una velocidad más aceptable.

No hemos definido una constante para la comparación con el contador de la bola ya que, en un futuro, la velocidad será variable.

Repintamos línea central y borde

Ahora vamos a abordar el problema de las partes que va borrando la bola a su paso, y vamos a empezar por la línea central.

En una primera aproximación, vamos a repintar la parte de la línea que coincide en la coordenada Y con la bola, sin importar si la bola está pasando por encima o no. Parece innecesario, pero nos va a ayudar a temporizar.

Abrimos el archivo Video.asm e implementamos después de la rutina PrintLine.

ReprintLine:
ld		hl, (ballPos)
ld		a, l
and		$e0
or		$10
ld		l, a

Cargamos la posición de la bola en HL, LD HL, (ballPos), cargamos línea y columna en A, LD A, L, nos quedamos con el valor de la línea, AND $E0, ponemos la columna a 16, que es donde está la línea vertical, OR $10, y cargamos el valor en L, LD L, A.

Vamos a repintar 6 scanlines, que son los mismos que tiene la bola.

ld		b, $06
reprintLine_loop:
ld		a, h

Cargamos en B el número de scanlines que se repintan, LD B, $06, y cargamos tercio y scanline en A, LD A, H.

Para pintar la línea, en los scanlines 0 y 7 pintábamos en blanco, y en el resto la parte visible de la línea.

and		$07
cp		$01
jr		c, reprintLine_00
cp		$07
jr		z, reprintLine_00

Nos quedamos con la parte del scanline, AND $07, y comprobamos si es 1, CP $01. Si el scanline en menor que 1 saltamos, JR C, reprintLine_00, en el caso contrario comprobamos si es 7, CP $07. Si el scanline es 7 saltamos, JR Z, reprintLine_00.

Si no hemos saltado, el scanline está entre 1 y 6.

ld		c, LINE
jr		reprintLine_loopCont

Cargamos el sprite de la línea en C, LD C, LINE, y saltamos, JR reprintLine_loopCont.

Si anteriormente saltamos, el scanline es 0 o 7.

reprintLine_00:
ld		c, ZERO

Cargamos el sprite en blanco en C, LD C, ZERO, y pintamos lo que corresponda.

reprintLine_loopCont:
ld		a, (hl)
or		c
ld		(hl), a	
call	NextScan
djnz	reprintLine_loop

ret

Cargamos el valor de la dirección de memoria del byte que vamos a repintar en A, LD A, (HL), le añadimos los píxeles del repintado de la línea, OR C, y lo pintamos en pantalla, LD (HL), A. Calculamos la dirección de memoria del scanline siguiente, CALL NextScan, y repetimos la operación hasta que B valga 0, DJNZ reprintLine_loop. Por último, salimos, RET.

El aspecto final de la rutina es el siguiente.

; -----------------------------------------------------------------------------
; Repinta la línea central.
; Altera el valor de los registros AF, BC y HL.
; -----------------------------------------------------------------------------
ReprintLine:
ld		hl, (ballPos)			; Carga en HL la posición de la bola
ld		a, l					; Carga la línea y columna en A
and		$e0						; Se queda con la línea
or		$10						; Pone la columna a 16 ($10)
ld		l, a					; Carga el valor en L. HL = Posición inicial repintar

ld		b, $06					; Se repintan 6 scanlines
reprintLine_loop:
ld		a, h					; Carga tercio y scanline en A
and		$07						; Se queda con el scanline
; Si está en los scanline 0 o 7 pinta ZERO
; Si está en los scanline 1, 2, 3, 4, 5 o 6 pinta LINE
cp		$01						; Comprueba si está en scanline 1 o superior
jr		c, reprintLine_00		; Si está por debajo, pinta $00
cp		$07						; Comprueba si está en scanline 7
jr		z, reprintLine_00		; Si es así, pinta ZERO

ld		c, LINE					; Esta en scanline 1 a 6, pinta LINE
jr		reprintLine_loopCont	; Salta
reprintLine_00:
ld		c, ZERO					; Está en scanline 0 o 7, pinta ZERO
reprintLine_loopCont:
ld		a, (hl)					; Obtiene los pixeles de la posición actual
or		c						; Los mezcla con C
ld		(hl), a					; Pinta el resultado en la posición actual
call	NextScan				; Obtiene el scanline siguiente
djnz	reprintLine_loop		; Hasta que B = 0

ret

Podemos ahorrar 4 bytes y 19 ciclos de reloj modificando seis líneas de la rutina. Lo dejamos en vuestras manos y veremos la forma de hacerlo al final del tutorial.

Ya solo queda probar lo que hemos implementado, para lo cual abrimos el archivo Main.asm y después de la llamada a PrintBall incluimos la llamada a ReprintLine.

call	ReprintLine

Compilamos y vemos los resultados en el emulador.

Ensamblador ZX Spectrum Pong - Repinta la línea
Ensamblador ZX Spectrum, repinta la línea

La línea central ya no se borra, pero podemos apreciar que la velocidad de la bola ha disminuido. Hay que tener en cuenta que ahora realizamos más operaciones que antes. Según avancemos iremos ajustando la velocidad de la bola.

Vamos ahora a evitar que se borre el borde, para lo cual vamos a modificar los límites superior e inferior de la bola, en el fichero Sprite.asm.

BALL_BOTTOM:	EQU	$b8
BALL_TOP:		EQU	$02

Compilamos, cargamos en el emulador y comprobamos que ya no se borra el borde.

Incluimos las palas

Ahora vamos a empezar con las palas. Volvemos a Main.asm y añadimos las siguientes líneas entre CALL ReprintLine y JR Loop.

ld		hl, (paddle1pos)
call	PrintPaddle
ld		hl, (paddle2pos)
call	PrintPaddle

Cargamos la posición de la pala 1 en HL, LD HL, (paddle1pos), y la pintamos, CALL PrintPaddle. Hacemos lo mismo con la pala 2.

Como se puede apreciar, las palas se pintan en todas las iteraciones del bucle, al igual que la bola y el repintado de línea.

Compilamos y vemos los resultados en el emulador.

Ensamblador ZX Spectrum Pong - No borra el borde
Ensamblador ZX Spectrum, no borra el borde

Se dibujan las palas y la bola no las borra al pasar. También se aprecia que ahora la bola va mucho más lenta, debido a que hacemos más operaciones en cada iteración del bucle.

Para que la bola vuelva a ir más rápida, vamos a cambiar en Main.asm el valor que tenía que alcanzar el contador para que la bola se moviese.

ld		(countLoopBall), a
cp		$06						; ¡CAMBIO!
jr		nz, loop_continue

Compilamos, cargamos en el emulador y comprobamos que la bola vuelve a ir más rápido.

Movemos las palas

Ahora vamos a implementar la rutina para mover las palas; ya vimos como hacerlo en el paso 03. Editamos el archivo Game.asm y vamos al final del mismo.

La rutina que vamos a implementar, recibe en el registro D las pulsaciones de las teclas de control.

MovePaddle:
bit		$00, d
jr		z, movePaddle_1Down

Evaluamos si se ha pulsado la tecla arriba del jugador 1, BIT $00, D. Si no se ha pulsado saltamos a comprobar si se ha pulsado la tecla abajo, JR Z, movePaddle_1Down.

Si no salta, se ha pulsado la tecla arriba del jugador 1.

ld		hl, (paddle1pos)
ld		a, PADDLE_TOP
call	CheckTop
jr		z, movePaddle_2Up

Cargamos la posición de la pala 1 en HL, LD HL, (paddle1pos), el límite superior para las palas en A, LD A, PADDLE_TOP, y comprobamos si se ha alcanzado, CALL CheckTop. Si se ha alcanzado el límite, saltamos a comprobar los controles del jugador 2, JR Z, movePaddle_2Up.

Si no se ha alcanzado el límite superior, movemos la pala 1.

call	PreviousScan
ld		(paddle1pos), hl
jr		movePaddle_2Up

Calculamos la nueva posición para la pala 1, CALL PreviousScan, la cargamos en memoria, LD (paddle1pos), HL, y saltamos a comprobar los controles del jugador 2, JR movePaddle_2Up.

Si no se ha pulsado la tecla arriba del jugador 1, comprobamos si se ha pulsado la tecla abajo.

movePaddle_1Down:
bit		$01, d 
jr		z, movePaddle_2Up

Evaluamos si se ha pulsado la tecla abajo del jugador 1, BIT $01, D. Si no se ha pulsado saltamos a comprobar los controles del jugador 2, JR Z, movePaddle_2Up.

Si no salta, se ha pulsado la tecla abajo del jugador 1.

ld		hl, (paddle1pos)
ld		a, PADDLE_BOTTOM
call	CheckBottom
jr		z, movePaddle_2Up

Cargamos la posición de la pala 1 en HL, LD HL, (paddle1pos), el límite inferior para las palas en A, LD A, PADDLE_BOTTOM, y comprobamos si se ha alcanzado, CALL CheckBottom. Si se ha alcanzado el límite saltamos a comprobar los controles del jugador 2, JR Z, movePaddle_2Up.

Si no se ha alcanzado el límite inferior, mueve la pala 1.

call	NextScan
ld		(paddle1pos), hl

Calculamos la nueva posición para la pala 1, CALL NextScan, y la cargamos en memoria, LD (paddle1pos), HL.

Hacemos las comprobaciones con los controles del jugador 2. Dada la semejanza, simplemente marcamos los cambios con respecto a la comprobación del jugador 1.

movePaddle_2Up:
bit		$02, d					; ¡CAMBIO!
jr		z, movePaddle_2Down		; ¡CAMBIO!
ld		hl, (paddle2pos)		; ¡CAMBIO!
ld		a, PADDLE_TOP
call	CheckTop
jr		z, movePaddle_End		; ¡CAMBIO!
call	PreviousScan
ld		(paddle2pos), hl		; ¡CAMBIO!
jr		movePaddle_End			; ¡CAMBIO!

movePaddle_2Down:				; ¡CAMBIO!
bit		$03, d					; ¡CAMBIO!
jr		z, movePaddle_End		; ¡CAMBIO!
ld		hl, (paddle2pos)		; ¡CAMBIO!
ld		a, PADDLE_BOTTOM
call	CheckBottom
jr		z, movePaddle_End		; ¡CAMBIO!
call	NextScan
ld		(paddle2pos), hl		; ¡CAMBIO!

movePaddle_End:					; ¡NUEVA!
ret								; ¡NUEVA!

El aspecto final de la rutina es el siguiente.

; -----------------------------------------------------------------------------
; Calcula la posición de las palas para moverlas.
; Entrada:	D = Pulsaciones de los controles
; Altera el valor de los registros AF y HL.
; -----------------------------------------------------------------------------
MovePaddle:
bit		$00, d 				; Evalúa si se ha pulsado la A
jr		z, movePaddle_1Down	; Si no se ha pulsado salta
ld		hl, (paddle1pos)	; Carga en HL la posición de la pala 1
ld		a, PADDLE_TOP		; Carga en A el margen superior
call	CheckTop			; Evalúa si se ha alcanzado el margen superior
jr		z, movePaddle_2Up	; Si se ha alcanzado, salta
call	PreviousScan		; Obtiene el scanline anterior a la posición de la pala 1
ld		(paddle1pos), hl	; Carga en memoria la nueva posición de la pala 1
jr		movePaddle_2Up		; Salta

movePaddle_1Down:
bit		$01, d				; Evalúa si se ha pulsado la Z               
jr		z, movePaddle_2Up	; Si no se ha pulsado salta
ld		hl, (paddle1pos)	; Carga en HL la posición de la pala 1
ld		a, PADDLE_BOTTOM	; Carga en A el margen inferior
call	CheckBottom			; Evalúa si se ha alcanzado el margen inferior
jr		z, movePaddle_2Up	; Si se ha alcanzado, salta
call	NextScan			; Obtiene el scanline siguiente a la posición de la pala 1
ld		(paddle1pos), hl	; Carga en memoria la nueva posición de la pala 1

movePaddle_2Up:
bit		$02, d				; Evalúa si se ha pulsado el 0
jr		z, movePaddle_2Down	; Si no se ha pulsado salta
ld		hl, (paddle2pos)	; Carga en HL la posición de la pala 2
ld		a, PADDLE_TOP		; Carga en A el margen superior
call	CheckTop			; Evalúa si se ha alcanzado el margen superior
jr		z, movePaddle_End	; Si se ha alcanzado, salta
call	PreviousScan		; Obtiene el scanline anterior a la posición de la pala 2
ld		(paddle2pos), hl	; Carga en memoria la nueva posición de la pala 2
jr		movePaddle_End		; Salta

movePaddle_2Down:
bit		$03, d				; Evalúa si se ha pulsado la O
jr		z, movePaddle_End	; Si no se ha pulsado salta
ld		hl, (paddle2pos)	; Carga en HL la posición de la pala 2
ld		a, PADDLE_BOTTOM	; Carga en A el margen inferior
call	CheckBottom			; Evalúa si se ha alcanzado el margen inferior
jr		z, movePaddle_End	; Si se ha alcanzado, salta
call	NextScan			; Obtiene el scanline siguiente a la posición de la pala 2
ld		(paddle2pos), hl	; Carga en memoria la nueva posición de la pala 2

movePaddle_End:
ret

Podemos ahorrar 2 bytes y 2 ciclos de reloj, de la misma forma que en la entrega anterior. Esta vez no daremos la solución al final del tutorial, ya que es similar a la que se verá para la entrega anterior.

Para terminar, vamos a implementar en Main.asm las llamadas a esta rutina, dentro de nuestro bloque infinito, justo encima de la etiqueta loop_continue.

loop_paddle:
call	ScanKeys
call	MovePaddle

Primero comprobamos las teclas de control que se han pulsado, CALL ScanKeys, y luego movemos las palas, CALL MovePaddle.

También tenemos que cambiar la etiqueta a la que salta cuando la bola no se mueve, cuatro líneas más arriba.

cp		$06
jr		nz, loop_paddle		; ¡CAMBIO!
call	MoveBall

Compilamos y probamos en el emulador.

Ensamblador ZX Spectrum - Palas borrando borde
Ensamblador ZX Spectrum, palas borrando borde

Observamos dos problemas:

  1. Las palas borran el borde.
  2. Las palas se mueven muy rápido y son difíciles de controlar.

Para resolver el primer problema vamos a modificar las constantes que marcan los límites superior e inferior de las palas, que están en Sprite.asm.

PADDLE_BOTTOM:	EQU	$a6
PADDLE_TOP:		EQU	$02

Compilamos, cargamos en el emulador y comprobamos que ya no se borra el borde.

Ensamblador ZX Spectrum - Palas no borran borde
Ensamblador ZX Spectrum, palas no borran borde

Velocidad de las palas

Para reducir la velocidad del movimiento de las palas, vamos a usar la misma técnica que usamos con la bola, no vamos a mover las palas en cada iteración del bucle.

Lo primero es declarar la variable que usaremos como contador, lo que hacemos antes de END $8000.

countLoopPaddle:	db	$00

Ahora, justo debajo de la etiqueta loop_paddle, implementamos la comprobación del contador.

ld		a, (countLoopPaddle)
inc		a
ld		(countLoopPaddle), a
cp		$02
jr		nz, loop_continue
call	ScanKeys
call	MovePaddle

Cargamos el contador en A, LD A, (countLoopPaddle), lo incrementamos, INC A, y lo cargamos en memoria, LD (countLoopPaddle), A. Evaluamos si han pasado las veces que hemos definido para mover las palas, CP $02, y si no es así saltamos, JR NZ, loop_continue.

Si no salta, comprobamos si se ha pulsado alguna tecla de control, CALL ScanKeys, y movemos las palas, CALL MovePaddle, y tal y como hicimos con la bola, hay que poner el contador a cero. Añadimos las líneas siguiente antes de la etiqueta loop_continue.

ld		a, ZERO
ld		(countLoopPaddle), a

Cargamos 0 en A, LD A, ZERO, y lo cargamos en memoria, LD (countLoopPaddle), A, poniendo el contador a 0.

Compilamos y cargamos en el emulador. Ahora el control de las palabras es menos rápido y más preciso.

El código final de Main.asm es el siguiente.

; Pintado de campo, movimiento de palas y bola y temporización
org	$8000

; -----------------------------------------------------------------------------
; Entrada al programa
; -----------------------------------------------------------------------------
Main:
ld		a, $00					; A = 0
out		($fe), a				; Pone el borde en negro

call	Cls						; Limpia la pantalla
call	PrintLine				; Imprime la línea central
call	PrintBorder				; Imprime el borde del campo

Loop:
ld		a, (countLoopBall)		; Carga el contador de vueltas de la bola	
inc		a						; Lo incrementa
ld		(countLoopBall), a		; Lo carga en memoria
cp		$06						; Comprueba si ha llegado a 6
jr		nz, loop_paddle			; Si no ha llegado a 4 salta
call	MoveBall				; Mueve la bola
ld		a, ZERO					; Pone el contador a 0
ld		(countLoopBall), a		; Lo carga en memoria

loop_paddle:
ld		a, (countLoopPaddle)	; Carga el contador de vueltas de las palas
inc		a						; Lo incrementa
ld		(countLoopPaddle), a	; Lo carga en memoria
cp		$02						; Comprueba si ha llegado a 2
jr		nz, loop_continue		; Si no ha llegado a 2 salta
call	ScanKeys        		; Escanea las teclas pulsadas
call	MovePaddle				; Mueva las palas
ld		a, ZERO					; Pone el contador a 0
ld		(countLoopPaddle), a	; Lo carga en memoria

loop_continue:
call	PrintBall				; Pinta la bola
call	ReprintLine				; Repinta la línea
ld		hl, (paddle1pos)		; Carga en HL la posición de la pala 1
call	PrintPaddle				; Pinta la pala 1
ld		hl, (paddle2pos)		; Carga en HL la posición de la pala 2
call	PrintPaddle				; Pinta la pala 2
jr		Loop					; Bucle infinito

include "Game.asm"
include "Controls.asm"
include "Sprite.asm"
include "Video.asm"

countLoopBall:		db $00		; Contador de vueltas de la bola
countLoopPaddle:	db $00		; Contador de vueltas de las palas

end		$8000

Podéis descargar todo el código que hemos generado.

Enlaces de interés

Vídeo

Si lo prefieres, puedes ver el vídeo que grabamos de esta sesión.

Ensamblador ZX Spectrum, campo, palas, bola y temporización

Ensamblador para ZX Spectrum PONG por Juan Antonio Rubio García.
Comentarios al código por Spirax.
Correcciones al texto original realizadas por Joaquín Ferrero.
Esta obra está bajo licencia de Creative Commons Reconocimiento-NoComercial-CompartitIgual 4.0 Internacional License.
Este tutorial ha sido publicado con anterioridad en AUA y se han grabado vídeos que están publicados a través de Retro Parla.

No olvides visitar las webs amigas.

AUA

Aquí puedes ver más cosas que he desarrollado para .Net, y aquí las desarrolladas en ensamblador para Z80.

Y recuerda, si lo usas no te limites a copiarlo, intenta entenderlo y adaptarlo a tus necesidades.

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
A %d blogueros les gusta esto: