Espamática
ZX SpectrumEnsamblador Z80Retro

0x03 Ensamblador ZX Spectrum OXO – sonido

En esta entrega de Ensamblador ZX Spectrum OXO vamos a implementar distintos sonidos, y los vamos a usar para temporizar haciendo pausas entre distintos eventos.

Sonidos

Creamos el archivo Sound.asm y añadimos el include en Main.asm.

En Sound.asm vamos añadir la definición de los distintos sonidos y melodías.

; -------------------------------------------------------------------
; Sonidos.
;
; Los sonidos acaban en $00, byte que indica el final.
; -------------------------------------------------------------------
; Cuenta atrás
SoundCountDown:
db $03, $8c, $3a, $00

; Error
SoundError:
db $0d, $c6, $1e, $16, $13, $13, $00

; Perdida de movimiento
SoundLostMovement:	
db $0d, $07, $10, $0b, $96, $12, $0a, $4d, $14
db $0b, $96, $12, $1a, $2c, $08, $00

; Siguiente jugador
SoundNextPlayer:
db $01, $9d, $7b, $00

; Movimiento Spectrum
SoundSpectrum:
db $06, $6e, $20, $06, $6e, $20, $05, $b7, $24
db $06, $6e, $20, $05, $13, $29, $05, $b7, $24
db $06, $6e, $20, $00

; Tablas
SoundTie:
db $0d, $07, $10, $0b, $96, $12, $06, $d4, $1e
db $0b, $96, $12, $07, $a6, $1b, $0b, $96, $12
db $08, $a5, $18, $0b, $96, $12, $09, $b4, $15
db $0b, $96, $12, $0a, $4d, $14, $0b, $96, $12
db $0d, $07, $10, $00

; Punto
SoundWinGame:
db $06, $6e, $20, $05, $13, $29, $04, $40, $30
db $06, $6e, $20, $05, $13, $29, $04, $40, $30
db $06, $6e, $20, $05, $13, $29, $04, $c7, $2b
db $05, $13, $29, $05, $b7, $24, $05, $13, $29
db $06, $6e, $20, $05, $13, $29, $04, $40, $30
db $06, $6e, $20, $05, $13, $29, $04, $40, $30
db $06, $6e, $20, $05, $13, $29, $04, $c7, $2b
db $05, $13, $29, $05, $b7, $24, $06, $6e, $20
db $00

Todos los sonidos tienen como último byte cero, que vamos a usar en la rutina que los reproduce para saber cuando se acaban.

En estas definiciones cada nota está compuesta por tres bytes en lugar de cuatro, los dos primeros son la nota y el tercero el byte bajo de la duración, siendo todas fusas y/o semifusas.

Rutina musical

Implementamos la rutina que reproduce los sonidos.

; -------------------------------------------------------------------
; Reproduce una melodía o sonido.
;
; Entrada: BC = Dirección de inicio de la melodía.
;
; Para ahorrar se hace byte a byte, siendo la duración siempre fusa o
; semifusa; sólo se necesita un byte para la duración.
; Esta rutina está adaptada para este funcionamiento.
; -------------------------------------------------------------------
PlayMusic:
push    af
push    bc
push    de
push    hl
push    ix                  ; Preserva registros

playMusic_loop:
ld      a, (bc)             ; A = byte alto nota
ld      h, a                ; H = A
or      a                   ; ¿A = 0?
jr      z, playMusic_end    ; A = 0, fin

inc     bc                  ; BC = siguiente valor
ld      a, (bc)             ; A = byte bajo nota
ld      l, a                ; L = A

inc     bc                  ; BC = siguiente valor
ld      a, (bc)             ; A = duración nota
ld      e, a                ; E = A
ld      d, $00              ; D = 0 (fusa o semifusa)
inc     bc                  ; BC = siguiente valor

push    bc                  ; Preserva BC
call    BEEPER              ; Reproduce nota
pop     bc                  ; Recupera BC

jr      playMusic_loop      ; Bucle hasta que llegue al 0
                            ; fin melodía

playMusic_end:
pop     ix
pop     hl
pop     de
pop     bc
pop     af              ; Recupera registros
ret

PlayMusic recibe en BC la dirección del sonido a emitir, carga la nota en HL, la duración en DE, la emite y sigue en bucle hasta que se llegue al byte cero, que marca el fin del sonido. Sólo usa un byte para la duración, siendo D siempre cero.

En Main.asm vamos a incluir las llamadas necesarias para que se emitan los sonidos.

Localizamos la etiqueta Loop y borramos desde LD B, $19 hasta DJNZ loop_wait, ya que vamos a temportizar con el sonido. Más abajo, tras CALL PrintPoints, añadimos el sonido de siguiente jugador:

ld      bc, SoundNextPlayer
call    PlayMusic

Localizamos la etiqueta loop_print y justo por encima la línea JR Loop. Encima de esta línea añadimos el sonido de error:

ld      bc, SoundError
call    PlayMusic

Localizamos la etiqueta loop_win y justo por debajo la línea INC (HL). Debajo de esta línea añadimos el sonido de ganador de punto:

ld      bc, SoundWinGame
call    PlayMusic

