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.
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.