aboutsummaryrefslogtreecommitdiff
path: root/lib/plw
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-11-05 11:22:55 +0000
committerJuan J. Martinez <jjm@usebox.net>2023-11-05 11:31:28 +0000
commit2fbdf974338bde8576efdae40a819a76b2391033 (patch)
tree64d41a37470143f142344f9a439d96de3e7918c2 /lib/plw
downloadkitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.tar.gz
kitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.zip
Initial import of the open source release
Diffstat (limited to 'lib/plw')
-rw-r--r--lib/plw/Makefile34
-rw-r--r--lib/plw/README.md16
-rw-r--r--lib/plw/player/Makefile11
-rw-r--r--lib/plw/player/PlayerLightWeight.asm1694
-rw-r--r--lib/plw/player/PlayerLightWeight_SoundEffects.asm458
-rw-r--r--lib/plw/player/player.asm43
-rw-r--r--lib/plw/plw_init.z8014
-rw-r--r--lib/plw/plw_init_sound_effects.z809
-rw-r--r--lib/plw/plw_is_sound_effect_on.z807
-rw-r--r--lib/plw/plw_play.z806
-rw-r--r--lib/plw/plw_play_sound_effect.z8048
-rw-r--r--lib/plw/plw_player.asm1193
-rw-r--r--lib/plw/plw_player.c3
-rw-r--r--lib/plw/plw_stop.z806
14 files changed, 3542 insertions, 0 deletions
diff --git a/lib/plw/Makefile b/lib/plw/Makefile
new file mode 100644
index 0000000..ff6e632
--- /dev/null
+++ b/lib/plw/Makefile
@@ -0,0 +1,34 @@
+LIB=plw.lib
+
+all: $(LIB)
+
+CC=sdcc
+AS=sdasz80
+AR=sdar
+CFLAGS=-mz80 --Werror --fsigned-char --std-sdcc99 --opt-code-speed
+LDFLAGS=--no-std-crt0 --fomit-frame-pointer
+
+lib_SRCS=$(wildcard *.z80)
+lib_OBJS=$(patsubst %.z80,%.rel,$(lib_SRCS))
+
+$(LIB): $(lib_OBJS) plw_player.rel
+ $(AR) -rcD $(LIB) $(lib_OBJS)
+ cp $(LIB) ..
+ cp plw_player.rel ../
+
+plw_player.rel: plw_player.c player/plw_player.h
+
+player/plw_player.h:
+ make -C player
+
+%.rel: %.z80
+ $(AS) -o $<
+
+%.rel: %.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -c $<
+
+.PHONY: clean
+clean:
+ make -C player clean
+ rm -f *.bin *.rel *.lk *.noi *.map *.ihx *.sym *.lst *.zx7 player.asm $(LIB)
+
diff --git a/lib/plw/README.md b/lib/plw/README.md
new file mode 100644
index 0000000..174ae56
--- /dev/null
+++ b/lib/plw/README.md
@@ -0,0 +1,16 @@
+This is Arkos 2 lightweight player.
+
+It comes with two components:
+
+ - A .rel module to load in 0x0b06 (just after the CRT0)
+ - A library with the functions to call the module
+
+This is so the player can be assembled by rasm and still use SDCC linker.
+
+IMPORTANT: if your CRT0 changes its size, you'll have to change the loading
+address and the entry point for all the functions.
+
+For licensing information, PLW is just a wrapper that can be distributed
+under the same license as Arkos Player 2 AKW player.
+
+Code by Juan J. Martinez <jjm@usebox.net>
diff --git a/lib/plw/player/Makefile b/lib/plw/player/Makefile
new file mode 100644
index 0000000..53e0b23
--- /dev/null
+++ b/lib/plw/player/Makefile
@@ -0,0 +1,11 @@
+SOURCES=$(wildcard *.asm)
+
+all: plw_player.h
+
+plw_player.h: $(SOURCES)
+ rasm player.asm -ob plw_player.bin
+ bin2h.py plw_player.bin plw_player > plw_player.h
+
+.PHONY: clean all
+clean:
+ rm -f *.bin plw_player.h
diff --git a/lib/plw/player/PlayerLightWeight.asm b/lib/plw/player/PlayerLightWeight.asm
new file mode 100644
index 0000000..5da830c
--- /dev/null
+++ b/lib/plw/player/PlayerLightWeight.asm
@@ -0,0 +1,1694 @@
+; Arkos Tracker 2 Lightweight player (format V1 (used by AT2 since alpha4)).
+
+; ** This player has been superseded by the AKM format, more compact but also more powerful. Please use it instead. **
+
+; This compiles with RASM. Check the compatibility page on the Arkos Tracker 2 website, it contains a source converter to any Z80 assembler!
+
+; This is a generic player, but much simpler and using only the most used features, so that the music and players are both
+; lightweight. The player supports sound effects.
+
+; Though the player is optimized in speed, it is much slower than the generic one or the AKY player.
+; With effects used at the same time, it may reach 35 scanlines on a CPC, plus some few more if you are using sound effects.
+
+; The player uses the stack for optimizations. Make sure the interruptions are disabled before it is called.
+; The stack pointer is saved at the beginning and restored at the end.
+
+; Target harware:
+; ---------------
+; This code can target Amstrad CPC, MSX, Spectrum and Pentagon. By default, it targets Amstrad CPC.
+; Simply use one of the follow line (BEFORE this player):
+; PLY_LW_HARDWARE_CPC = 1
+; PLY_LW_HARDWARE_MSX = 1
+; PLY_LW_HARDWARE_SPECTRUM = 1
+; PLY_LW_HARDWARE_PENTAGON = 1
+; Note that the PRESENCE of this variable is tested, NOT its value.
+;
+; Some severe optimizations of CPU/memory can be performed:
+; ---------------------------------------------------------
+; - Use the Player Configuration of Arkos Tracker 2 to generate a configuration file to be included at the beginning of this player.
+; It will disable useless features according to your songs! Check the manual for more details, or more simply the testers.
+;
+; Sound effects:
+; --------------
+; Sound effects are disabled by default. Declare PLY_LW_MANAGE_SOUND_EFFECTS to enable it:
+; PLY_LW_MANAGE_SOUND_EFFECTS = 1
+; Check the sound effect tester to see how it enables it.
+; Note that the PRESENCE of this variable is tested, NOT its value.
+;
+; ROM
+; ----------------------
+; No ROM player is available for this player. I suggest you try the AKM player, which is more powerful and more compact (albeit a bit slower).
+;
+; -------------------------------------------------------
+
+PLY_LW_Start:
+
+ ;Checks the hardware. Only one must be selected.
+PLY_LW_HardwareCounter = 0
+ IFDEF PLY_LW_HARDWARE_CPC
+ PLY_LW_HardwareCounter = PLY_LW_HardwareCounter + 1
+ ENDIF
+ IFDEF PLY_LW_HARDWARE_MSX
+ PLY_LW_HardwareCounter = PLY_LW_HardwareCounter + 1
+ PLY_LW_HARDWARE_SPECTRUM_OR_MSX = 1
+ ENDIF
+ IFDEF PLY_LW_HARDWARE_SPECTRUM
+ PLY_LW_HardwareCounter = PLY_LW_HardwareCounter + 1
+ PLY_LW_HARDWARE_SPECTRUM_OR_PENTAGON = 1
+ PLY_LW_HARDWARE_SPECTRUM_OR_MSX = 1
+ ENDIF
+ IFDEF PLY_LW_HARDWARE_PENTAGON
+ PLY_LW_HardwareCounter = PLY_LW_HardwareCounter + 1
+ PLY_LW_HARDWARE_SPECTRUM_OR_PENTAGON = 1
+ ENDIF
+ IF PLY_LW_HARDWARECounter > 1
+ FAIL 'Only one hardware must be selected!'
+ ENDIF
+ ;By default, selects the Amstrad CPC.
+ IF PLY_LW_HARDWARECounter == 0
+ PLY_LW_HARDWARE_CPC = 1
+ ENDIF
+
+;PLY_LW_USE_HOOKS: equ 1 ;Use hooks for external calls? 0 if the Init/Play methods are directly called, will save a few bytes.
+;PLY_LW_STOP_SOUNDS: equ 1 ;1 to have the "stop sounds" code. Set it to 0 if you never plan on stopping your music.
+
+ ;Is there a loaded Player Configuration source? If no, use a default configuration.
+ IFNDEF PLY_CFG_ConfigurationIsPresent
+ PLY_CFG_UseTranspositions = 1
+ PLY_CFG_UseSpeedTracks = 1
+ PLY_CFG_UseEffects = 1
+ PLY_CFG_UseHardwareSounds = 1
+ PLY_CFG_NoSoftNoHard_Noise = 1
+ PLY_CFG_SoftOnly_Noise = 1
+ PLY_CFG_SoftOnly_SoftwareArpeggio = 1
+ PLY_CFG_SoftOnly_SoftwarePitch = 1
+ PLY_CFG_SoftToHard_SoftwarePitch = 1
+ PLY_CFG_SoftToHard_SoftwareArpeggio = 1
+ PLY_CFG_SoftAndHard_SoftwarePitch = 1
+ PLY_CFG_SoftAndHard_SoftwareArpeggio = 1
+ PLY_CFG_UseEffect_ArpeggioTable = 1
+ PLY_CFG_UseEffect_PitchTable = 1
+ PLY_CFG_UseEffect_PitchUp = 1
+ PLY_CFG_UseEffect_PitchDown = 1
+ PLY_CFG_UseEffect_SetVolume = 1
+ PLY_CFG_UseEffect_Reset = 1
+ ENDIF
+
+ ;Agglomerates some flags, because they are treated the same way by this player.
+ ;--------------------------------------------------
+ ;Creates a flag for pitch in instrument, and also pitch in hardware.
+ IFDEF PLY_CFG_SoftOnly_SoftwarePitch
+ PLY_LW_PitchInInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftToHard_SoftwarePitch
+ PLY_LW_PitchInInstrument = 1
+ PLY_LW_PitchInHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftAndHard_SoftwarePitch
+ PLY_LW_PitchInInstrument = 1
+ PLY_LW_PitchInHardwareInstrument = 1
+ ENDIF
+ ;A flag for Arpeggios in Instrument, both in software and hardware.
+ IFDEF PLY_CFG_SoftOnly_SoftwareArpeggio
+ PLY_LW_ArpeggioInSoftwareOrHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftToHard_SoftwareArpeggio
+ PLY_LW_ArpeggioInSoftwareOrHardwareInstrument = 1
+ PLY_LW_ArpeggioInHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftAndHard_SoftwareArpeggio
+ PLY_LW_ArpeggioInSoftwareOrHardwareInstrument = 1
+ PLY_LW_ArpeggioInHardwareInstrument = 1
+ ENDIF
+
+ ;A flag if noise is used (noise in hardware not tested, not present in this format).
+ IFDEF PLY_CFG_NoSoftNoHard_Noise
+ PLY_LW_USE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftOnly_Noise
+ PLY_LW_USE_Noise = 1
+ ENDIF
+ ;The noise is managed? Then the noise register access must be compiled.
+ IFDEF PLY_LW_USE_Noise
+ PLY_LW_USE_NoiseRegister = 1
+ ENDIF
+
+ ;Mixing Pitch up/down effects.
+ IFDEF PLY_CFG_UseEffect_PitchUp
+ PLY_LW_USE_EffectPitchUpDown = 1
+ ENDIF
+ IFDEF PLY_CFG_UseEffect_PitchDown
+ PLY_LW_USE_EffectPitchUpDown = 1
+ ENDIF
+ ;Volume and Pitch up/down dual effects (if one exists, the other one too).
+ IFDEF PLY_CFG_UseEffect_SetVolume
+ PLY_LW_USE_Volume_And_PitchUpDown_Effects = 1
+ PLY_LW_USE_EffectPitchUpDown = 1
+ ENDIF
+ IFDEF PLY_LW_USE_EffectPitchUpDown
+ PLY_LW_USE_Volume_And_PitchUpDown_Effects = 1
+ PLY_CFG_UseEffect_SetVolume = 1
+ ENDIF
+ ;Volume and Arpeggio Table dual effect (if one exists, the other one too).
+ IFDEF PLY_CFG_UseEffect_SetVolume
+ PLY_LW_USE_Volume_And_ArpeggioTable_Effects = 1
+ PLY_CFG_UseEffect_ArpeggioTable = 1
+ ENDIF
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable
+ PLY_LW_USE_Volume_And_ArpeggioTable_Effects = 1
+ PLY_CFG_UseEffect_SetVolume = 1
+ ENDIF
+ ;Reset and Arpeggio Table dual effect (if one exists, the other one too).
+ IFDEF PLY_CFG_UseEffect_Reset
+ PLY_LW_USE_Reset_And_ArpeggioTable_Effects = 1
+ PLY_CFG_UseEffect_ArpeggioTable = 1
+ ENDIF
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable
+ PLY_LW_USE_Reset_And_ArpeggioTable_Effects = 1
+ PLY_CFG_UseEffect_Reset = 1
+ ENDIF
+
+
+ ;Disark macro: Word region Start.
+ disarkCounter = 0
+ IFNDEF dkws
+ MACRO dkws
+PLY_LW_DisarkWordRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Word region End.
+ IFNDEF dkwe
+ MACRO dkwe
+PLY_LW_DisarkWordRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Pointer region Start.
+ disarkCounter = 0
+ IFNDEF dkps
+ MACRO dkps
+PLY_LW_DisarkPointerRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Pointer region End.
+ IFNDEF dkpe
+ MACRO dkpe
+PLY_LW_DisarkPointerRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Byte region Start.
+ disarkCounter = 0
+ IFNDEF dkbs
+ MACRO dkbs
+PLY_LW_DisarkByteRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Byte region End.
+ IFNDEF dkbe
+ MACRO dkbe
+PLY_LW_DisarkByteRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Force "No Reference Area" for 3 bytes (ld hl,xxxx).
+ IFNDEF dknr3
+ MACRO dknr3
+PLY_LW_DisarkForceNonReferenceDuring3_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+
+
+ ;Hooks for external calls. Can be removed if not needed.
+ if PLY_LW_USE_HOOKS
+ assert PLY_LW_Start == $ ;Makes sure no extra byte were inserted before the hooks.
+ jp PLY_LW_Init ;Player + 0.
+ jp PLY_LW_Play ;Player + 3.
+ if PLY_LW_STOP_SOUNDS
+ jp PLY_LW_Stop ;Player + 6.
+ endif
+ endif
+
+ ;Includes the sound effects player, if wanted. Important to do it as soon as possible, so that
+ ;its code can react to the Player Configuration and possibly alter it.
+ IFDEF PLY_LW_MANAGE_SOUND_EFFECTS
+ include "PlayerLightWeight_SoundEffects.asm"
+ ENDIF
+ ;[[INSERT_SOUND_EFFECT_SOURCE]] ;A tag for test units. Don't touch or you're dead.
+
+
+;Initializes the song. MUST be called before actually playing the song.
+;IN: HL = Address of the song.
+; A = Index of the subsong to play (>=0).
+PLY_LW_InitDisarkGenerateExternalLabel:
+PLY_LW_Init:
+ ;Reads the Song data.
+ ;Skips the tag and format number.
+dknr3: ld de,5
+ add hl,de
+
+ ;Reads the pointers to the various index tables.
+ ld de,PLY_LW_PtInstruments + 1
+ ldi
+ ldi
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld de,PLY_LW_PtArpeggios + 1
+ ldi
+ ldi
+ ELSE
+ inc hl
+ inc hl
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld de,PLY_LW_PtPitches + 1
+ ldi
+ ldi
+ ELSE
+ inc hl
+ inc hl
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ELSE
+dknr3: ld de,4
+ add hl,de
+ ENDIF ;PLY_CFG_UseEffects
+
+ ;Finds the address of the Subsong.
+ ;HL points on the table, adds A * 2.
+ ;Possible optimization: possible to set the Subsong directly.
+ ld e,a
+ ld d,0
+ add hl,de
+ add hl,de
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+
+ ;Reads the header of the Subsong.
+ ld a,(de) ;Gets the speed.
+ inc de
+ ld (PLY_LW_Linker + 1),de
+ ld (PLY_LW_Speed + 1),a
+ ;Forces a new line.
+ dec a
+ ld (PLY_LW_TickCounter + 1),a
+
+ ;Can be removed if there is no need to reset the song.
+ xor a
+ ld (PLY_LW_PatternRemainingHeight + 1),a
+
+ ;A big LDIR to erase all the data blocks. Optimization: can be removed if there is no need to reset the song.
+ ld hl,PLY_LW_Track1_Data
+ ld de,PLY_LW_Track1_Data + 1
+dknr3: ld bc,PLY_LW_Track3_Data_End - PLY_LW_Track3_Data - 1
+ ld (hl),0
+ ldir
+
+ ;Reads the first instrument, the empty one, and set-ups the pointers to the instrument to read.
+ ;Optimization: needed if the song doesn't start with an instrument on all the channels. Else, it can be removed.
+ ld hl,(PLY_LW_PtInstruments + 1)
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc de ;Skips the header.
+ ld (PLY_LW_Track1_PtInstrument),de
+ ld (PLY_LW_Track2_PtInstrument),de
+ ld (PLY_LW_Track3_PtInstrument),de
+ ret
+
+
+;Cuts the channels, stopping all sounds.
+ if PLY_LW_STOP_SOUNDS
+PLY_LW_StopDisarkGenerateExternalLabel:
+PLY_LW_Stop:
+ ld (PLY_LW_SaveSP + 1),sp
+
+ xor a
+ ld (PLY_LW_Track1_Volume),a
+ ld (PLY_LW_Track2_Volume),a
+ ld (PLY_LW_Track3_Volume),a
+ IFDEF PLY_LW_HARDWARE_MSX
+ ld a,%10111111 ;Bit 7/6 must be 10 on MSX!
+ ELSE
+ ld a,%00111111 ;On CPC, bit 6 must be 0! Other platforms don't care.
+ ENDIF
+ ld (PLY_LW_MixerRegister),a
+ jp PLY_LW_SendPsg
+ endif ;PLY_LW_STOP_SOUNDS
+
+;Plays one frame of the song. It MUST have been initialized before.
+;The stack is saved and restored, but is diverted, so watch out for the interruptions.
+PLY_LW_PlayDisarkGenerateExternalLabel:
+PLY_LW_Play:
+ ld (PLY_LW_SaveSP + 1),sp
+
+ ;Reads a new line?
+PLY_LW_TickCounter: ld a,0
+ inc a
+PLY_LW_Speed: cp 1 ;Speed (>0).
+ jp nz,PLY_LW_TickCounterManaged
+
+ ;A new line must be read. But have we reached the end of the Pattern?
+PLY_LW_PatternRemainingHeight: ld a,0 ;Height. If 0, end of the pattern.
+ sub 1
+ jr c,PLY_LW_Linker
+ ;Pattern not ended. No need to read the Linker.
+ ld (PLY_LW_PatternRemainingHeight + 1),a
+ jr PLY_LW_ReadLine
+
+ ;New pattern. Reads the Linker.
+dknr3:
+PLY_LW_Linker: ld hl,0
+PLY_LW_LinkerPostPt:
+ ;Resets the possible empty cell counter of each Track.
+ xor a
+ ld (PLY_LW_Track1_WaitEmptyCell),a
+ ld (PLY_LW_Track2_WaitEmptyCell),a
+ ld (PLY_LW_Track3_WaitEmptyCell),a
+
+ ;Reads the state byte of the pattern.
+ ld a,(hl)
+ inc hl
+ rra
+ jr c,PLY_LW_LinkerNotEndOfSongOk
+ ;End of song.
+ ld a,(hl) ;Reads where to loop in the Linker.
+ inc hl
+ ld h,(hl)
+ ld l,a
+ jr PLY_LW_LinkerPostPt
+
+PLY_LW_LinkerNotEndOfSongOk:
+ rra
+ ld b,a
+ IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
+ ;New speed?
+ jr nc,PLY_LW_LinkerAfterSpeed
+ ld a,(hl)
+ inc hl
+ ld (PLY_LW_Speed + 1),a
+PLY_LW_LinkerAfterSpeed:
+ ENDIF ;PLY_CFG_UseSpeedTracks
+
+ ;New height?
+ rr b
+ jr nc,PLY_LW_LinkerUsePreviousHeight
+ ld a,(hl)
+ inc hl
+ ld (PLY_LW_LinkerPreviousRemainingHeight + 1),a
+ jr PLY_LW_LinkerSetRemainingHeight
+ ;The same height is used. It was stored before.
+PLY_LW_LinkerUsePreviousHeight:
+PLY_LW_LinkerPreviousRemainingHeight: ld a,0
+PLY_LW_LinkerSetRemainingHeight:
+ ld (PLY_LW_PatternRemainingHeight + 1),a
+
+ ;New transpositions?
+ rr b
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+ jr nc,PLY_LW_LinkerAfterNewTranspositions
+ ;New transpositions.
+ ld de,PLY_LW_Track1_Transposition
+ ldi
+ ld de,PLY_LW_Track2_Transposition
+ ldi
+ ld de,PLY_LW_Track3_Transposition
+ ldi
+PLY_LW_LinkerAfterNewTranspositions:
+ ENDIF ;PLY_CFG_UseTranspositions
+
+ ;Reads the 3 track pointers.
+ ld de,PLY_LW_Track1_PtTrack
+ ldi
+ ldi
+ ld de,PLY_LW_Track2_PtTrack
+ ldi
+ ldi
+ ld de,PLY_LW_Track3_PtTrack
+ ldi
+ ldi
+ ld (PLY_LW_Linker + 1),hl
+
+;Reads the Tracks.
+;---------------------------------
+PLY_LW_ReadLine:
+dknr3:
+PLY_LW_PtInstruments: ld de,0
+ exx
+ ld ix,PLY_LW_Track1_Data
+ call PLY_LW_ReadTrack
+ ld ix,PLY_LW_Track2_Data
+ call PLY_LW_ReadTrack
+ ld ix,PLY_LW_Track3_Data
+ call PLY_LW_ReadTrack
+
+ xor a
+PLY_LW_TickCounterManaged:
+ ld (PLY_LW_TickCounter + 1),a
+
+
+
+;Plays the sound stream.
+;---------------------------------
+ ld de,PLY_LW_PeriodTable
+ exx
+
+ ld c,%11100000 ;Register 7, shifted of 2 to the left. Bits 2 and 5 will be possibly changed by each iteration.
+
+ ld ix,PLY_LW_Track1_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_LW_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_LW_Track1_Registers
+ call PLY_LW_PlaySoundStream
+
+ srl c ;Not RR, because we have to make sure the b6 is 0, else no more keyboard (on CPC)!
+ ;Also, on MSX? bit 6 must be 0.
+ ld ix,PLY_LW_Track2_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_LW_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_LW_Track2_Registers
+ call PLY_LW_PlaySoundStream
+
+ IFDEF PLY_LW_HARDWARE_MSX
+ scf ;On MSX, bit 7 must be 1.
+ rr c
+ ELSE
+ rr c ;On other platforms, we don't care about b7.
+ ENDIF
+ ld ix,PLY_LW_Track3_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_LW_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_LW_Track3_Registers
+ call PLY_LW_PlaySoundStream
+
+ ld a,c
+
+;Plays the sound effects, if desired.
+;-------------------------------------------
+ IFDEF PLY_LW_MANAGE_SOUND_EFFECTS
+ call PLY_LW_PlaySoundEffectsStream
+ ELSE
+ ld (PLY_LW_MixerRegister),a
+ ENDIF ;PLY_LW_MANAGE_SOUND_EFFECTS
+
+
+
+;Sends the values to the PSG.
+;---------------------------------
+PLY_LW_SendPsg:
+ ld sp,PLY_LW_Registers_RetTable
+
+ IFDEF PLY_LW_HARDWARE_CPC
+dknr3: ld bc,#f680
+ ld a,#c0
+dknr3: ld de,#f4f6
+ out (c),a ;#f6c0 ;Madram's trick requires to start with this. out (c),b works, but will activate K7's relay! Not clean.
+ ENDIF
+
+ IFDEF PLY_LW_HARDWARE_SPECTRUM_OR_PENTAGON
+dknr3: ld de,#bfff
+dknr3: ld bc,#fffd
+ ENDIF
+
+PLY_LW_SendPsgRegister:
+ pop hl ;H = value, L = register.
+PLY_LW_SendPsgRegisterAfterPop:
+
+ IFDEF PLY_LW_HARDWARE_CPC
+ ld b,d
+ out (c),l ;#f400 + register.
+ ld b,e
+ out (c),0 ;#f600
+ ld b,d
+ out (c),h ;#f400 + value.
+ ld b,e
+ out (c),c ;#f680
+ out (c),a ;#f6c0
+ ENDIF
+
+ IFDEF PLY_LW_HARDWARE_SPECTRUM_OR_PENTAGON
+ out (c),l ;#fffd + register.
+ ld b,d
+ out (c),h ;#bffd + value
+ ld b,e
+ ENDIF
+
+ IFDEF PLY_LW_HARDWARE_MSX
+ ld a,l ;Register.
+ out (#a0),a
+ ld a,h ;Value.
+ out (#a1),a
+ ENDIF
+
+ ret
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+PLY_LW_SendPsgRegisterR13:
+
+ ;Should the R13 be played? Yes only if different. No "force retrig" is managed by this player.
+PLY_LW_SetReg13: ld a,0
+PLY_LW_SetReg13Old: cp 0
+ jr z,PLY_LW_SendPsgRegisterEnd
+ ;Different. R13 must be played. Updates the old R13 value.
+ ld (PLY_LW_SetReg13Old + 1),a
+
+ ld h,a
+ ld l,13
+ IFDEF PLY_LW_HARDWARE_CPC
+ ld a,#c0
+ ENDIF
+ ret ;Sends the 13th registers.
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+PLY_LW_SendPsgRegisterEnd:
+
+dknr3:
+PLY_LW_SaveSP: ld sp,0
+ ret
+
+
+
+
+
+
+
+
+
+
+;Reads a Track.
+;IN: IX = Data block of the Track.
+; DE'= Instrument table. Do not modify!
+PLY_LW_ReadTrack:
+ ;Are there any empty lines to wait?
+ ld a,(ix + PLY_LW_Data_OffsetWaitEmptyCell)
+ sub 1
+ jr c,PLY_LW_RT_NoEmptyCell
+ ;Wait!
+ ld (ix + PLY_LW_Data_OffsetWaitEmptyCell),a
+ ret
+
+PLY_LW_RT_NoEmptyCell:
+ ;Reads the Track pointer.
+ ld l,(ix + PLY_LW_Data_OffsetPtTrack + 0)
+ ld h,(ix + PLY_LW_Data_OffsetPtTrack + 1)
+ ld a,(hl)
+ inc hl
+ ld b,a
+ and %111111 ;Keeps only the note.
+ sub 60
+ jr c,PLY_LW_RT_NoteMaybeEffect
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ jr z,PLY_LW_RT_ReadEffect ;No note, but effect.
+ ENDIF ;PLY_CFG_UseEffects
+ dec a
+ jr z,PLY_LW_RT_WaitLong
+ dec a
+ jr z,PLY_LW_RT_WaitShort
+ ;63: Escape code for a note, because octave <2 or >5.
+ ;Reads the note.
+ ld a,(hl)
+ inc hl
+ ;The rest is exactly as the "note maybe effect", as B contains the flag to know about the possible
+ ;New Instrument and/or Effect?.
+ jr PLY_LW_RT_NMB_AfterOctaveCompensation
+
+PLY_LW_RT_NoteMaybeEffect:
+ ;A is the note from octave 2, and 60 to compensate the sub above.
+ ;Then adds the transposition.
+ add a,12 * 2 + 60
+PLY_LW_RT_NMB_AfterOctaveCompensation:
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+ add a,(ix + PLY_LW_Data_OffsetTransposition)
+ ENDIF ;PLY_CFG_UseTranspositions
+ ld (ix + PLY_LW_Data_OffsetBaseNote),a
+
+ ;New Instrument?
+ rl b
+ jr c,PLY_LW_RT_NME_NewInstrument
+ ;Same Instrument. Retrieves the address previously stored.
+ ld a,(ix + PLY_LW_Data_OffsetPtBaseInstrument + 0)
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 0),a
+ ld a,(ix + PLY_LW_Data_OffsetPtBaseInstrument + 1)
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 1),a
+ jr PLY_LW_RT_NME_AfterInstrument
+
+PLY_LW_RT_NME_NewInstrument:
+ ;New Instrument, reads it.
+ ld a,(hl)
+ inc hl
+ exx
+ ;Gets the address of the Instrument.
+ ld l,a ;No need to *2, it is already encoded like that.
+ ld h,0
+ add hl,de ;Adds to the Instrument Table.
+ ld c,(hl)
+ inc hl
+ ld b,(hl)
+ ;Reads the header of the Instrument.
+ ld a,(bc) ;Speed.
+ ld (ix + PLY_LW_Data_OffsetInstrumentSpeed),a
+ inc bc
+ ;Stores the pointer on the data of the Instrument.
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 0),c
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 1),b
+ ld (ix + PLY_LW_Data_OffsetPtBaseInstrument + 0),c ;Useful to store the base Instrument address to retrieve it when
+ ld (ix + PLY_LW_Data_OffsetPtBaseInstrument + 1),b ;there is a new instrument, without providing its number (optimization).
+ exx
+PLY_LW_RT_NME_AfterInstrument:
+ ;Resets the step on the Instrument.
+ ld (ix + PLY_LW_Data_OffsetInstrumentCurrentStep),0
+
+ ;Resets the Track pitch.
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ xor a
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetIsPitchUpDownUsed),a
+ ld (ix + PLY_LW_Data_OffsetTrackPitchInteger + 0),a
+ ld (ix + PLY_LW_Data_OffsetTrackPitchInteger + 1),a
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+ ;ld (ix + PLY_LW_Data_OffsetTrackPitchDecimal),a ;Shouldn't be needed, the difference shouldn't be noticeable.
+ ;Resets the offset on Arpeggio and Pitch tables.
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetPtArpeggioOffset),a
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetPtPitchOffset),a
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+ ENDIF ;PLY_CFG_UseEffects
+
+ ;Any effect? If no, stop.
+ rl b
+ jr nc,PLY_LW_RT_CellRead
+ ;Effect present.
+ ;jr PLY_LW_RT_ReadEffect
+
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+PLY_LW_RT_ReadEffect:
+ ;Reads effect number and possible data.
+ ld a,(hl)
+ inc hl
+ ld b,a
+ exx
+ rra
+ rra
+ rra
+ rra
+ and %1110
+ ld iy,PLY_LW_EffectTable
+ ld c,a
+ ld b,0
+ add iy,bc
+ exx
+ jp (iy)
+ ENDIF ;PLY_CFG_UseEffects
+
+PLY_LW_RT_WaitLong:
+ ;A 8-bit byte is encoded just after.
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetWaitEmptyCell),a
+ jr PLY_LW_RT_CellRead
+PLY_LW_RT_WaitShort:
+ ;Only a 2-bit value is encoded.
+ ld a,b
+ rla ;Transfers the bit 7/6 to 1/0.
+ rla
+ rla
+ and %11
+ ;inc a
+ ld (ix + PLY_LW_Data_OffsetWaitEmptyCell),a
+ ;jr PLY_LW_RT_CellRead
+;Jumped to after the Cell has been read.
+;IN: HL = new value of the Track pointer. Must point after the read Cell.
+PLY_LW_RT_CellRead:
+ ld (ix + PLY_LW_Data_OffsetPtTrack + 0),l
+ ld (ix + PLY_LW_Data_OffsetPtTrack + 1),h
+ ret
+
+
+;Manages the effects, if any. For the activated effects, modifies the internal data for the Track which data block is given.
+;IN: IX = data block of the Track.
+;OUT: IX, IY = unmodified.
+; C must NOT be modified!
+; DE' must NOT be modified!
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+PLY_LW_ManageEffects:
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ;Pitch up/down used?
+ ld a,(ix + PLY_LW_Data_OffsetIsPitchUpDownUsed)
+ or a
+ jr z,PLY_LW_ME_PitchUpDownFinished
+
+ ;Adds the LSB of integer part and decimal part, using one 16 bits operation.
+ ld l,(ix + PLY_LW_Data_OffsetTrackPitchDecimal)
+ ld h,(ix + PLY_LW_Data_OffsetTrackPitchInteger + 0)
+
+ ld e,(ix + PLY_LW_Data_OffsetTrackPitchSpeed + 0)
+ ld d,(ix + PLY_LW_Data_OffsetTrackPitchSpeed + 1)
+
+ ld a,(ix + PLY_LW_Data_OffsetTrackPitchInteger + 1)
+
+ ;Negative pitch?
+ bit 7,d
+ jr nz,PLY_LW_ME_PitchUpDown_NegativeSpeed
+
+PLY_LW_ME_PitchUpDown_PositiveSpeed:
+ ;Positive speed. Adds it to the LSB of the integer part, and decimal part.
+ add hl,de
+
+ ;Carry? Transmits it to the MSB of the integer part.
+ adc 0
+ jr PLY_LW_ME_PitchUpDown_Save
+PLY_LW_ME_PitchUpDown_NegativeSpeed:
+ ;Negative speed. Resets the sign bit. The encoded pitch IS positive.
+ ;Subtracts it to the LSB of the integer part, and decimal part.
+ res 7,d
+
+ or a
+ sbc hl,de
+
+ ;Carry? Transmits it to the MSB of the integer part.
+ sbc 0
+
+PLY_LW_ME_PitchUpDown_Save:
+ ld (ix + PLY_LW_Data_OffsetTrackPitchInteger + 1),a
+
+ ld (ix + PLY_LW_Data_OffsetTrackPitchDecimal),l
+ ld (ix + PLY_LW_Data_OffsetTrackPitchInteger + 0),h
+
+PLY_LW_ME_PitchUpDownFinished:
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+
+
+ ;Manages the Arpeggio Table effect, if any.
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_LW_Data_OffsetIsArpeggioTableUsed)
+ or a
+ jr z,PLY_LW_ME_ArpeggioTableFinished
+ ;Reads the Arpeggio Table. Adds the Arpeggio base address to an offset.
+ ld e,(ix + PLY_LW_Data_OffsetPtArpeggioTable + 0)
+ ld d,(ix + PLY_LW_Data_OffsetPtArpeggioTable + 1)
+ ld l,(ix + PLY_LW_Data_OffsetPtArpeggioOffset)
+PLY_LW_ME_ArpeggioTableReadAgain: ld h,0
+ add hl,de
+ ld a,(hl)
+ ;End of the Arpeggio?
+ sra a
+ jr nc,PLY_LW_ME_ArpeggioTableEndNotReached
+ ;End of the Arpeggio. The loop offset is now in A.
+ ld l,a ;And read the next value!
+ ld (ix + PLY_LW_Data_OffsetPtArpeggioOffset),a
+ jr PLY_LW_ME_ArpeggioTableReadAgain
+
+PLY_LW_ME_ArpeggioTableEndNotReached:
+ ;Not the end. A = arpeggio note.
+ ld (ix + PLY_LW_Data_OffsetCurrentArpeggioValue),a
+ ;Increases the offset for next time.
+ inc (ix + PLY_LW_Data_OffsetPtArpeggioOffset)
+PLY_LW_ME_ArpeggioTableFinished:
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+
+ ;Manages the Pitch Table effect, if any.
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_LW_Data_OffsetIsPitchTableUsed)
+ or a
+ ret z
+ ;Reads the Pitch Table. Adds the Pitch base address to an offset.
+ ld e,(ix + PLY_LW_Data_OffsetPtPitchTable + 0)
+ ld d,(ix + PLY_LW_Data_OffsetPtPitchTable + 1)
+ ld l,(ix + PLY_LW_Data_OffsetPtPitchOffset)
+PLY_LW_ME_PitchTableReadAgain: ld h,0
+ add hl,de
+ ld a,(hl)
+ ;End of the Pitch?
+ sra a
+ jr nc,PLY_LW_ME_PitchTableEndNotReached
+ ;End of the Pitch. The loop offset is now in A.
+ ld l,a ;And read the next value!
+ ld (ix + PLY_LW_Data_OffsetPtPitchOffset),a
+ jr PLY_LW_ME_PitchTableReadAgain
+
+PLY_LW_ME_PitchTableEndNotReached:
+ ;Not the end. A = pitch note. It is converted to 16 bits.
+ ld h,0
+ or a
+ jp p,PLY_LW_ME_PitchTableEndNotReached_Positive
+ dec h
+PLY_LW_ME_PitchTableEndNotReached_Positive:
+ ld (ix + PLY_LW_Data_OffsetCurrentPitchTableValue + 0),a
+ ld (ix + PLY_LW_Data_OffsetCurrentPitchTableValue + 1),h
+ ;Increases the offset for next time.
+ inc (ix + PLY_LW_Data_OffsetPtPitchOffset)
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ret
+
+ ENDIF ;PLY_CFG_UseEffects
+
+
+
+
+
+
+
+;---------------------------------------------------------------------
+;Sound stream.
+;---------------------------------------------------------------------
+
+;Plays the sound stream, filling the PSG registers table (but not playing it).
+;The Instrument pointer must be updated as it evolves inside the Instrument.
+;IN: IX = Data block of the Track.
+; IY = Points at the beginning of the register structure related to the channel.
+; C = R7. Only bit 2 (sound) must be *set* to cut the sound if needed, and bit 5 (noise) must be *reset* if there is noise.
+; DE' = Period table. Must not be modified.
+PLY_LW_PlaySoundStream:
+ ;Gets the pointer on the Instrument, from its base address and the offset.
+ ld l,(ix + PLY_LW_Data_OffsetPtInstrument + 0)
+ ld h,(ix + PLY_LW_Data_OffsetPtInstrument + 1)
+
+ ;Reads the first byte of the cell of the Instrument. What type?
+PLY_LW_PSS_ReadFirstByte:
+ ld a,(hl)
+ ld b,a
+ inc hl
+ rra
+ jr c,PLY_LW_PSS_SoftOrSoftAndHard
+
+ ;NoSoftNoHard or SoftwareToHardware
+ rra
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ jr c,PLY_LW_PSS_SoftwareToHardware
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+ ;No software no hardware, or end of sound (loop)!
+ ;End of sound?
+ rra
+ jr nc,PLY_LW_PSS_NSNH_NotEndOfSound
+ ;The sound loops/ends. Where?
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ ;As a sound always has at least one cell, we should safely be able to read its bytes without storing the instrument pointer.
+ ;However, we do it anyway to remove the overhead of the Speed management: if looping, the same last line will be read,
+ ;if several channels do so, it will be costly. So...
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 0),l
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 1),h
+ jr PLY_LW_PSS_ReadFirstByte
+
+PLY_LW_PSS_NSNH_NotEndOfSound:
+ ;No software, no hardware.
+ ;-------------------------
+ ;Stops the sound.
+ set 2,c
+
+ ;Volume. A now contains the volume on b0-3.
+ call PLY_LW_PSS_Shared_AdjustVolume
+ ld (iy + PLY_LW_Registers_OffsetVolume),a
+
+ ;Read noise?
+ rl b
+ IFDEF PLY_CFG_NoSoftNoHard_Noise ;CONFIG SPECIFIC
+ call c,PLY_LW_PSS_ReadNoise
+ ENDIF ;PLY_CFG_NoSoftNoHard_Noise
+ jr PLY_LW_PSS_Shared_StoreInstrumentPointer
+
+ ;Software sound, or Software and Hardware?
+PLY_LW_PSS_SoftOrSoftAndHard:
+ rra
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ jr c,PLY_LW_PSS_SoftAndHard
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+ ;Software sound.
+ ;-----------------
+ ;A is the volume. Already shifted twice, so it can be used directly.
+ call PLY_LW_PSS_Shared_AdjustVolume
+ ld (iy + PLY_LW_Registers_OffsetVolume),a
+
+ ;Arp and/or noise?
+ ld d,0 ;Default arpeggio.
+ rl b
+ jr nc,PLY_LW_PSS_S_AfterArpAndOrNoise
+ ld a,(hl)
+ inc hl
+ ;Noise?
+ sra a
+ ;A is now the signed Arpeggio. It must be kept.
+ ld d,a
+ ;Now takes care of the noise, if there is a Carry.
+ IFDEF PLY_LW_USE_Noise ;CONFIG SPECIFIC
+ call c,PLY_LW_PSS_ReadNoise
+ ENDIF ;PLY_LW_USE_Noise
+PLY_LW_PSS_S_AfterArpAndOrNoise:
+
+ ld a,d ;Gets the instrument arpeggio, if any.
+ call PLY_LW_CalculatePeriodForBaseNote
+
+ ;Read pitch?
+ rl b
+ IFDEF PLY_CFG_SoftOnly_SoftwarePitch ;CONFIG SPECIFIC
+ call c,PLY_LW_ReadPitchAndAddToPeriod
+ ENDIF ;PLY_CFG_SoftOnly_SoftwarePitch
+
+ ;Stores the new period of this channel.
+ exx
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodLSB),l
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodMSB),h
+ exx
+
+ ;The code below is mutualized!
+ ;Stores the new instrument pointer, if Speed allows it.
+ ;--------------------------------------------------
+PLY_LW_PSS_Shared_StoreInstrumentPointer:
+ ;Checks the Instrument speed, and only stores the Instrument new pointer if the speed is reached.
+ ld a,(ix + PLY_LW_Data_OffsetInstrumentCurrentStep)
+ cp (ix + PLY_LW_Data_OffsetInstrumentSpeed)
+ jr z,PLY_LW_PSS_S_SpeedReached
+ ;Increases the current step.
+ inc (ix + PLY_LW_Data_OffsetInstrumentCurrentStep)
+ ret
+PLY_LW_PSS_S_SpeedReached:
+ ;Stores the Instrument new pointer, resets the speed counter.
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 0),l
+ ld (ix + PLY_LW_Data_OffsetPtInstrument + 1),h
+ ld (ix + PLY_LW_Data_OffsetInstrumentCurrentStep),0
+ ret
+
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+
+ ;Software and Hardware.
+ ;----------------------------
+PLY_LW_PSS_SoftAndHard:
+ ;Reads the envelope bit, the possible pitch, and sets the software period accordingly.
+ call PLY_LW_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv
+ ;Reads the hardware period.
+ ld a,(hl)
+ ld (PLY_LW_Reg11),a
+ inc hl
+ ld a,(hl)
+ ld (PLY_LW_Reg12),a
+ inc hl
+
+ jr PLY_LW_PSS_Shared_StoreInstrumentPointer
+
+
+ ;Software to Hardware.
+ ;-------------------------
+PLY_LW_PSS_SoftwareToHardware:
+ call PLY_LW_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv
+
+ ;Now we can calculate the hardware period thanks to the ratio.
+ ld a,b
+ rra
+ rra
+ and %11100
+ ld (PLY_LW_PSS_STH_Jump + 1),a
+ exx
+PLY_LW_PSS_STH_Jump: jr $ + 2 ;Automodified by the line above to jump to the right place.
+ srl h
+ rr l
+ srl h
+ rr l
+ srl h
+ rr l
+ srl h
+ rr l
+ srl h
+ rr l
+ srl h
+ rr l
+ srl h
+ rr l
+ jr nc,PLY_LW_PSS_STH_RatioEnd
+ inc hl
+PLY_LW_PSS_STH_RatioEnd:
+ ld a,l
+ ld (PLY_LW_Reg11),a
+ ld a,h
+ ld (PLY_LW_Reg12),a
+ exx
+
+ jr PLY_LW_PSS_Shared_StoreInstrumentPointer
+
+;A shared code for hardware sound.
+;Reads the envelope bit in bit 1, arpeggio in bit 7 pitch in bit 2 from A. If pitch present, adds it to BC'.
+;Converts the note to period, adds the instrument pitch, sets the software period of the channel.
+;Also sets the hardware volume, and sets the hardware curve.
+PLY_LW_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv:
+ ;Envelope bit? R13 = 8 + 2 * (envelope bit?). Allows to have hardware envelope to 8 or 0xa.
+ ;Shifted by 2 to the right, bit 1 is now envelope bit, which is perfect for us.
+ and %10
+ add a,8
+ ld (PLY_LW_SetReg13 + 1),a
+
+ ;Volume to 16 to trigger the hardware envelope.
+ ld (iy + PLY_LW_Registers_OffsetVolume),16
+
+ ;Arpeggio?
+ xor a ;Default arpeggio.
+ IFDEF PLY_LW_ArpeggioInHardwareInstrument ;CONFIG SPECIFIC
+ bit 7,b ;Not shifted yet.
+ jr z,PLY_LW_PSS_Shared_REnvBAP_AfterArpeggio
+ ;Reads the Arpeggio.
+ ld a,(hl)
+ inc hl
+PLY_LW_PSS_Shared_REnvBAP_AfterArpeggio:
+ ENDIF ;PLY_LW_ArpeggioInHardwareInstrument
+ ;Calculates the software period.
+ call PLY_LW_CalculatePeriodForBaseNote
+
+ ;Pitch?
+ IFDEF PLY_LW_PitchInHardwareInstrument ;CONFIG SPECIFIC
+ bit 2,b ;Not shifted yet.
+ call nz,PLY_LW_ReadPitchAndAddToPeriod
+ ENDIF ;PLY_LW_PitchInHardwareInstrument
+
+ ;Stores the new period of this channel.
+ exx
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodLSB),l
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodMSB),h
+ exx
+ ret
+
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+;Decreases the given volume (encoded in possibly more then 4 bits). If <0, forced to 0.
+;IN: A = volume, not ANDed.
+;OUT: A = new volume.
+PLY_LW_PSS_Shared_AdjustVolume:
+ and %1111
+ sub (ix + PLY_LW_Data_OffsetTrackInvertedVolume)
+ ret nc
+ xor a
+ ret
+
+;Reads and stores the noise pointed by HL, opens the noise channel.
+;IN: HL = instrument data where the noise is.
+;OUT: HL = HL++.
+;MOD: A.
+ IFDEF PLY_LW_USE_Noise ;CONFIG SPECIFIC
+PLY_LW_PSS_ReadNoise:
+ ld a,(hl)
+ inc hl
+ ld (PLY_LW_NoiseRegister),a
+ res 5,c ;Opens the noise channel.
+ ret
+ ENDIF ;PLY_LW_USE_Noise
+
+;Calculates the period according to the base note and put it in BC'. Used by both software and hardware codes.
+;IN: DE' = period table.
+; A = instrument arpeggio (0 if not used).
+;OUT: HL' = period.
+;MOD: A
+PLY_LW_CalculatePeriodForBaseNote:
+ ;Gets the period from the current note.
+ exx
+ ld h,0
+ add a,(ix + PLY_LW_Data_OffsetBaseNote) ;Adds the instrument Arp to the base note (including the transposition).
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ add (ix + PLY_LW_Data_OffsetCurrentArpeggioValue) ;Adds the Arpeggio Table effect.
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ ld l,a
+ sla l ;Note encoded on 7 bits, so should be fine.
+ add hl,de
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a ;HL' = period.
+
+ ;Adds the Pitch Table value, if used.
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_LW_Data_OffsetIsPitchTableUsed)
+ or a
+ jr z,PLY_LW_CalculatePeriodForBaseNote_NoPitchTable
+ ld c,(ix + PLY_LW_Data_OffsetCurrentPitchTableValue + 0)
+ ld b,(ix + PLY_LW_Data_OffsetCurrentPitchTableValue + 1)
+ add hl,bc
+PLY_LW_CalculatePeriodForBaseNote_NoPitchTable:
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ;Adds the Track Pitch.
+ IFDEF PLY_LW_USE_EffectPitchUpDown
+ ld c,(ix + PLY_LW_Data_OffsetTrackPitchInteger + 0)
+ ld b,(ix + PLY_LW_Data_OffsetTrackPitchInteger + 1)
+ add hl,bc
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+ exx
+ ret
+
+ IFDEF PLY_LW_PitchInInstrument ;CONFIG SPECIFIC
+;Reads the pitch in the Instruments (16 bits) and adds it to HL', which should contain the software period.
+;IN: HL = points on the pitch value.
+;OUT: HL = points after the pitch.
+;MOD: A, BC', HL' updated.
+PLY_LW_ReadPitchAndAddToPeriod:
+ ;Reads 2 * 8 bits for the pitch. Slow...
+ ld a,(hl)
+ inc hl
+ exx
+ ld c,a ;Adds the read pitch to the note period.
+ exx
+ ld a,(hl)
+ inc hl
+ exx
+ ld b,a
+ add hl,bc
+ exx
+ ret
+ ENDIF ;PLY_LW_PitchInInstrument
+
+
+
+
+
+
+
+
+
+
+
+
+
+;---------------------------------------------------------------------
+;Effect management.
+;---------------------------------------------------------------------
+
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+
+;IN: HL = points after the first byte.
+; B = data of the first byte on bits 0-4 (will probably needed to be ANDed, as bits 5-7 are undetermined).
+; DE'= Instrument Table (not useful here). Do not modify!
+; IX = data block of the Track.
+;OUT: HL = points after the data of the effect (maybe nothing to do).
+; Each effect must jump to PLY_LW_RT_CellRead.
+
+;Clears all the effects (volume, pitch table, arpeggio table).
+ IFDEF PLY_CFG_UseEffect_Reset ;CONFIG SPECIFIC.
+PLY_LW_EffectReset:
+ ;Inverted volume.
+ call PLY_LW_ReadInvertedVolumeFromB
+
+ xor a
+ ;The inverted volume is managed above, so don't change it.
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetIsPitchUpDownUsed),a
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetIsArpeggioTableUsed),a
+ ld (ix + PLY_LW_Data_OffsetCurrentArpeggioValue),a ;Contrary to the Pitch, the value must be reset.
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld (ix + PLY_LW_Data_OffsetIsPitchTableUsed),a
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ jp PLY_LW_RT_CellRead
+ ENDIF ;PLY_CFG_UseEffect_Reset
+
+;Changes the volume. Possibly changes the Track pitch.
+ IFDEF PLY_LW_USE_Volume_And_PitchUpDown_Effects ;CONFIG SPECIFIC.
+PLY_LW_EffectVolumeAndPitchUpDown:
+ ;Stores the new inverted volume.
+ call PLY_LW_ReadInvertedVolumeFromB
+
+ ;Pitch? Warning, the code below is shared with the PitchUp/Down effect.
+ bit 4,b
+ jp z,PLY_LW_RT_CellRead
+ ;Pitch present. Reads and stores its 16 bits value (integer/decimal).
+PLY_LW_EffectPitchUpDown_Activated:
+ ;Code shared with the effect above.
+ ;Activates the effect.
+ ld (ix + PLY_LW_Data_OffsetIsPitchUpDownUsed),255
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetTrackPitchSpeed + 0),a
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetTrackPitchSpeed + 1),a
+
+ jp PLY_LW_RT_CellRead
+ ENDIF ;PLY_LW_USE_Volume_And_PitchUpDown_Effects
+
+
+;Effect table. Each entry jumps to an effect management code.
+;Put after the code above so that the JR are within bound.
+PLY_LW_EffectTable:
+ IFDEF PLY_LW_EffectReset ;CONFIG SPECIFIC
+ jr PLY_LW_EffectReset ;000
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ jr PLY_LW_EffectArpeggioTable ;001
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ jr PLY_LW_EffectPitchTable ;010
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ jr PLY_LW_EffectPitchUpDown ;011
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_LW_USE_Volume_And_PitchUpDown_Effects ;CONFIG SPECIFIC
+ jr PLY_LW_EffectVolumeAndPitchUpDown ;100
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_LW_USE_Volume_And_ArpeggioTable_Effects ;CONFIG SPECIFIC
+ jr PLY_LW_EffectVolumeArpeggioTable ;101
+ ELSE
+ jr $
+ ENDIF
+
+ IFDEF PLY_LW_USE_Reset_And_ArpeggioTable_Effects ;CONFIG SPECIFIC
+ jr PLY_LW_EffectResetArpeggioTable ;110
+ ELSE
+ jr $
+ ENDIF
+
+ ;111 Unused.
+
+
+
+;Pitch up/down effect, activation or stop.
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+PLY_LW_EffectPitchUpDown:
+ rr b ;Pitch present or pitch stop?
+ jr c,PLY_LW_EffectPitchUpDown_Activated
+ ;Pitch stop.
+ ld (ix + PLY_LW_Data_OffsetIsPitchUpDownUsed),0
+ jp PLY_LW_RT_CellRead
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+
+;Arpeggio table effect, activation or stop.
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+PLY_LW_EffectArpeggioTable:
+ ld a,b
+ and %11111
+PLY_LW_EffectArpeggioTable_AfterMask:
+ ld (ix + PLY_LW_Data_OffsetIsArpeggioTableUsed),a ;Sets to 0 if the Arpeggio is stopped, or any other value if it starts.
+ jr z,PLY_LW_EffectArpeggioTable_Stop
+
+ ;Gets the Arpeggio address.
+ add a,a
+ exx
+ ld l,a
+ ld h,0
+dknr3:
+PLY_LW_PtArpeggios: ld bc,0
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetPtArpeggioTable + 0),a
+ ld a,(hl)
+ ld (ix + PLY_LW_Data_OffsetPtArpeggioTable + 1),a
+ exx
+
+ ;Resets the offset of the Arpeggio, to force a restart.
+ xor a
+ ld (ix + PLY_LW_Data_OffsetPtArpeggioOffset),a
+ jp PLY_LW_RT_CellRead
+PLY_LW_EffectArpeggioTable_Stop:
+ ;Contrary to the Pitch, the Arpeggio must also be set to 0 when stopped.
+ ld (ix + PLY_LW_Data_OffsetCurrentArpeggioValue),a
+ jp PLY_LW_RT_CellRead
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+;Pitch table effect, activation or stop.
+;This is exactly the same code as for the Arpeggio, but I can't find a way to share it...
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+PLY_LW_EffectPitchTable:
+ ld a,b
+ and %11111
+PLY_LW_EffectPitchTable_AfterMask:
+ ld (ix + PLY_LW_Data_OffsetIsPitchTableUsed),a ;Sets to 0 if the Pitch is stopped, or any other value if it starts.
+ jp z,PLY_LW_RT_CellRead
+
+ ;Gets the Pitch address.
+ add a,a
+ exx
+ ld l,a
+ ld h,0
+dknr3:
+PLY_LW_PtPitches: ld bc,0
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetPtPitchTable + 0),a
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_LW_Data_OffsetPtPitchTable + 1),a
+ exx
+
+ ;Resets the offset of the Pitch, to force a restart.
+ xor a
+ ld (ix + PLY_LW_Data_OffsetPtPitchOffset),a
+
+ jp PLY_LW_RT_CellRead
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+
+
+;Volume, and Arpeggio Table, activation or stop.
+ IFDEF PLY_LW_USE_Volume_And_ArpeggioTable_Effects ;CONFIG SPECIFIC
+PLY_LW_EffectVolumeArpeggioTable:
+ ;Stores the new inverted volume.
+ call PLY_LW_ReadInvertedVolumeFromB
+
+ ;Manages the Arpeggio, encoded just after.
+ ld a,(hl)
+ inc hl
+ or a ;Required, else a volume of 0 will disturb the flag test after the jump!
+ jr PLY_LW_EffectArpeggioTable_AfterMask
+ ENDIF ;PLY_LW_USE_Volume_And_ArpeggioTable_Effects
+
+;Reset, and Arpeggio Table (activation only).
+ IFDEF PLY_LW_USE_Reset_And_ArpeggioTable_Effects ;CONFIG SPECIFIC
+PLY_LW_EffectResetArpeggioTable:
+ ;Resets effects and read volume.
+ ;A bit of loss of CPU because we're going to set the Arpeggio just after, AND the effect pointer is stored!
+ ;Oh well, less memory taken this way.
+ call PLY_LW_EffectReset
+
+ ;Reads the Arpeggio.
+ ld a,(hl)
+ inc hl
+ or a ;Required, else a volume of 0 will disturb the flag test after the jump!
+ jp PLY_LW_EffectArpeggioTable_AfterMask ;No need to use the mask, the value is clean.
+ ENDIF ;PLY_LW_USE_Reset_And_ArpeggioTable_Effects
+
+
+;Reads the inverted volume from B, stored it after masking the bits in A.
+PLY_LW_ReadInvertedVolumeFromB:
+ ld a,b
+ and %1111
+ ld (ix + PLY_LW_Data_OffsetTrackInvertedVolume),a
+ ret
+
+ ENDIF ;PLY_CFG_UseEffects
+
+
+
+
+
+
+
+
+;---------------------------------------------------------------------
+;Data blocks for the three channels. Make sure NOTHING is added between, as the init clears everything!
+;---------------------------------------------------------------------
+
+;Data block for channel 1.
+PLY_LW_Track1_Data:
+dkbs:
+PLY_LW_Track1_WaitEmptyCell: db 0 ;How many empty cells have to be waited. 0 = none.
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+PLY_LW_Track1_Transposition: db 0
+ ENDIF ;PLY_CFG_UseTranspositions
+PLY_LW_Track1_BaseNote: db 0 ;Base note, such as the note played. The transposition IS included.
+PLY_LW_Track1_InstrumentCurrentStep: db 0 ;The current step on the Instrument (>=0, till it reaches the Speed).
+PLY_LW_Track1_InstrumentSpeed: db 0 ;The Instrument speed (>=0).
+PLY_LW_Track1_TrackInvertedVolume: db 0
+dkbe:
+dkws:
+PLY_LW_Track1_PtTrack: dw 0 ;Points on the next Cell of the Track to read. Evolves.
+PLY_LW_Track1_PtInstrument: dw 0 ;Points on the Instrument, evolves.
+PLY_LW_Track1_PtBaseInstrument: dw 0 ;Points on the base of the Instrument, does not evolve.
+dkwe:
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+dkbs:
+PLY_LW_Track1_IsPitchUpDownUsed: db 0 ;>0 if a Pitch Up/Down is currently in use.
+PLY_LW_Track1_TrackPitchDecimal: db 0 ;The decimal part of the Track pitch. Evolves as the pitch goes up/down.
+dkbe:
+dkws:
+PLY_LW_Track1_TrackPitchSpeed: dw 0 ;The integer and decimal part of the Track pitch speed. Is added to the Track Pitch every frame.
+PLY_LW_Track1_TrackPitchInteger: dw 0 ;The integer part of the Track pitch. Evolves as the pitch goes up/down.
+dkwe:
+dkbs:
+PLY_LW_Track1_IsArpeggioTableUsed: db 0 ;>0 if an Arpeggio Table is currently in use.
+PLY_LW_Track1_PtArpeggioOffset: db 0 ;Increases over the Arpeggio.
+PLY_LW_Track1_CurrentArpeggioValue: db 0 ;Value from the Arpeggio to add to the base note. Read even if the Arpeggio effect is deactivated.
+dkbe:
+dkws:
+PLY_LW_Track1_PtArpeggioTable: dw 0 ;Point on the base of the Arpeggio table, does not evolve.
+dkwe:
+dkbs:
+PLY_LW_Track1_IsPitchTableUsed: db 0 ;>0 if a Pitch Table is currently in use.
+PLY_LW_Track1_PtPitchOffset: db 0 ;Increases over the Pitch.
+dkbe:
+dkws:
+PLY_LW_Track1_CurrentPitchTableValue: dw 0 ;16 bit value from the Pitch to add to the base note. Not read if the Pitch effect is deactivated.
+PLY_LW_Track1_PtPitchTable: dw 0 ;Points on the base of the Pitch table, does not evolve.
+dkwe:
+ ENDIF ;PLY_CFG_UseEffects
+PLY_LW_Track1_Data_End:
+
+PLY_LW_Track1_Data_Size: equ PLY_LW_Track1_Data_End - PLY_LW_Track1_Data
+
+PLY_LW_Data_OffsetWaitEmptyCell: equ PLY_LW_Track1_WaitEmptyCell - PLY_LW_Track1_Data
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+PLY_LW_Data_OffsetTransposition: equ PLY_LW_Track1_Transposition - PLY_LW_Track1_Data
+ ENDIF ;PLY_CFG_UseTranspositions
+
+PLY_LW_Data_OffsetPtTrack: equ PLY_LW_Track1_PtTrack - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetBaseNote: equ PLY_LW_Track1_BaseNote - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtInstrument: equ PLY_LW_Track1_PtInstrument - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtBaseInstrument: equ PLY_LW_Track1_PtBaseInstrument - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetInstrumentCurrentStep: equ PLY_LW_Track1_InstrumentCurrentStep - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetInstrumentSpeed: equ PLY_LW_Track1_InstrumentSpeed - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetTrackInvertedVolume: equ PLY_LW_Track1_TrackInvertedVolume - PLY_LW_Track1_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ IFDEF PLY_LW_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+PLY_LW_Data_OffsetIsPitchUpDownUsed: equ PLY_LW_Track1_IsPitchUpDownUsed - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetTrackPitchInteger: equ PLY_LW_Track1_TrackPitchInteger - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetTrackPitchDecimal: equ PLY_LW_Track1_TrackPitchDecimal - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetTrackPitchSpeed: equ PLY_LW_Track1_TrackPitchSpeed - PLY_LW_Track1_Data
+ ENDIF ;PLY_LW_USE_EffectPitchUpDown
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+PLY_LW_Data_OffsetIsArpeggioTableUsed: equ PLY_LW_Track1_IsArpeggioTableUsed - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtArpeggioTable: equ PLY_LW_Track1_PtArpeggioTable - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtArpeggioOffset: equ PLY_LW_Track1_PtArpeggioOffset - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetCurrentArpeggioValue: equ PLY_LW_Track1_CurrentArpeggioValue - PLY_LW_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+PLY_LW_Data_OffsetIsPitchTableUsed: equ PLY_LW_Track1_IsPitchTableUsed - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtPitchTable: equ PLY_LW_Track1_PtPitchTable - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetCurrentPitchTableValue: equ PLY_LW_Track1_CurrentPitchTableValue - PLY_LW_Track1_Data
+PLY_LW_Data_OffsetPtPitchOffset: equ PLY_LW_Track1_PtPitchOffset - PLY_LW_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ENDIF ;PLY_CFG_UseEffects
+
+;Data block for channel 2.
+PLY_LW_Track2_Data:
+dkbs:
+ ds PLY_LW_Track1_Data_Size, 0
+dkbe:
+PLY_LW_Track2_Data_End:
+PLY_LW_Track2_WaitEmptyCell: equ PLY_LW_Track2_Data + PLY_LW_Data_OffsetWaitEmptyCell
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+PLY_LW_Track2_Transposition: equ PLY_LW_Track2_Data + PLY_LW_Data_OffsetTransposition
+ ENDIF ;PLY_CFG_UseTranspositions
+
+PLY_LW_Track2_PtTrack: equ PLY_LW_Track2_Data + PLY_LW_Data_OffsetPtTrack
+PLY_LW_Track2_PtInstrument: equ PLY_LW_Track2_Data + PLY_LW_Data_OffsetPtInstrument
+
+;Data block for channel 3.
+PLY_LW_Track3_Data:
+dkbs:
+ ds PLY_LW_Track1_Data_Size, 0
+dkbe:
+PLY_LW_Track3_Data_End:
+PLY_LW_Track3_WaitEmptyCell: equ PLY_LW_Track3_Data + PLY_LW_Data_OffsetWaitEmptyCell
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+PLY_LW_Track3_Transposition: equ PLY_LW_Track3_Data + PLY_LW_Data_OffsetTransposition
+ ENDIF ;PLY_CFG_UseTranspositions
+PLY_LW_Track3_PtTrack: equ PLY_LW_Track3_Data + PLY_LW_Data_OffsetPtTrack
+PLY_LW_Track3_PtInstrument: equ PLY_LW_Track3_Data + PLY_LW_Data_OffsetPtInstrument
+
+ ;Makes sure the structure all have the same size!
+ ASSERT (PLY_LW_Track1_Data_End - PLY_LW_Track1_Data) == (PLY_LW_Track2_Data_End - PLY_LW_Track2_Data)
+ ASSERT (PLY_LW_Track1_Data_End - PLY_LW_Track1_Data) == (PLY_LW_Track3_Data_End - PLY_LW_Track3_Data)
+ ;No holes between the blocks, the init makes a LDIR to clear everything!
+ ASSERT PLY_LW_Track1_Data_End == PLY_LW_Track2_Data
+ ASSERT PLY_LW_Track2_Data_End == PLY_LW_Track3_Data
+
+
+
+;---------------------------------------------------------------------
+;Register block for all the channels. They are "polluted" with pointers to code because all this
+;is actually a RET table!
+;---------------------------------------------------------------------
+;DB register, DB value then DW code to jump to once the value is read.
+PLY_LW_Registers_RetTable:
+PLY_LW_Track1_Registers:
+dkbs: db 8
+PLY_LW_Track1_Volume: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 0
+PLY_LW_Track1_SoftwarePeriodLSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 1
+PLY_LW_Track1_SoftwarePeriodMSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+PLY_LW_Track2_Registers:
+dkbs: db 9
+PLY_LW_Track2_Volume: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 2
+PLY_LW_Track2_SoftwarePeriodLSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 3
+PLY_LW_Track2_SoftwarePeriodMSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+
+PLY_LW_Track3_Registers:
+dkbs: db 10
+PLY_LW_Track3_Volume: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 4
+PLY_LW_Track3_SoftwarePeriodLSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 5
+PLY_LW_Track3_SoftwarePeriodMSB: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+;Generic registers.
+ IFDEF PLY_LW_USE_NoiseRegister ;CONFIG SPECIFIC
+dkbs: db 6
+PLY_LW_NoiseRegister: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+ ENDIF ;PLY_LW_USE_NoiseRegister
+
+dkbs: db 7
+PLY_LW_MixerRegister: db 0
+dkbe:
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+
+dkbs: db 11
+PLY_LW_Reg11: db 0
+dkbe:
+dkps: dw PLY_LW_SendPsgRegister
+dkpe:
+
+dkbs: db 12
+PLY_LW_Reg12: db 0
+dkbe:
+dkps:
+ dw PLY_LW_SendPsgRegisterR13
+ ;This one is a trick to send the register after R13 is managed.
+ dw PLY_LW_SendPsgRegisterAfterPop
+
+ dw PLY_LW_SendPsgRegisterEnd
+dkpe:
+ ELSE
+dkps:
+ dw PLY_LW_SendPsgRegisterEnd
+dkpe:
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+
+
+PLY_LW_Registers_OffsetVolume: equ PLY_LW_Track1_Volume - PLY_LW_Track1_Registers
+PLY_LW_Registers_OffsetSoftwarePeriodLSB: equ PLY_LW_Track1_SoftwarePeriodLSB - PLY_LW_Track1_Registers
+PLY_LW_Registers_OffsetSoftwarePeriodMSB: equ PLY_LW_Track1_SoftwarePeriodMSB - PLY_LW_Track1_Registers
+
+;The period table for each note (from 0 to 127 included).
+PLY_LW_PeriodTable:
+dkws
+ IFDEF PLY_LW_HARDWARE_CPC
+ ;PSG running to 1000000 Hz.
+ dw 3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025 ;0
+ dw 1911,1804,1703,1607,1517,1432,1351,1276,1204,1136,1073,1012 ;12
+ dw 956,902,851,804,758,716,676,638,602,568,536,506 ;24
+ dw 478,451,426,402,379,358,338,319,301,284,268,253 ;36
+ dw 239,225,213,201,190,179,169,159,150,142,134,127 ;48
+ dw 119,113,106,100,95,89,84,80,75,71,67,63 ;60
+ dw 60,56,53,50,47,45,42,40,38,36,34,32 ;72
+ dw 30,28,27,25,24,22,21,20,19,18,17,16 ;84
+ dw 15,14,13,13,12,11,11,10,9,9,8,8 ;96
+ dw 7,7,7,6,6,6,5,5,5,4,4,4 ;108
+ dw 4,4,3,3,3,3,3,2 ;,2,2,2,2 ;120 -> 127
+ ENDIF
+
+ IFDEF PLY_LW_HARDWARE_SPECTRUM_OR_MSX
+ ;PSG running to 1773400 Hz.
+ dw 6778, 6398, 6039, 5700, 5380, 5078, 4793, 4524, 4270, 4030, 3804, 3591 ; Octave 0
+ dw 3389, 3199, 3019, 2850, 2690, 2539, 2397, 2262, 2135, 2015, 1902, 1795 ; Octave 1
+ dw 1695, 1599, 1510, 1425, 1345, 1270, 1198, 1131, 1068, 1008, 951, 898 ; Octave 2
+ dw 847, 800, 755, 712, 673, 635, 599, 566, 534, 504, 476, 449 ; Octave 3
+ dw 424, 400, 377, 356, 336, 317, 300, 283, 267, 252, 238, 224 ; Octave 4
+ dw 212, 200, 189, 178, 168, 159, 150, 141, 133, 126, 119, 112 ; Octave 5
+ dw 106, 100, 94, 89, 84, 79, 75, 71, 67, 63, 59, 56 ; Octave 6
+ dw 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28 ; Octave 7
+ dw 26, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14 ; Octave 8
+ dw 13, 12, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7 ; Octave 9
+ dw 7, 6, 6, 6, 5, 5, 5, 4 ; Octave 10
+ ENDIF
+
+ IFDEF PLY_LW_HARDWARE_PENTAGON
+ ;PSG running to 1750000 Hz.
+ dw 6689, 6314, 5959, 5625, 5309, 5011, 4730, 4464, 4214, 3977, 3754, 3543 ; Octave 0
+ dw 3344, 3157, 2980, 2812, 2655, 2506, 2365, 2232, 2107, 1989, 1877, 1772 ; Octave 1
+ dw 1672, 1578, 1490, 1406, 1327, 1253, 1182, 1116, 1053, 994, 939, 886 ; Octave 2
+ dw 836, 789, 745, 703, 664, 626, 591, 558, 527, 497, 469, 443 ; Octave 3
+ dw 418, 395, 372, 352, 332, 313, 296, 279, 263, 249, 235, 221 ; Octave 4
+ dw 209, 197, 186, 176, 166, 157, 148, 140, 132, 124, 117, 111 ; Octave 5
+ dw 105, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55 ; Octave 6
+ dw 52, 49, 47, 44, 41, 39, 37, 35, 33, 31, 29, 28 ; Octave 7
+ dw 26, 25, 23, 22, 21, 20, 18, 17, 16, 16, 15, 14 ; Octave 8
+ dw 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7 ; Octave 9
+ dw 7, 6, 6, 5, 5, 5, 5, 4 ; Octave 10
+ ENDIF
+dkwe
+PLY_LW_End:
+
+
+
diff --git a/lib/plw/player/PlayerLightWeight_SoundEffects.asm b/lib/plw/player/PlayerLightWeight_SoundEffects.asm
new file mode 100644
index 0000000..cd15cb8
--- /dev/null
+++ b/lib/plw/player/PlayerLightWeight_SoundEffects.asm
@@ -0,0 +1,458 @@
+; Player of sound effects, for the Lightweight player.
+
+ ;Is there a loaded Player Configuration source? If no, use a default configuration.
+ IFNDEF PLY_CFG_SFX_ConfigurationIsPresent
+ PLY_CFG_UseHardwareSounds = 1
+ PLY_CFG_SFX_LoopTo = 1
+ PLY_CFG_SFX_NoSoftNoHard = 1
+ PLY_CFG_SFX_NoSoftNoHard_Noise = 1
+ PLY_CFG_SFX_SoftOnly = 1
+ PLY_CFG_SFX_SoftOnly_Noise = 1
+ PLY_CFG_SFX_HardOnly = 1
+ PLY_CFG_SFX_HardOnly_Noise = 1
+ PLY_CFG_SFX_HardOnly_Retrig = 1
+ PLY_CFG_SFX_SoftAndHard = 1
+ PLY_CFG_SFX_SoftAndHard_Noise = 1
+ PLY_CFG_SFX_SoftAndHard_Retrig = 1
+ ENDIF
+
+; Agglomerates some Player Configuration flags.
+; --------------------------------------------
+; Mixes the Hardware flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly
+ PLY_LW_SE_HardwareSounds = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard
+ PLY_LW_SE_HardwareSounds = 1
+ ENDIF
+; Mixes the Hardware Noise flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly_Noise
+ PLY_LW_SE_HardwareNoise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard_Noise
+ PLY_LW_SE_HardwareNoise = 1
+ ENDIF
+; Mixes the Noise flags into one.
+ IFDEF PLY_LW_SE_HardwareNoise
+ PLY_LW_SE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise
+ PLY_LW_SE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftOnly
+ PLY_LW_SE_Noise = 1
+ ENDIF
+; Noise in Sound Effects? Then noise register code must be compiled.
+ IFDEF PLY_LW_SE_Noise
+ PLY_LW_USE_NoiseRegister = 1
+ ENDIF
+; Mixes the Software Volume flags into one.
+ IFDEF PLY_CFG_SFX_NoSoftNoHard
+ PLY_LW_SE_VolumeSoft = 1
+ PLY_LW_SE_VolumeSoftOrHard = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftOnly
+ PLY_LW_SE_VolumeSoft = 1
+ PLY_LW_SE_VolumeSoftOrHard = 1
+ ENDIF
+; Mixes the volume (soft/hard) into one.
+ IFDEF PLY_CFG_UseHardwareSounds
+ PLY_LW_SE_VolumeSoftOrHard = 1
+ ENDIF
+; Mixes the retrig flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly_Retrig
+ PLY_LW_SE_Retrig = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard_Retrig
+ PLY_LW_SE_Retrig = 1
+ ENDIF
+
+
+;Initializes the sound effects. It MUST be called at any times before a first sound effect is triggered.
+;It doesn't matter whether the song is playing or not, or if it has been initialized or not.
+;IN: HL = Address to the sound effects data.
+PLY_LW_InitSoundEffectsDisarkGenerateExternalLabel:
+PLY_LW_InitSoundEffects:
+ ld (PLY_LW_SE_PtSoundEffectTable + 1),hl
+ ret
+
+
+;Plays a sound effect. If a previous one was already playing on the same channel, it is replaced.
+;This does not actually plays the sound effect, but programs its playing.
+;The music player, when called, will call the PLY_LW_PlaySoundEffectsStream method below.
+;IN: A = Sound effect number (>0!).
+; C = The channel where to play the sound effect (0, 1, 2).
+; B = Inverted volume (0 = full volume, 16 = no sound). Hardware sounds are also lowered.
+PLY_LW_PlaySoundEffectDisarkGenerateExternalLabel:
+PLY_LW_PlaySoundEffect:
+ ;Gets the address to the sound effect.
+ dec a ;The 0th is not encoded.
+dknr3:
+PLY_LW_SE_PtSoundEffectTable: ld hl,0
+ ld e,a
+ ld d,0
+ add hl,de
+ add hl,de
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ ;Reads the header of the sound effect to get the speed.
+ ld a,(de)
+ inc de
+
+ push af
+ ;ex af,af'
+
+ ld a,b
+
+ ;Finds the pointer to the sound effect of the desired channel.
+ ld hl,PLY_LW_Channel1_SoundEffectData
+ ld b,0
+ sla c
+ sla c
+ sla c
+ add hl,bc
+ ld (hl),e
+ inc hl
+ ld (hl),d
+ inc hl
+
+ ;Now stores the inverted volume.
+ ld (hl),a
+ inc hl
+
+ ;Resets the current speed, stores the instrument speed.
+ ld (hl),0
+ inc hl
+
+ ;ex af,af'
+ pop af
+
+ ld (hl),a
+
+ ret
+
+;Stops a sound effect. Nothing happens if there was no sound effect.
+;IN: A = The channel where to stop the sound effect (0, 1, 2).
+PLY_LW_StopSoundEffectFromChannelDisarkGenerateExternalLabel:
+PLY_LW_StopSoundEffectFromChannel:
+ ;Puts 0 to the pointer of the sound effect.
+ add a,a
+ add a,a
+ add a,a
+ ld e,a
+ ld d,0
+ ld hl,PLY_LW_Channel1_SoundEffectData
+ add hl,de
+ ld (hl),d ;0 means "no sound".
+ inc hl
+ ld (hl),d
+ ret
+
+;Plays the sound effects, if any has been triggered by the user.
+;This does not actually send registers to the PSG, it only overwrite the required values of the registers of the player.
+;The sound effects initialization method must have been called before!
+;As R7 is required, this must be called after the music has been played, but BEFORE the registers are sent to the PSG.
+;IN: A = R7.
+PLY_LW_PlaySoundEffectsStream:
+ ;Shifts the R7 to the left twice, so that bit 2 and 5 only can be set for each track, below.
+ rla
+ rla
+
+ ;Plays the sound effects on every track.
+ ld ix,PLY_LW_Channel1_SoundEffectData
+ ld iy,PLY_LW_Track1_Registers
+ ld c,a
+ call PLY_LW_PSES_Play
+ ld ix,PLY_LW_Channel2_SoundEffectData
+ ld iy,PLY_LW_Track2_Registers
+ srl c ;Not RR, because we have to make sure the b6 is 0, else no more keyboard (on CPC)!
+ ;Also, on MSX, bit 6 must be 0.
+ call PLY_LW_PSES_Play
+ ld ix,PLY_LW_Channel3_SoundEffectData
+ ld iy,PLY_LW_Track3_Registers
+ IFDEF PLY_LW_HARDWARE_MSX
+ scf ;On MSX, bit 7 must be 1.
+ rr c
+ ELSE
+ rr c ;On other platforms, we don't care about b7.
+ ENDIF
+ call PLY_LW_PSES_Play
+
+ ld a,c
+ ld (PLY_LW_MixerRegister),a
+ ret
+
+
+;Plays the sound stream from the given pointer to the sound effect. If 0, no sound is played.
+;The given R7 is given shift twice to the left, so that this code MUST set/reset the bit 2 (sound), and maybe reset bit 5 (noise).
+;This code MUST overwrite these bits because sound effects have priority over the music.
+;IN: IX = Points on the sound effect pointer. If the sound effect pointer is 0, nothing must be played.
+; IY = Points at the beginning of the register structure related to the channel.
+; C = R7, shifted twice to the left.
+;OUT: The pointed pointer by IX may be modified as the sound advances.
+; C = R7, MUST be modified if there is a sound effect.
+PLY_LW_PSES_Play:
+ ;Reads the pointer pointed by IX.
+ ld l,(ix + 0)
+ ld h,(ix + 1)
+ ld a,l
+ or h
+ ret z ;No sound to be played? Returns immediately.
+
+ ;Reads the first byte. What type of sound is it?
+PLY_LW_PSES_ReadFirstByte:
+ ld a,(hl)
+ inc hl
+ ld b,a
+ rra
+ jr c,PLY_LW_PSES_SoftwareOrSoftwareAndHardware
+ rra
+ IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC
+ jr c,PLY_LW_PSES_HardwareOnly
+ ENDIF ;PLY_CFG_SFX_HardOnly
+
+ ;No software, no hardware, or end/loop.
+ ;-------------------------------------------
+ ;End or loop?
+ rra
+ IFDEF PLY_CFG_SFX_NoSoftNoHard ;CONFIG SPECIFIC. If not present, the jump is not needed, the method is just below.
+ jr c,PLY_LW_PSES_S_EndOrLoop
+
+ ;No software, no hardware.
+ ;-------------------------------------------
+ ;Gets the volume.
+ call PLY_LW_PSES_ManageVolumeFromA_Filter4Bits
+
+ ;Noise?
+ IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise ;CONFIG SPECIFIC
+ rl b
+ call c,PLY_LW_PSES_ReadNoiseAndOpenNoiseChannel
+ ENDIF ;PLY_CFG_SFX_NoSoftNoHard_Noise
+
+ jr PLY_LW_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_NoSoftNoHard
+
+ ;**Warning!** Do not put any instruction between EndOrLoop and NoSoftNoHard.
+
+PLY_LW_PSES_S_EndOrLoop:
+ IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC. If no "loop to", the sounds always end, no need to test.
+ ;Is it an end?
+ rra
+ jr c,PLY_LW_PSES_S_Loop
+ ENDIF ;PLY_CFG_SFX_LoopTo
+ ;End of the sound. Marks the sound pointer with 0, meaning "no sound".
+ xor a
+ ld (ix + 0),a
+ ld (ix + 1),a
+ ret
+ IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC.
+PLY_LW_PSES_S_Loop:
+ ;Loops. Reads the pointer and directly uses it.
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ jr PLY_LW_PSES_ReadFirstByte
+ ENDIF ;PLY_CFG_SFX_LoopTo
+
+
+;Saves HL into IX, and exits. This must be called at the end of each Cell.
+;If the speed has not been reached, it is not saved.
+PLY_LW_PSES_SavePointerAndExit:
+ ;Speed reached?
+ ld a,(ix + PLY_LW_SoundEffectData_OffsetCurrentStep)
+ cp (ix + PLY_LW_SoundEffectData_OffsetSpeed)
+ jr c,PLY_LW_PSES_NotReached
+ ;The speed has been reached, so resets it and saves the pointer to the next cell to read.
+ ld (ix + PLY_LW_SoundEffectData_OffsetCurrentStep),0
+ ld (ix + 0),l
+ ld (ix + 1),h
+ ret
+PLY_LW_PSES_NotReached:
+ ;Speed not reached. Increases it, that's all. The same cell will be read next time.
+ inc (ix + PLY_LW_SoundEffectData_OffsetCurrentStep)
+ ret
+
+ IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC
+ ;Hardware only.
+ ;-------------------------------------------
+PLY_LW_PSES_HardwareOnly:
+ ;Calls the shared code that manages everything.
+ call PLY_LW_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise
+ ;Cuts the sound.
+ set 2,c
+
+ jr PLY_LW_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_HardOnly
+
+
+
+PLY_LW_PSES_SoftwareOrSoftwareAndHardware:
+ ;Software only?
+ rra
+ IFDEF PLY_CFG_SFX_SoftAndHard ;CONFIG SPECIFIC
+ jr c,PLY_LW_PSES_SoftwareAndHardware
+ ENDIF ;PLY_CFG_SFX_SoftAndHard
+
+ ;Software.
+ ;-------------------------------------------
+ IFDEF PLY_CFG_SFX_SoftOnly ;CONFIG SPECIFIC
+ ;Volume.
+ call PLY_LW_PSES_ManageVolumeFromA_Filter4Bits
+
+ ;Noise?
+ rl b
+ IFDEF PLY_CFG_SFX_SoftOnly_Noise ;CONFIG SPECIFIC
+ call c,PLY_LW_PSES_ReadNoiseAndOpenNoiseChannel
+ ENDIF ;PLY_CFG_SFX_SoftOnly_Noise
+
+ ;Opens the "sound" channel.
+ res 2,c
+
+ ;Reads the software period.
+ call PLY_LW_PSES_ReadSoftwarePeriod
+
+ jr PLY_LW_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_SoftOnly
+
+
+ ;Software and Hardware.
+ ;-------------------------------------------
+ IFDEF PLY_LW_SE_HardwareSounds ;CONFIG SPECIFIC
+PLY_LW_PSES_SoftwareAndHardware:
+ ;Calls the shared code that manages everything.
+ call PLY_LW_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise
+
+ ;Reads the software period.
+ call PLY_LW_PSES_ReadSoftwarePeriod
+
+ ;Opens the sound.
+ res 2,c
+
+ jr PLY_LW_PSES_SavePointerAndExit
+ ENDIF ;PLY_LW_SE_HardwareSounds
+
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ ;Shared code used by the "hardware only" and "software and hardware" part.
+ ;Reads the Retrig flag, the Hardware Envelope, the possible noise, the hardware period,
+ ;and sets the volume to 16. The R7 sound channel is NOT modified.
+PLY_LW_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise:
+ ;Retrig?
+ rra
+ IFDEF PLY_LW_SE_Retrig ;CONFIG SPECIFIC
+ jr nc,PLY_LW_PSES_H_AfterRetrig
+ ld d,a
+ ld a,255
+ ld (PLY_LW_SetReg13Old + 1),a
+ ld a,d
+PLY_LW_PSES_H_AfterRetrig:
+ ENDIF ;PLY_LW_SE_Retrig
+
+ ;The hardware envelope can be set (8-15).
+ and %111
+ add a,8
+ ld (PLY_LW_SetReg13 + 1),a
+
+ ;Noise?
+ IFDEF PLY_LW_SE_HardwareNoise ;CONFIG SPECIFIC. B not needed after, we can put it in the condition too.
+ rl b
+ call c,PLY_LW_PSES_ReadNoiseAndOpenNoiseChannel
+ ENDIF ;PLY_LW_SE_HardwareNoise
+
+ ;Reads the hardware period.
+ call PLY_LW_PSES_ReadHardwarePeriod
+
+ ;Sets the volume to "hardware". It still may be decreased.
+ ld a,16
+ jp PLY_LW_PSES_ManageVolumeFromA_Hard
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+
+ IFDEF PLY_LW_SE_Noise
+;Reads the noise pointed by HL, increases HL, and opens the noise channel.
+PLY_LW_PSES_ReadNoiseAndOpenNoiseChannel:
+ ;Reads the noise.
+ ld a,(hl)
+ ld (PLY_LW_NoiseRegister),a
+ inc hl
+
+ ;Opens noise channel.
+ res 5,c
+ ret
+ ENDIF ;PLY_LW_SE_Noise
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+;Reads the hardware period from HL and sets the R11/R12 registers. HL is incremented of 2.
+PLY_LW_PSES_ReadHardwarePeriod:
+ ld a,(hl)
+ ld (PLY_LW_Reg11),a
+ inc hl
+ ld a,(hl)
+ ld (PLY_LW_Reg12),a
+ inc hl
+ ret
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+;Reads the software period from HL and sets the period registers thanks to IY. HL is incremented of 2.
+PLY_LW_PSES_ReadSoftwarePeriod:
+ ld a,(hl)
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodLSB),a
+ inc hl
+ ld a,(hl)
+ ld (iy + PLY_LW_Registers_OffsetSoftwarePeriodMSB),a
+ inc hl
+ ret
+
+ IFDEF PLY_LW_SE_VolumeSoft ;CONFIG SPECIFIC
+;Reads the volume in A, decreases it from the inverted volume of the channel, and sets the volume via IY.
+;IN: A = volume, from 0 to 15 (no hardware envelope).
+PLY_LW_PSES_ManageVolumeFromA_Filter4Bits:
+ and %1111
+ ENDIF ;PLY_LW_SE_VolumeSoft
+ IFDEF PLY_LW_SE_VolumeSoftOrHard ;CONFIG SPECIFIC
+;After the filtering. Useful for hardware sound (volume has been forced to 16).
+PLY_LW_PSES_ManageVolumeFromA_Hard:
+ ;Decreases the volume, checks the limit.
+ sub (ix + PLY_LW_SoundEffectData_OffsetInvertedVolume)
+ jr nc,PLY_LW_PSES_MVFA_NoOverflow
+ xor a
+PLY_LW_PSES_MVFA_NoOverflow:
+ ld (iy + PLY_LW_Registers_OffsetVolume),a
+ ret
+ ENDIF ;PLY_LW_SE_VolumeSoftOrHard
+
+
+;The data of the Channels MUST be consecutive.
+PLY_LW_Channel1_SoundEffectData:
+dkws
+ dw 0 ;Points to the sound effect for the track 1, or 0 if not playing.
+PLY_LW_Channel1_SoundEffectInvertedVolume:
+dkwe
+dkbs
+ db 0 ;Inverted volume.
+PLY_LW_Channel1_SoundEffectCurrentStep:
+ db 0 ;Current step (>=0).
+PLY_LW_Channel1_SoundEffectSpeed:
+ db 0 ;Speed (>=0).
+ ds 3,0 ;Padding.
+dkbe
+PLY_LW_Channel_SoundEffectDataSize: equ $ - PLY_LW_Channel1_SoundEffectData
+
+dkbs
+PLY_LW_Channel2_SoundEffectData:
+ ds PLY_LW_Channel_SoundEffectDataSize, 0
+PLY_LW_Channel3_SoundEffectData:
+ ds PLY_LW_Channel_SoundEffectDataSize, 0
+dkbe
+
+;Offset from the beginning of the data, to reach the inverted volume.
+PLY_LW_SoundEffectData_OffsetInvertedVolume: equ PLY_LW_Channel1_SoundEffectInvertedVolume - PLY_LW_Channel1_SoundEffectData
+PLY_LW_SoundEffectData_OffsetCurrentStep: equ PLY_LW_Channel1_SoundEffectCurrentStep - PLY_LW_Channel1_SoundEffectData
+PLY_LW_SoundEffectData_OffsetSpeed: equ PLY_LW_Channel1_SoundEffectSpeed - PLY_LW_Channel1_SoundEffectData
+
+ ;Checks that the pointers are consecutive.
+ assert (PLY_LW_Channel1_SoundEffectData + PLY_LW_Channel_SoundEffectDataSize) == PLY_LW_Channel2_SoundEffectData
+ assert (PLY_LW_Channel2_SoundEffectData + PLY_LW_Channel_SoundEffectDataSize) == PLY_LW_Channel3_SoundEffectData
+
diff --git a/lib/plw/player/player.asm b/lib/plw/player/player.asm
new file mode 100644
index 0000000..3c2d137
--- /dev/null
+++ b/lib/plw/player/player.asm
@@ -0,0 +1,43 @@
+org #0b06
+
+PLY_LW_HARDWARE_CPC = 1
+PLY_LW_MANAGE_SOUND_EFFECTS = 1
+PLY_LW_USE_HOOKS = 0
+PLY_LW_STOP_SOUNDS = 1
+
+PLY_CFG_ConfigurationIsPresent = 1
+PLY_CFG_SFX_ConfigurationIsPresent = 1
+PLY_CFG_SFX_SoftOnly = 1
+PLY_CFG_SFX_SoftOnly_Noise = 1
+PLY_CFG_NoSoftNoHard = 1
+PLY_CFG_SoftOnly = 1
+PLY_CFG_SoftOnly_Noise = 1
+PLY_CFG_SoftOnly_SoftwarePitch = 1
+
+ ; my hook table
+
+ jp PLY_LW_Init
+ jp PLY_LW_Play
+ jp PLY_LW_Stop
+ jp PLY_LW_InitSoundEffects
+ jp PLY_LW_PlaySoundEffect
+ jp PLW_LW_IsSoundEffectOn
+
+include "PlayerLightWeight.asm"
+
+; IN: L = channel
+; OUT: L = 0 if is not on
+PLW_LW_IsSoundEffectOn:
+ ld a,l
+ add a,a
+ add a,a
+ add a,a
+ ld c,a
+ ld b,0
+ ld hl,PLY_LW_Channel1_SoundEffectData
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ or (hl)
+ ld l,a
+ ret
diff --git a/lib/plw/plw_init.z80 b/lib/plw/plw_init.z80
new file mode 100644
index 0000000..aaa6ba5
--- /dev/null
+++ b/lib/plw/plw_init.z80
@@ -0,0 +1,14 @@
+.globl _PLW_Init
+.globl _plw_player
+
+PLY_LW_INIT = 0x0b06
+
+_PLW_Init::
+ ld ix,#2
+ add ix,sp
+
+ ld l,0 (ix)
+ ld h,1 (ix)
+ ld a,2 (ix)
+
+ jp PLY_LW_INIT
diff --git a/lib/plw/plw_init_sound_effects.z80 b/lib/plw/plw_init_sound_effects.z80
new file mode 100644
index 0000000..a82cfdc
--- /dev/null
+++ b/lib/plw/plw_init_sound_effects.z80
@@ -0,0 +1,9 @@
+.globl _PLW_InitSoundEffects
+.globl plw_current_efx
+
+PLY_LW_INIT_SOUND_EFFECTS = 0x0b06 + 0x9
+
+_PLW_InitSoundEffects::
+ xor a
+ ld (plw_current_efx), a
+ jp PLY_LW_INIT_SOUND_EFFECTS
diff --git a/lib/plw/plw_is_sound_effect_on.z80 b/lib/plw/plw_is_sound_effect_on.z80
new file mode 100644
index 0000000..5953cce
--- /dev/null
+++ b/lib/plw/plw_is_sound_effect_on.z80
@@ -0,0 +1,7 @@
+.globl _PLW_IsSoundEffectOn
+
+PLY_LW_IS_SOUND_EFFECT_ON = 0x0b06 + 0xf
+
+_PLW_IsSoundEffectOn::
+ jp PLY_LW_IS_SOUND_EFFECT_ON
+
diff --git a/lib/plw/plw_play.z80 b/lib/plw/plw_play.z80
new file mode 100644
index 0000000..a1f0ac8
--- /dev/null
+++ b/lib/plw/plw_play.z80
@@ -0,0 +1,6 @@
+.globl _PLW_Play
+
+PLY_LW_PLAY = 0x0b06 + 0x3
+
+_PLW_Play::
+ jp PLY_LW_PLAY
diff --git a/lib/plw/plw_play_sound_effect.z80 b/lib/plw/plw_play_sound_effect.z80
new file mode 100644
index 0000000..a20fcfe
--- /dev/null
+++ b/lib/plw/plw_play_sound_effect.z80
@@ -0,0 +1,48 @@
+.globl _PLW_PlaySoundEffect
+.globl _PLW_IsSoundEffectOn
+.globl _PLW_PlaySoundEffectP
+.globl plw_current_efx
+
+PLY_LW_PLAY_SOUND_EFFECT = 0x0b06 + 0xc
+
+_PLW_PlaySoundEffect::
+ ld hl,#2
+ add hl,sp
+
+ ld a, (hl)
+ inc hl
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+
+ jp PLY_LW_PLAY_SOUND_EFFECT
+
+_PLW_PlaySoundEffectP::
+ ; check if current is over
+ ; de is not used
+ ld e, l
+ ; fixed channel 2
+ ld l, #2
+ call _PLW_IsSoundEffectOn
+ ld a, l
+ or a
+ jr z, play_efx
+
+ ld a, (plw_current_efx)
+ cp e
+ ret nc
+
+play_efx:
+ ; all good, play the effect
+ ld a, e
+ ld (plw_current_efx), a
+
+ ; fixed channel 2, full volume
+ ld bc, #0x0002
+
+ jp PLY_LW_PLAY_SOUND_EFFECT
+
+.area _DATA
+
+plw_current_efx: .ds 1
+
diff --git a/lib/plw/plw_player.asm b/lib/plw/plw_player.asm
new file mode 100644
index 0000000..f47a9bf
--- /dev/null
+++ b/lib/plw/plw_player.asm
@@ -0,0 +1,1193 @@
+;--------------------------------------------------------
+; File Created by SDCC : free open source ANSI-C Compiler
+; Version 4.0.0 #11528 (Linux)
+;--------------------------------------------------------
+ .module plw_player
+ .optsdcc -mz80
+
+;--------------------------------------------------------
+; Public variables in this module
+;--------------------------------------------------------
+ .globl _plw_player
+;--------------------------------------------------------
+; special function registers
+;--------------------------------------------------------
+;--------------------------------------------------------
+; ram data
+;--------------------------------------------------------
+ .area _DATA
+;--------------------------------------------------------
+; ram data
+;--------------------------------------------------------
+ .area _INITIALIZED
+;--------------------------------------------------------
+; absolute external ram data
+;--------------------------------------------------------
+ .area _DABS (ABS)
+;--------------------------------------------------------
+; global & static initialisations
+;--------------------------------------------------------
+ .area _HOME
+ .area _GSINIT
+ .area _GSFINAL
+ .area _GSINIT
+;--------------------------------------------------------
+; Home
+;--------------------------------------------------------
+ .area _HOME
+ .area _HOME
+;--------------------------------------------------------
+; code
+;--------------------------------------------------------
+ .area _CODE
+ .area _CODE
+_plw_player:
+ .db #0xc3 ; 195
+ .db #0xf6 ; 246
+ .db #0x0b ; 11
+ .db #0xc3 ; 195
+ .db #0x55 ; 85 'U'
+ .db #0x0c ; 12
+ .db #0xc3 ; 195
+ .db #0x3f ; 63
+ .db #0x0c ; 12
+ .db #0xc3 ; 195
+ .db #0x18 ; 24
+ .db #0x0b ; 11
+ .db #0xc3 ; 195
+ .db #0x1c ; 28
+ .db #0x0b ; 11
+ .db #0xc3 ; 195
+ .db #0x71 ; 113 'q'
+ .db #0x0f ; 15
+ .db #0x22 ; 34
+ .db #0x1e ; 30
+ .db #0x0b ; 11
+ .db #0xc9 ; 201
+ .db #0x3d ; 61
+ .db #0x21 ; 33
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x5f ; 95
+ .db #0x16 ; 22
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x19 ; 25
+ .db #0x5e ; 94
+ .db #0x23 ; 35
+ .db #0x56 ; 86 'V'
+ .db #0x1a ; 26
+ .db #0x13 ; 19
+ .db #0xf5 ; 245
+ .db #0x78 ; 120 'x'
+ .db #0x21 ; 33
+ .db #0xde ; 222
+ .db #0x0b ; 11
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0xcb ; 203
+ .db #0x21 ; 33
+ .db #0xcb ; 203
+ .db #0x21 ; 33
+ .db #0xcb ; 203
+ .db #0x21 ; 33
+ .db #0x09 ; 9
+ .db #0x73 ; 115 's'
+ .db #0x23 ; 35
+ .db #0x72 ; 114 'r'
+ .db #0x23 ; 35
+ .db #0x77 ; 119 'w'
+ .db #0x23 ; 35
+ .db #0x36 ; 54 '6'
+ .db #0x00 ; 0
+ .db #0x23 ; 35
+ .db #0xf1 ; 241
+ .db #0x77 ; 119 'w'
+ .db #0xc9 ; 201
+ .db #0x87 ; 135
+ .db #0x87 ; 135
+ .db #0x87 ; 135
+ .db #0x5f ; 95
+ .db #0x16 ; 22
+ .db #0x00 ; 0
+ .db #0x21 ; 33
+ .db #0xde ; 222
+ .db #0x0b ; 11
+ .db #0x19 ; 25
+ .db #0x72 ; 114 'r'
+ .db #0x23 ; 35
+ .db #0x72 ; 114 'r'
+ .db #0xc9 ; 201
+ .db #0x17 ; 23
+ .db #0x17 ; 23
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0xde ; 222
+ .db #0x0b ; 11
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x45 ; 69 'E'
+ .db #0x0e ; 14
+ .db #0x4f ; 79 'O'
+ .db #0xcd ; 205
+ .db #0x7f ; 127
+ .db #0x0b ; 11
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0xe6 ; 230
+ .db #0x0b ; 11
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x51 ; 81 'Q'
+ .db #0x0e ; 14
+ .db #0xcb ; 203
+ .db #0x39 ; 57 '9'
+ .db #0xcd ; 205
+ .db #0x7f ; 127
+ .db #0x0b ; 11
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0xee ; 238
+ .db #0x0b ; 11
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x5d ; 93
+ .db #0x0e ; 14
+ .db #0xcb ; 203
+ .db #0x19 ; 25
+ .db #0xcd ; 205
+ .db #0x7f ; 127
+ .db #0x0b ; 11
+ .db #0x79 ; 121 'y'
+ .db #0x32 ; 50 '2'
+ .db #0x6e ; 110 'n'
+ .db #0x0e ; 14
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x6e ; 110 'n'
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x66 ; 102 'f'
+ .db #0x01 ; 1
+ .db #0x7d ; 125
+ .db #0xb4 ; 180
+ .db #0xc8 ; 200
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x47 ; 71 'G'
+ .db #0x1f ; 31
+ .db #0x38 ; 56 '8'
+ .db #0x21 ; 33
+ .db #0x1f ; 31
+ .db #0x1f ; 31
+ .db #0xaf ; 175
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x01 ; 1
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x7e ; 126
+ .db #0x03 ; 3
+ .db #0xdd ; 221
+ .db #0xbe ; 190
+ .db #0x04 ; 4
+ .db #0x38 ; 56 '8'
+ .db #0x0b ; 11
+ .db #0xdd ; 221
+ .db #0x36 ; 54 '6'
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x75 ; 117 'u'
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x74 ; 116 't'
+ .db #0x01 ; 1
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x34 ; 52 '4'
+ .db #0x03 ; 3
+ .db #0xc9 ; 201
+ .db #0x1f ; 31
+ .db #0xcd ; 205
+ .db #0xd2 ; 210
+ .db #0x0b ; 11
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0xdc ; 220
+ .db #0xbf ; 191
+ .db #0x0b ; 11
+ .db #0xcb ; 203
+ .db #0x91 ; 145
+ .db #0xcd ; 205
+ .db #0xc7 ; 199
+ .db #0x0b ; 11
+ .db #0x18 ; 24
+ .db #0xd9 ; 217
+ .db #0x7e ; 126
+ .db #0x32 ; 50 '2'
+ .db #0x6a ; 106 'j'
+ .db #0x0e ; 14
+ .db #0x23 ; 35
+ .db #0xcb ; 203
+ .db #0xa9 ; 169
+ .db #0xc9 ; 201
+ .db #0x7e ; 126
+ .db #0xfd ; 253
+ .db #0x77 ; 119 'w'
+ .db #0x05 ; 5
+ .db #0x23 ; 35
+ .db #0x7e ; 126
+ .db #0xfd ; 253
+ .db #0x77 ; 119 'w'
+ .db #0x09 ; 9
+ .db #0x23 ; 35
+ .db #0xc9 ; 201
+ .db #0xe6 ; 230
+ .db #0x0f ; 15
+ .db #0xdd ; 221
+ .db #0x96 ; 150
+ .db #0x02 ; 2
+ .db #0x30 ; 48 '0'
+ .db #0x01 ; 1
+ .db #0xaf ; 175
+ .db #0xfd ; 253
+ .db #0x77 ; 119 'w'
+ .db #0x01 ; 1
+ .db #0xc9 ; 201
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x11 ; 17
+ .db #0x05 ; 5
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x11 ; 17
+ .db #0xb1 ; 177
+ .db #0x0c ; 12
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0x11 ; 17
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x5f ; 95
+ .db #0x16 ; 22
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x19 ; 25
+ .db #0x5e ; 94
+ .db #0x23 ; 35
+ .db #0x56 ; 86 'V'
+ .db #0x1a ; 26
+ .db #0x13 ; 19
+ .db #0xed ; 237
+ .db #0x53 ; 83 'S'
+ .db #0x6d ; 109 'm'
+ .db #0x0c ; 12
+ .db #0x32 ; 50 '2'
+ .db #0x5d ; 93
+ .db #0x0c ; 12
+ .db #0x3d ; 61
+ .db #0x32 ; 50 '2'
+ .db #0x5a ; 90 'Z'
+ .db #0x0c ; 12
+ .db #0xaf ; 175
+ .db #0x32 ; 50 '2'
+ .db #0x62 ; 98 'b'
+ .db #0x0c ; 12
+ .db #0x21 ; 33
+ .db #0x24 ; 36
+ .db #0x0e ; 14
+ .db #0x11 ; 17
+ .db #0x25 ; 37
+ .db #0x0e ; 14
+ .db #0x01 ; 1
+ .db #0x0a ; 10
+ .db #0x00 ; 0
+ .db #0x36 ; 54 '6'
+ .db #0x00 ; 0
+ .db #0xed ; 237
+ .db #0xb0 ; 176
+ .db #0x2a ; 42
+ .db #0xb1 ; 177
+ .db #0x0c ; 12
+ .db #0x5e ; 94
+ .db #0x23 ; 35
+ .db #0x56 ; 86 'V'
+ .db #0x13 ; 19
+ .db #0xed ; 237
+ .db #0x53 ; 83 'S'
+ .db #0x2b ; 43
+ .db #0x0e ; 14
+ .db #0xed ; 237
+ .db #0x53 ; 83 'S'
+ .db #0x36 ; 54 '6'
+ .db #0x0e ; 14
+ .db #0xed ; 237
+ .db #0x53 ; 83 'S'
+ .db #0x41 ; 65 'A'
+ .db #0x0e ; 14
+ .db #0xc9 ; 201
+ .db #0xed ; 237
+ .db #0x73 ; 115 's'
+ .db #0x1a ; 26
+ .db #0x0d ; 13
+ .db #0xaf ; 175
+ .db #0x32 ; 50 '2'
+ .db #0x46 ; 70 'F'
+ .db #0x0e ; 14
+ .db #0x32 ; 50 '2'
+ .db #0x52 ; 82 'R'
+ .db #0x0e ; 14
+ .db #0x32 ; 50 '2'
+ .db #0x5e ; 94
+ .db #0x0e ; 14
+ .db #0x3e ; 62
+ .db #0x3f ; 63
+ .db #0x32 ; 50 '2'
+ .db #0x6e ; 110 'n'
+ .db #0x0e ; 14
+ .db #0xc3 ; 195
+ .db #0xfc ; 252
+ .db #0x0c ; 12
+ .db #0xed ; 237
+ .db #0x73 ; 115 's'
+ .db #0x1a ; 26
+ .db #0x0d ; 13
+ .db #0x3e ; 62
+ .db #0x00 ; 0
+ .db #0x3c ; 60
+ .db #0xfe ; 254
+ .db #0x01 ; 1
+ .db #0xc2 ; 194
+ .db #0xca ; 202
+ .db #0x0c ; 12
+ .db #0x3e ; 62
+ .db #0x00 ; 0
+ .db #0xd6 ; 214
+ .db #0x01 ; 1
+ .db #0x38 ; 56 '8'
+ .db #0x05 ; 5
+ .db #0x32 ; 50 '2'
+ .db #0x62 ; 98 'b'
+ .db #0x0c ; 12
+ .db #0x18 ; 24
+ .db #0x44 ; 68 'D'
+ .db #0x21 ; 33
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0xaf ; 175
+ .db #0x32 ; 50 '2'
+ .db #0x24 ; 36
+ .db #0x0e ; 14
+ .db #0x32 ; 50 '2'
+ .db #0x2f ; 47
+ .db #0x0e ; 14
+ .db #0x32 ; 50 '2'
+ .db #0x3a ; 58
+ .db #0x0e ; 14
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x1f ; 31
+ .db #0x38 ; 56 '8'
+ .db #0x06 ; 6
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x66 ; 102 'f'
+ .db #0x6f ; 111 'o'
+ .db #0x18 ; 24
+ .db #0xeb ; 235
+ .db #0x1f ; 31
+ .db #0x47 ; 71 'G'
+ .db #0xcb ; 203
+ .db #0x18 ; 24
+ .db #0x30 ; 48 '0'
+ .db #0x07 ; 7
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x32 ; 50 '2'
+ .db #0x92 ; 146
+ .db #0x0c ; 12
+ .db #0x18 ; 24
+ .db #0x02 ; 2
+ .db #0x3e ; 62
+ .db #0x00 ; 0
+ .db #0x32 ; 50 '2'
+ .db #0x62 ; 98 'b'
+ .db #0x0c ; 12
+ .db #0xcb ; 203
+ .db #0x18 ; 24
+ .db #0x11 ; 17
+ .db #0x29 ; 41
+ .db #0x0e ; 14
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0x11 ; 17
+ .db #0x34 ; 52 '4'
+ .db #0x0e ; 14
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0x11 ; 17
+ .db #0x3f ; 63
+ .db #0x0e ; 14
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0xed ; 237
+ .db #0xa0 ; 160
+ .db #0x22 ; 34
+ .db #0x6d ; 109 'm'
+ .db #0x0c ; 12
+ .db #0x11 ; 17
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0xd9 ; 217
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x24 ; 36
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x1d ; 29
+ .db #0x0d ; 13
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x2f ; 47
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x1d ; 29
+ .db #0x0d ; 13
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x3a ; 58
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x1d ; 29
+ .db #0x0d ; 13
+ .db #0xaf ; 175
+ .db #0x32 ; 50 '2'
+ .db #0x5a ; 90 'Z'
+ .db #0x0c ; 12
+ .db #0x11 ; 17
+ .db #0x71 ; 113 'q'
+ .db #0x0e ; 14
+ .db #0xd9 ; 217
+ .db #0x0e ; 14
+ .db #0xe0 ; 224
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x24 ; 36
+ .db #0x0e ; 14
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x45 ; 69 'E'
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x93 ; 147
+ .db #0x0d ; 13
+ .db #0xcb ; 203
+ .db #0x39 ; 57 '9'
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x2f ; 47
+ .db #0x0e ; 14
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x51 ; 81 'Q'
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x93 ; 147
+ .db #0x0d ; 13
+ .db #0xcb ; 203
+ .db #0x19 ; 25
+ .db #0xdd ; 221
+ .db #0x21 ; 33
+ .db #0x3a ; 58
+ .db #0x0e ; 14
+ .db #0xfd ; 253
+ .db #0x21 ; 33
+ .db #0x5d ; 93
+ .db #0x0e ; 14
+ .db #0xcd ; 205
+ .db #0x93 ; 147
+ .db #0x0d ; 13
+ .db #0x79 ; 121 'y'
+ .db #0xcd ; 205
+ .db #0x52 ; 82 'R'
+ .db #0x0b ; 11
+ .db #0x31 ; 49 '1'
+ .db #0x45 ; 69 'E'
+ .db #0x0e ; 14
+ .db #0x01 ; 1
+ .db #0x80 ; 128
+ .db #0xf6 ; 246
+ .db #0x3e ; 62
+ .db #0xc0 ; 192
+ .db #0x11 ; 17
+ .db #0xf6 ; 246
+ .db #0xf4 ; 244
+ .db #0xed ; 237
+ .db #0x79 ; 121 'y'
+ .db #0xe1 ; 225
+ .db #0x42 ; 66 'B'
+ .db #0xed ; 237
+ .db #0x69 ; 105 'i'
+ .db #0x43 ; 67 'C'
+ .db #0xed ; 237
+ .db #0x71 ; 113 'q'
+ .db #0x42 ; 66 'B'
+ .db #0xed ; 237
+ .db #0x61 ; 97 'a'
+ .db #0x43 ; 67 'C'
+ .db #0xed ; 237
+ .db #0x49 ; 73 'I'
+ .db #0xed ; 237
+ .db #0x79 ; 121 'y'
+ .db #0xc9 ; 201
+ .db #0x31 ; 49 '1'
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x7e ; 126
+ .db #0x00 ; 0
+ .db #0xd6 ; 214
+ .db #0x01 ; 1
+ .db #0x38 ; 56 '8'
+ .db #0x04 ; 4
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x00 ; 0
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x6e ; 110 'n'
+ .db #0x05 ; 5
+ .db #0xdd ; 221
+ .db #0x66 ; 102 'f'
+ .db #0x06 ; 6
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x47 ; 71 'G'
+ .db #0xe6 ; 230
+ .db #0x3f ; 63
+ .db #0xd6 ; 214
+ .db #0x3c ; 60
+ .db #0x38 ; 56 '8'
+ .db #0x0a ; 10
+ .db #0x3d ; 61
+ .db #0x28 ; 40
+ .db #0x42 ; 66 'B'
+ .db #0x3d ; 61
+ .db #0x28 ; 40
+ .db #0x46 ; 70 'F'
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x18 ; 24
+ .db #0x02 ; 2
+ .db #0xc6 ; 198
+ .db #0x54 ; 84 'T'
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x01 ; 1
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0x38 ; 56 '8'
+ .db #0x0e ; 14
+ .db #0xdd ; 221
+ .db #0x7e ; 126
+ .db #0x09 ; 9
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x07 ; 7
+ .db #0xdd ; 221
+ .db #0x7e ; 126
+ .db #0x0a ; 10
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x08 ; 8
+ .db #0x18 ; 24
+ .db #0x1c ; 28
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xd9 ; 217
+ .db #0x6f ; 111 'o'
+ .db #0x26 ; 38
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x4e ; 78 'N'
+ .db #0x23 ; 35
+ .db #0x46 ; 70 'F'
+ .db #0x0a ; 10
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x03 ; 3
+ .db #0x03 ; 3
+ .db #0xdd ; 221
+ .db #0x71 ; 113 'q'
+ .db #0x07 ; 7
+ .db #0xdd ; 221
+ .db #0x70 ; 112 'p'
+ .db #0x08 ; 8
+ .db #0xdd ; 221
+ .db #0x71 ; 113 'q'
+ .db #0x09 ; 9
+ .db #0xdd ; 221
+ .db #0x70 ; 112 'p'
+ .db #0x0a ; 10
+ .db #0xd9 ; 217
+ .db #0xdd ; 221
+ .db #0x36 ; 54 '6'
+ .db #0x02 ; 2
+ .db #0x00 ; 0
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0x30 ; 48 '0'
+ .db #0x10 ; 16
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x00 ; 0
+ .db #0x18 ; 24
+ .db #0x09 ; 9
+ .db #0x78 ; 120 'x'
+ .db #0x17 ; 23
+ .db #0x17 ; 23
+ .db #0x17 ; 23
+ .db #0xe6 ; 230
+ .db #0x03 ; 3
+ .db #0xdd ; 221
+ .db #0x77 ; 119 'w'
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x75 ; 117 'u'
+ .db #0x05 ; 5
+ .db #0xdd ; 221
+ .db #0x74 ; 116 't'
+ .db #0x06 ; 6
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x6e ; 110 'n'
+ .db #0x07 ; 7
+ .db #0xdd ; 221
+ .db #0x66 ; 102 'f'
+ .db #0x08 ; 8
+ .db #0x7e ; 126
+ .db #0x47 ; 71 'G'
+ .db #0x23 ; 35
+ .db #0x1f ; 31
+ .db #0x38 ; 56 '8'
+ .db #0x1c ; 28
+ .db #0x1f ; 31
+ .db #0x1f ; 31
+ .db #0x30 ; 48 '0'
+ .db #0x0c ; 12
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x66 ; 102 'f'
+ .db #0x6f ; 111 'o'
+ .db #0xdd ; 221
+ .db #0x75 ; 117 'u'
+ .db #0x07 ; 7
+ .db #0xdd ; 221
+ .db #0x74 ; 116 't'
+ .db #0x08 ; 8
+ .db #0x18 ; 24
+ .db #0xea ; 234
+ .db #0xcb ; 203
+ .db #0xd1 ; 209
+ .db #0xcd ; 205
+ .db #0xf8 ; 248
+ .db #0x0d ; 13
+ .db #0xfd ; 253
+ .db #0x77 ; 119 'w'
+ .db #0x01 ; 1
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0x18 ; 24
+ .db #0x26 ; 38
+ .db #0x1f ; 31
+ .db #0xcd ; 205
+ .db #0xf8 ; 248
+ .db #0x0d ; 13
+ .db #0xfd ; 253
+ .db #0x77 ; 119 'w'
+ .db #0x01 ; 1
+ .db #0x16 ; 22
+ .db #0x00 ; 0
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0x30 ; 48 '0'
+ .db #0x08 ; 8
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xcb ; 203
+ .db #0x2f ; 47
+ .db #0x57 ; 87 'W'
+ .db #0xdc ; 220
+ .db #0x00 ; 0
+ .db #0x0e ; 14
+ .db #0x7a ; 122 'z'
+ .db #0xcd ; 205
+ .db #0x08 ; 8
+ .db #0x0e ; 14
+ .db #0xcb ; 203
+ .db #0x10 ; 16
+ .db #0xdc ; 220
+ .db #0x18 ; 24
+ .db #0x0e ; 14
+ .db #0xd9 ; 217
+ .db #0xfd ; 253
+ .db #0x75 ; 117 'u'
+ .db #0x05 ; 5
+ .db #0xfd ; 253
+ .db #0x74 ; 116 't'
+ .db #0x09 ; 9
+ .db #0xd9 ; 217
+ .db #0xdd ; 221
+ .db #0x7e ; 126
+ .db #0x02 ; 2
+ .db #0xdd ; 221
+ .db #0xbe ; 190
+ .db #0x03 ; 3
+ .db #0x28 ; 40
+ .db #0x04 ; 4
+ .db #0xdd ; 221
+ .db #0x34 ; 52 '4'
+ .db #0x02 ; 2
+ .db #0xc9 ; 201
+ .db #0xdd ; 221
+ .db #0x75 ; 117 'u'
+ .db #0x07 ; 7
+ .db #0xdd ; 221
+ .db #0x74 ; 116 't'
+ .db #0x08 ; 8
+ .db #0xdd ; 221
+ .db #0x36 ; 54 '6'
+ .db #0x02 ; 2
+ .db #0x00 ; 0
+ .db #0xc9 ; 201
+ .db #0xe6 ; 230
+ .db #0x0f ; 15
+ .db #0xdd ; 221
+ .db #0x96 ; 150
+ .db #0x04 ; 4
+ .db #0xd0 ; 208
+ .db #0xaf ; 175
+ .db #0xc9 ; 201
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x32 ; 50 '2'
+ .db #0x6a ; 106 'j'
+ .db #0x0e ; 14
+ .db #0xcb ; 203
+ .db #0xa9 ; 169
+ .db #0xc9 ; 201
+ .db #0xd9 ; 217
+ .db #0x26 ; 38
+ .db #0x00 ; 0
+ .db #0xdd ; 221
+ .db #0x86 ; 134
+ .db #0x01 ; 1
+ .db #0x6f ; 111 'o'
+ .db #0xcb ; 203
+ .db #0x25 ; 37
+ .db #0x19 ; 25
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0x66 ; 102 'f'
+ .db #0x6f ; 111 'o'
+ .db #0xd9 ; 217
+ .db #0xc9 ; 201
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xd9 ; 217
+ .db #0x4f ; 79 'O'
+ .db #0xd9 ; 217
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xd9 ; 217
+ .db #0x47 ; 71 'G'
+ .db #0x09 ; 9
+ .db #0xd9 ; 217
+ .db #0xc9 ; 201
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x08 ; 8
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x00 ; 0
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x01 ; 1
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x09 ; 9
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x02 ; 2
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x0a ; 10
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x05 ; 5
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x0d ; 13
+ .db #0x07 ; 7
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x0d ; 13
+ .db #0xee ; 238
+ .db #0x0e ; 14
+ .db #0x18 ; 24
+ .db #0x0e ; 14
+ .db #0x4d ; 77 'M'
+ .db #0x0d ; 13
+ .db #0x8e ; 142
+ .db #0x0c ; 12
+ .db #0xda ; 218
+ .db #0x0b ; 11
+ .db #0x2f ; 47
+ .db #0x0b ; 11
+ .db #0x8f ; 143
+ .db #0x0a ; 10
+ .db #0xf7 ; 247
+ .db #0x09 ; 9
+ .db #0x68 ; 104 'h'
+ .db #0x09 ; 9
+ .db #0xe1 ; 225
+ .db #0x08 ; 8
+ .db #0x61 ; 97 'a'
+ .db #0x08 ; 8
+ .db #0xe9 ; 233
+ .db #0x07 ; 7
+ .db #0x77 ; 119 'w'
+ .db #0x07 ; 7
+ .db #0x0c ; 12
+ .db #0x07 ; 7
+ .db #0xa7 ; 167
+ .db #0x06 ; 6
+ .db #0x47 ; 71 'G'
+ .db #0x06 ; 6
+ .db #0xed ; 237
+ .db #0x05 ; 5
+ .db #0x98 ; 152
+ .db #0x05 ; 5
+ .db #0x47 ; 71 'G'
+ .db #0x05 ; 5
+ .db #0xfc ; 252
+ .db #0x04 ; 4
+ .db #0xb4 ; 180
+ .db #0x04 ; 4
+ .db #0x70 ; 112 'p'
+ .db #0x04 ; 4
+ .db #0x31 ; 49 '1'
+ .db #0x04 ; 4
+ .db #0xf4 ; 244
+ .db #0x03 ; 3
+ .db #0xbc ; 188
+ .db #0x03 ; 3
+ .db #0x86 ; 134
+ .db #0x03 ; 3
+ .db #0x53 ; 83 'S'
+ .db #0x03 ; 3
+ .db #0x24 ; 36
+ .db #0x03 ; 3
+ .db #0xf6 ; 246
+ .db #0x02 ; 2
+ .db #0xcc ; 204
+ .db #0x02 ; 2
+ .db #0xa4 ; 164
+ .db #0x02 ; 2
+ .db #0x7e ; 126
+ .db #0x02 ; 2
+ .db #0x5a ; 90 'Z'
+ .db #0x02 ; 2
+ .db #0x38 ; 56 '8'
+ .db #0x02 ; 2
+ .db #0x18 ; 24
+ .db #0x02 ; 2
+ .db #0xfa ; 250
+ .db #0x01 ; 1
+ .db #0xde ; 222
+ .db #0x01 ; 1
+ .db #0xc3 ; 195
+ .db #0x01 ; 1
+ .db #0xaa ; 170
+ .db #0x01 ; 1
+ .db #0x92 ; 146
+ .db #0x01 ; 1
+ .db #0x7b ; 123
+ .db #0x01 ; 1
+ .db #0x66 ; 102 'f'
+ .db #0x01 ; 1
+ .db #0x52 ; 82 'R'
+ .db #0x01 ; 1
+ .db #0x3f ; 63
+ .db #0x01 ; 1
+ .db #0x2d ; 45
+ .db #0x01 ; 1
+ .db #0x1c ; 28
+ .db #0x01 ; 1
+ .db #0x0c ; 12
+ .db #0x01 ; 1
+ .db #0xfd ; 253
+ .db #0x00 ; 0
+ .db #0xef ; 239
+ .db #0x00 ; 0
+ .db #0xe1 ; 225
+ .db #0x00 ; 0
+ .db #0xd5 ; 213
+ .db #0x00 ; 0
+ .db #0xc9 ; 201
+ .db #0x00 ; 0
+ .db #0xbe ; 190
+ .db #0x00 ; 0
+ .db #0xb3 ; 179
+ .db #0x00 ; 0
+ .db #0xa9 ; 169
+ .db #0x00 ; 0
+ .db #0x9f ; 159
+ .db #0x00 ; 0
+ .db #0x96 ; 150
+ .db #0x00 ; 0
+ .db #0x8e ; 142
+ .db #0x00 ; 0
+ .db #0x86 ; 134
+ .db #0x00 ; 0
+ .db #0x7f ; 127
+ .db #0x00 ; 0
+ .db #0x77 ; 119 'w'
+ .db #0x00 ; 0
+ .db #0x71 ; 113 'q'
+ .db #0x00 ; 0
+ .db #0x6a ; 106 'j'
+ .db #0x00 ; 0
+ .db #0x64 ; 100 'd'
+ .db #0x00 ; 0
+ .db #0x5f ; 95
+ .db #0x00 ; 0
+ .db #0x59 ; 89 'Y'
+ .db #0x00 ; 0
+ .db #0x54 ; 84 'T'
+ .db #0x00 ; 0
+ .db #0x50 ; 80 'P'
+ .db #0x00 ; 0
+ .db #0x4b ; 75 'K'
+ .db #0x00 ; 0
+ .db #0x47 ; 71 'G'
+ .db #0x00 ; 0
+ .db #0x43 ; 67 'C'
+ .db #0x00 ; 0
+ .db #0x3f ; 63
+ .db #0x00 ; 0
+ .db #0x3c ; 60
+ .db #0x00 ; 0
+ .db #0x38 ; 56 '8'
+ .db #0x00 ; 0
+ .db #0x35 ; 53 '5'
+ .db #0x00 ; 0
+ .db #0x32 ; 50 '2'
+ .db #0x00 ; 0
+ .db #0x2f ; 47
+ .db #0x00 ; 0
+ .db #0x2d ; 45
+ .db #0x00 ; 0
+ .db #0x2a ; 42
+ .db #0x00 ; 0
+ .db #0x28 ; 40
+ .db #0x00 ; 0
+ .db #0x26 ; 38
+ .db #0x00 ; 0
+ .db #0x24 ; 36
+ .db #0x00 ; 0
+ .db #0x22 ; 34
+ .db #0x00 ; 0
+ .db #0x20 ; 32
+ .db #0x00 ; 0
+ .db #0x1e ; 30
+ .db #0x00 ; 0
+ .db #0x1c ; 28
+ .db #0x00 ; 0
+ .db #0x1b ; 27
+ .db #0x00 ; 0
+ .db #0x19 ; 25
+ .db #0x00 ; 0
+ .db #0x18 ; 24
+ .db #0x00 ; 0
+ .db #0x16 ; 22
+ .db #0x00 ; 0
+ .db #0x15 ; 21
+ .db #0x00 ; 0
+ .db #0x14 ; 20
+ .db #0x00 ; 0
+ .db #0x13 ; 19
+ .db #0x00 ; 0
+ .db #0x12 ; 18
+ .db #0x00 ; 0
+ .db #0x11 ; 17
+ .db #0x00 ; 0
+ .db #0x10 ; 16
+ .db #0x00 ; 0
+ .db #0x0f ; 15
+ .db #0x00 ; 0
+ .db #0x0e ; 14
+ .db #0x00 ; 0
+ .db #0x0d ; 13
+ .db #0x00 ; 0
+ .db #0x0d ; 13
+ .db #0x00 ; 0
+ .db #0x0c ; 12
+ .db #0x00 ; 0
+ .db #0x0b ; 11
+ .db #0x00 ; 0
+ .db #0x0b ; 11
+ .db #0x00 ; 0
+ .db #0x0a ; 10
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x00 ; 0
+ .db #0x09 ; 9
+ .db #0x00 ; 0
+ .db #0x08 ; 8
+ .db #0x00 ; 0
+ .db #0x08 ; 8
+ .db #0x00 ; 0
+ .db #0x07 ; 7
+ .db #0x00 ; 0
+ .db #0x07 ; 7
+ .db #0x00 ; 0
+ .db #0x07 ; 7
+ .db #0x00 ; 0
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0x05 ; 5
+ .db #0x00 ; 0
+ .db #0x05 ; 5
+ .db #0x00 ; 0
+ .db #0x05 ; 5
+ .db #0x00 ; 0
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x04 ; 4
+ .db #0x00 ; 0
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x03 ; 3
+ .db #0x00 ; 0
+ .db #0x02 ; 2
+ .db #0x00 ; 0
+ .db #0x7d ; 125
+ .db #0x87 ; 135
+ .db #0x87 ; 135
+ .db #0x87 ; 135
+ .db #0x4f ; 79 'O'
+ .db #0x06 ; 6
+ .db #0x00 ; 0
+ .db #0x21 ; 33
+ .db #0xde ; 222
+ .db #0x0b ; 11
+ .db #0x09 ; 9
+ .db #0x7e ; 126
+ .db #0x23 ; 35
+ .db #0xb6 ; 182
+ .db #0x6f ; 111 'o'
+ .db #0xc9 ; 201
+ .area _INITIALIZER
+ .area _CABS (ABS)
diff --git a/lib/plw/plw_player.c b/lib/plw/plw_player.c
new file mode 100644
index 0000000..4ab2598
--- /dev/null
+++ b/lib/plw/plw_player.c
@@ -0,0 +1,3 @@
+#define LOCAL
+#include "player/plw_player.h"
+
diff --git a/lib/plw/plw_stop.z80 b/lib/plw/plw_stop.z80
new file mode 100644
index 0000000..6bb3daf
--- /dev/null
+++ b/lib/plw/plw_stop.z80
@@ -0,0 +1,6 @@
+.globl _PLW_Stop
+
+PLY_LW_STOP = 0x0b06 + 0x6
+
+_PLW_Stop::
+ jp PLY_LW_STOP