Localizamos la etiqueta loop_reset y justo por encima añadimos el sonido de tablas.

ld      bc, SoundTie
call    PlayMusic

Compilamos, cargamos en el emulador y oímos cada uno de los sonidos.

Main

El tamaño de Main.asm ha crecido considerablemente desde que empezamos, por lo que es hora de ver el aspecto que debe tener una vez comentado.

org     $5e88                   ; Dirección de carga

Main:
ld      hl, Sprite_P1           ; HL = dirección Sprite_P1
ld      (UDG), hl               ; UDG = dirección Sprite_p1
ld      a, $02                  ; A = 2
call    OPENCHAN                ; Abre canal 2

xor     a                       ; A = 0, Z = 0, C = 0
call    BORDER                  ; Cambia borde
call    CLA                     ; Cambia atributos pantalla
call    CLS                     ; Borra pantalla

Init:
call    PrintBoard              ; Pinta tablero

ld      hl, Name_p1             ; HL = nombre jugador 1
ld      de, player1_name        ; DE = nombre jugador 1
ld      bc, LENNAME             ; BC = longitud nombre
ldir                            ; Pasa datos
ld      hl, Name_p2             ; HL = nombre jugador 2
ld      de, player2_name        ; DE = nombre jugador 2
ld      bc, LENNAME             ; BC = longitud nombre
ldir                            ; Pasa datos
call    PrintInfo               ; Pinta info

ld      bc, LENDATA             ; BC = longitud datos partida
call    ResetValues             ; Limpia datos

Loop:
ld      hl, TitleTurn           ; HL = dirección turno
ld      de, TitleTurn_name      ; DE = dirección nombre en turno
call    DoMsg                   ; Compone y pinta turno
call    PrintPoints             ; Pinta puntos
ld      bc, SoundNextPlayer     ; BC = dirección sonido
call    PlayMusic               ; Emite sonido
loop_key:
call    WaitKeyBoard            ; Espera pulsación tecla
ld      a, c                    ; A = C
cp      KEY0                    ; ¿Pulsada tecla?
jr      z, loop_key             ; No, bucle
call    ToMove                  ; Comprueba movimiento
jr      z, loop_print           ; Correcto, salta
ld      hl, TitleError          ; HL = dirección error
call    PrintMsg                ; Pinta error
ld      bc, SoundError          ; BC = dirección sonido
call    PlayMusic               ; Emite sonido
jr      Loop                    ; Bucle principal
loop_print:
call    PrintOXO                ; Pinta ficha
loop_checkWinner:
call    ChekcWinner             ; ¿Algún ganador?
jr      nz, loop_tie            ; No, comprueba tablas
call    PrintWinnerLine         ; Pinta línea ganadora
ld      hl, TitlePointFor       ; HL = dirección ganador
ld      de, TitlePointName      ; HL = dirección nombre ganador
call    DoMsg                   ; Compone y pinta ganador
ld      hl, Points_p1           ; HL = dirección puntos jugador 1
ld      a, (PlayerMoves)        ; A = jugador que ha movido
or      a                       ; ¿Jugador 1?
jr      z, loop_win             ; Sí, salta
inc     hl                      ; HL = dirección puntos jugador 2
loop_win:
inc    (hl)                     ; Incrementa puntuación
ld      bc, SoundWinGame        ; BC = dirección sonido
call    PlayMusic               ; Emite sonido
jr      loop_reset              ; Salta
loop_tie:
ld      hl, MoveCounter         ; HL = dirección contador movimientos
inc     (hl)                    ; Incrementa contador
ld      a, $09                  ; A = 9
cp      (hl)                    ; ¿Contador = 9?
jr      nz, loop_cont           ; No, salta
ld      hl, Points_tie          ; HL = dirección puntos tablas
inc     (hl)                    ; Incrementa puntuación
ld      hl, TitleTie            ; HL = dirección tablas
call    PrintMsg                ; Pinta tablas
ld      bc, SoundTie            ; BC = dirección sonido
call    PlayMusic               ; Emite sonido
loop_reset:
ld      bc, LENDATA-$04         ; BC = longitud datos limpiar
call    ResetValues             ; Limpia datos
call    PrintBoard              ; Pinta tablero
loop_cont:
ld      a, (PlayerMoves)        ; A = jugador que ha movido
xor     $01                     ; A = jugador siguiente
ld      (PlayerMoves), a        ; Actualiza en memoria
jr      Loop                    ; Bucle principal

include "Game.asm"
include "Rom.asm"
include "Screen.asm"
include "Sprite.asm"
include "Sound.asm"
include "Var.asm"

end     Main

Ensamblador ZX Spectrum, conclusión

Tenemos desarrollado y funcional gran parte del juego. Podemos jugar nuestras primeras partidas con amigos y/o familiares, y tenemos sonido.

En el próximo capítulo implementaremos las distintas opciones de la partida, y el fin de la misma.

Todo el código que hemos generado lo podéis descargar desde aquí.

Ensamblador para ZX Spectrum OXO por Juan Antonio Rubio García.
Esta obra está bajo licencia de Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional License.



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

Descubre más desde Espamática

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

Seguir leyendo