ZX Spectrum Assembly, Tic-Tac-Toe – 0x03 Sound
In this chapter of ZX Spectrum Assembly, we will implement different sounds and use them for timing by pausing between events.
Translation by Felipe Monge Corbalán
Table of contents
Sound
We will create the file sound.asm and add the include to main.asm.
In sound.asm we will add the definition of the different sounds and melodies.
; -------------------------------------------------------------------
; Sounds.
;
; Sounds end in $00, the byte indicating the end.
; -------------------------------------------------------------------
; Countdown
SoundCountDown:
db $03, $8c, $3a, $00
; Error
SoundError:
db $0d, $c6, $1e, $16, $13, $13, $00
; Loss of movement
SoundLostMovement:
db $0d, $07, $10, $0b, $96, $12, $0a, $4d, $14
db $0b, $96, $12, $1a, $2c, $08, $00
; Next player
SoundNextPlayer:
db $01, $9d, $7b, $00
; Spectrum Movement
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
; Tables
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
; Point
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
All sounds have a zero byte as their last byte, which we will use in the routine that plays them to know when they are finished.
In these definitions, each note is made up of three bytes instead of four, the first two being the note and the third being the low byte of the duration, all of which are sixteenths and/or half notes.
We implement the routine that plays the tones.
; -------------------------------------------------------------------
; Plays a melody or sound.
;
; Input: BC = Start address of the melody.
;
; To save is done byte by byte, with the duration always being fuzzy
; or half-fuse; only one byte is needed for the duration.
; This routine is adapted for this operation.
; -------------------------------------------------------------------
PlayMusic:
push af
push bc
push de
push hl
push ix ; Preserves records
playMusic_loop:
ld a, (bc) ; A = high byte note
ld h, a ; H = A
or a ; A = 0?
jr z, playMusic_end ; A = 0, end
inc bc ; BC = next value
ld a, (bc) ; A = byte under note
ld l, a ; L = A
inc bc ; BC = next value
ld a, (bc) ; A = duration note
ld e, a ; E = A
ld d, $00 ; D = 0 (fuse or half-fuse)
inc bc ; BC = next value
push bc ; Preserves BC
call BEEPER ; Play note
pop bc ; Recover BC
jr playMusic_loop ; Loop until it reaches 0 end melody
playMusic_end:
pop ix
pop hl
pop de
pop bc
pop af ; Retrieves records
ret
PlayMusic receives the address of the sound to be played in BC, loads the note in HL, the duration in DE, plays it and loops until it reaches the zero byte which marks the end of the sound. D is always zero, only one byte is used for the duration.
In main.asm we will add the necessary calls to play the sounds.
We locate the Loop tag and delete from LD B, $19 to DJNZ loop_wait as we will be timing with the sound. Below this, after CALL PrintPoints, we add the next player sound:
ld bc, SoundNextPlayer
call PlayMusic
Locate the loop_print tag and just above it the JR Loop line. Above this line we add the error sound:
ld bc, SoundError
call PlayMusic
Locate the loop_win tag and just below it the INC (HL) line. Below this line we add the dot winner sound:
ld bc, SoundWinGame
call PlayMusic
Locate the loop_reset tag and just above it add the table sound:
ld bc, SoundTie
call PlayMusic
We compile, load into the emulator and listen to every single sound.
The main.asm file has grown considerably since we started, so it’s time to see what it looks like after commenting.
org $5e88 ; Loading address
Main:
ld hl, Sprite_P1 ; HL = address Sprite_P1
ld (UDG), hl ; UDG = address Sprite_p1
ld a, $02 ; A = 2
call OPENCHAN ; Open channel 2
xor a ; A = 0, Z = 0, C = 0
call BORDER ; Change border
call CLA ; Changes display attributes
call CLS ; Clear screen
Init:
call PrintBoard ; Paint board
ld hl, Name_p1 ; HL = name player 1
ld de, player1_name ; DE = player1_name
ld bc, LENNAME ; BC = length name
ldir ; Pass data
ld hl, Name_p2 ; HL = name player 2
ld de, player2_name ; DE = player 2 name
ld bc, LENNAME ; BC = length name
ldir ; Pass data
call PrintInfo ; Paint info
ld bc, LENDATA ; BC = length of starting data
call ResetValues ; Clear data
Loop:
ld hl, TitleTurn ; HL = shift address
ld de, TitleTurn_name ; DE = address name in turn name in turn
call DoMsg ; Compose and paint shift
call PrintPoints ; PaintPoints
ld bc, SoundNextPlayer ; BC = sound direction
call PlayMusic ; Play sound
loop_key:
call WaitKeyBoard ; Wait for key press
ld a, c ; A = C
cp KEY0 ; Key pressed?
jr z, loop_key ; No, loop
call ToMove ; Check movement
jr z, loop_print ; Correct, skip
ld hl, TitleError ; HL = address error
call PrintMsg ; Paint Error
ld bc, SoundError ; BC = sound address
call PlayMusic ; Play sound
jr Loop ; Main loop
loop_print:
call PrintOXO ; Paint token
loop_checkWinner:
call CheckWinner ; Any winners?
jr nz, loop_tie ; No, check tables
call PrintWinnerLine ; Paints winning line
ld hl, TitlePointFor ; HL = winning address
ld de, TitlePointName ; HL = address winning name
call DoMsg ; Compose and paint winner
ld hl, Points_p1 ; HL = address points player 1
ld a, (PlayerMoves) ; A = player that moved
or a ; Player 1?
jr z, loop_win ; Yes, skip
inc hl ; HL = address points player 2
loop_win:
inc (hl) ; Increases score
ld bc, SoundWinGame ; BC = sound address
call PlayMusic ; Play sound
jr loop_reset ; Jump
loop_tie:
ld hl, MoveCounter ; HL = move counter address
inc (hl) ; Increments counter
ld a, $09 ; A = 9
cp (hl) ; Counter = 9?
jr nz, loop_cont ; No, skip
ld hl, Points_tie ; HL = address points tables
inc (hl) ; Increases score
ld hl, TitleTie ; HL = address tables
call PrintMsg ; Paint tables
ld bc, SoundTie ; BC = sound direction
call PlayMusic ; Play sound
loop_reset:
ld bc, LENDATA-$04 ; BC = data length clean data
call ResetValues ; Clear data
call PrintBoard ; Paint board
loop_cont:
ld a, (PlayerMoves) ; A = player who moved
xor $01 ; A = next player
ld (PlayerMoves), a ; Refresh in memory
jr Loop ; Main loop
include "game.asm"
include "rom.asm"
include "screen.asm"
include "sprite.asm"
include "sound.asm"
include "var.asm"
end Main
ZX Spectrum Assembly, Tic-Tac-Toe
We have a large part of the game developed and working. We can play our first games with friends and/or family and we have sound.
In the next chapter of ZX Spectrum Assembly, we will implement the different options of the game and the end of the game.
Download the source code from here.
Useful links
ZX Spectrum Assembly, Tic-Tac-Toe 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.