├── examples ├── wladx │ ├── linkfile.txt │ ├── README.md │ ├── build.bat │ ├── build.sh │ └── main.asm ├── visualiser │ ├── assets │ │ ├── palette.bin │ │ ├── sprite_palette.bin │ │ ├── banjo_tiles.bin │ │ ├── labels_tiles.bin │ │ ├── keyboard_tiles.bin │ │ └── keys_lit_tiles.bin │ ├── banjo.png │ ├── labels.png │ ├── keyboard.png │ ├── keys_lit.png │ ├── bank1.c │ ├── README.md │ ├── bank2.h │ ├── song_table.h │ ├── key_x_tile.h │ ├── build.sh │ ├── build.bat │ └── main.c ├── wladx_no_queue │ ├── linkfile.txt │ ├── README.md │ ├── build.bat │ ├── build.sh │ └── main.asm ├── cmajor.fur ├── cmajor_sn.fur ├── sfx_test.fur ├── sfx_test_sn.fur ├── cmajor_dual_sn.fur ├── devkitsms │ ├── bank1.c │ ├── README.md │ ├── song_table.h │ ├── build.bat │ ├── build.sh │ └── main.c └── devkitsms_no_queue │ ├── bank1.c │ ├── README.md │ ├── song_table.h │ ├── main.c │ └── build.bat ├── music_driver_sdas ├── mode │ ├── sn.asm │ ├── dual_sn.asm │ ├── queues_off.asm │ ├── fm.asm │ ├── sn_fm.asm │ ├── fm_drums.asm │ └── sn_fm_drums.asm ├── sn │ ├── volume_change.inc │ ├── vibrato.inc │ ├── note_on_off.inc │ ├── pitch_slide.inc │ └── fnums_sn.inc ├── arpeggio.inc ├── vibrato.inc ├── build_music_driver_sdas.bat ├── process_channels_tic.inc ├── opll │ ├── volume_change.inc │ ├── vibrato.inc │ ├── fnums_fm.inc │ ├── drum_tables.inc │ ├── pitch_slide.inc │ └── note_on_off.inc ├── music_stop.inc ├── pitch_slide.inc ├── music_driver_sdas.asm ├── volume_macro.inc ├── music_queue.inc ├── mute_unmute.inc ├── defines_sdas.inc ├── music_init.inc ├── update_pitch_registers.inc ├── instrument_change.inc └── banjo.h ├── notes ├── sms_fm_music_notes.txt ├── _TODO.txt └── sms_sn_music_notes.txt ├── music_driver ├── sn │ ├── volume_change.inc │ ├── vibrato.inc │ ├── note_on_off.inc │ ├── pitch_slide.inc │ └── fnums_sn.inc ├── arpeggio.inc ├── vibrato.inc ├── process_channels_tic.inc ├── opll │ ├── volume_change.inc │ ├── vibrato.inc │ ├── fnums_fm.inc │ ├── drum_tables.inc │ └── pitch_slide.inc ├── music_stop.inc ├── pitch_slide.inc ├── music_driver.asm ├── music_queue.inc ├── volume_macro.inc ├── defines_wladx.inc ├── mute_unmute.inc ├── music_init.inc ├── update_pitch_registers.inc └── instrument_change.inc ├── LICENSE.md ├── sms_fm_fnums.js ├── sms_sn_fnums.js ├── convert_music_driver_sdas.sh ├── convert_music_driver_sdas.bat ├── old_js_scripts └── sms_sn_fnums_with_fine_pitch.js └── convert_wladx_sdas.js /examples/wladx/linkfile.txt: -------------------------------------------------------------------------------- 1 | [objects] 2 | main.o -------------------------------------------------------------------------------- /music_driver_sdas/mode/sn.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 4 -------------------------------------------------------------------------------- /examples/visualiser/assets/palette.bin: -------------------------------------------------------------------------------- 1 | :?1 /  -------------------------------------------------------------------------------- /examples/wladx_no_queue/linkfile.txt: -------------------------------------------------------------------------------- 1 | [objects] 2 | main.o -------------------------------------------------------------------------------- /music_driver_sdas/mode/dual_sn.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 8 -------------------------------------------------------------------------------- /music_driver_sdas/mode/queues_off.asm: -------------------------------------------------------------------------------- 1 | QUEUES_OFF .equ 1 -------------------------------------------------------------------------------- /examples/visualiser/assets/sprite_palette.bin: -------------------------------------------------------------------------------- 1 | ?>8+  ;350 2 | -------------------------------------------------------------------------------- /music_driver_sdas/mode/fm.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 9 2 | INCLUDE_OPLL .equ 1 -------------------------------------------------------------------------------- /music_driver_sdas/mode/sn_fm.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 13 2 | INCLUDE_OPLL .equ 1 -------------------------------------------------------------------------------- /music_driver_sdas/mode/fm_drums.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 11 2 | INCLUDE_OPLL .equ 1 -------------------------------------------------------------------------------- /music_driver_sdas/mode/sn_fm_drums.asm: -------------------------------------------------------------------------------- 1 | CHANNEL_COUNT .equ 15 2 | INCLUDE_OPLL .equ 1 -------------------------------------------------------------------------------- /examples/cmajor.fur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/cmajor.fur -------------------------------------------------------------------------------- /examples/cmajor_sn.fur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/cmajor_sn.fur -------------------------------------------------------------------------------- /examples/sfx_test.fur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/sfx_test.fur -------------------------------------------------------------------------------- /examples/sfx_test_sn.fur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/sfx_test_sn.fur -------------------------------------------------------------------------------- /examples/cmajor_dual_sn.fur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/cmajor_dual_sn.fur -------------------------------------------------------------------------------- /examples/visualiser/banjo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/banjo.png -------------------------------------------------------------------------------- /examples/visualiser/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/labels.png -------------------------------------------------------------------------------- /examples/visualiser/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/keyboard.png -------------------------------------------------------------------------------- /examples/visualiser/keys_lit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/keys_lit.png -------------------------------------------------------------------------------- /examples/devkitsms/bank1.c: -------------------------------------------------------------------------------- 1 | 2 | // this file exists just to have something in bank 1! 3 | 4 | void do_nothing() 5 | { 6 | 7 | } -------------------------------------------------------------------------------- /examples/visualiser/assets/banjo_tiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/assets/banjo_tiles.bin -------------------------------------------------------------------------------- /examples/visualiser/assets/labels_tiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/assets/labels_tiles.bin -------------------------------------------------------------------------------- /examples/visualiser/bank1.c: -------------------------------------------------------------------------------- 1 | 2 | // this file exists just to have something in bank 1! 3 | 4 | void do_nothing(void) 5 | { 6 | 7 | } -------------------------------------------------------------------------------- /examples/devkitsms_no_queue/bank1.c: -------------------------------------------------------------------------------- 1 | 2 | // this file exists just to have something in bank 1! 3 | 4 | void do_nothing() 5 | { 6 | 7 | } -------------------------------------------------------------------------------- /examples/visualiser/assets/keyboard_tiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/assets/keyboard_tiles.bin -------------------------------------------------------------------------------- /examples/visualiser/assets/keys_lit_tiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joffb/banjo/HEAD/examples/visualiser/assets/keys_lit_tiles.bin -------------------------------------------------------------------------------- /examples/wladx/README.md: -------------------------------------------------------------------------------- 1 | # Basic WLA_DX example 2 | This will play a song in the background (different songs depending on whether an FM chip is detected) 3 | You can press button 2 to play a sound effect -------------------------------------------------------------------------------- /examples/visualiser/README.md: -------------------------------------------------------------------------------- 1 | # Visualiser example 2 | This will play a song in the background (different songs depending on whether an FM chip is detected) 3 | 4 | To build this you'll need to copy crt0_sms.rel, peep-rules.txt, SMSlib.h and SMSlib.lib from DevkitSMS into this folder (or add the folders containing them to the include path and library include paths in the build scripts) -------------------------------------------------------------------------------- /examples/devkitsms/README.md: -------------------------------------------------------------------------------- 1 | # Basic DevkitSMS example 2 | This will play a song in the background (different songs depending on whether an FM chip is detected) 3 | You can press button 2 to play a sound effect 4 | 5 | To build this you'll need to copy crt0_sms.rel, SMSlib.h and SMSlib.lib from DevkitSMS into this folder (or add the folders containing them to the include path and library include paths in the build scripts) -------------------------------------------------------------------------------- /examples/wladx_no_queue/README.md: -------------------------------------------------------------------------------- 1 | # WLA_DX example not using the queue system 2 | This will play a song in the background (different songs depending on whether an FM chip is detected) 3 | You can press button 2 to play a sound effect 4 | 5 | This doesn't use the built in queues system, instead it manually sets the bank and uses `banjo_play_song/sfx` and `banjo_update_song/sfx`. 6 | 7 | `banjo_play_song/sfx` have the 1 byte loop variable on the stack, so as in the example you'll need to push it then `inc sp` so only a byte's worth of data is effectively pushed. -------------------------------------------------------------------------------- /examples/devkitsms_no_queue/README.md: -------------------------------------------------------------------------------- 1 | # DevkitSMS example not using the queue system 2 | This will play a song in the background (different songs depending on whether an FM chip is detected) 3 | You can press button 2 to play a sound effect 4 | 5 | This doesn't use the built in queues system, instead it manually sets the bank and uses `banjo_play_song/sfx` and `banjo_update_song/sfx`. 6 | 7 | To build this you'll need to copy crt0_sms.rel, SMSlib.h and SMSlib.lib from DevkitSMS into this folder (or add the folders containing them to the include path and library include paths in the build scripts) -------------------------------------------------------------------------------- /notes/sms_fm_music_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | MUSIC_CLOCK = 3579545 3 | 4 | MUSIC_FM_FSAM = MUSIC_CLOCK / 72.0 5 | MUSIC_FM_FSAM = 49715 6 | 7 | freq = (pow(2.0, (note)/12.0) * 261.63) 8 | fnum = floor(((MUSIC_FM_FREQ * pow(2.0, 18)) / MUSIC_FM_FSAM) / pow(2, 3)) 9 | 10 | // C4 11 | freq_c4 = Math.pow(2.0, 0/12.0) * 261.63 12 | freq_c4 = 261.63 13 | 14 | fnum_c4 = Math.floor(((261.63 * Math.pow(2.0, 18)) / 49715) / Math.pow(2, 3)) 15 | fnum_c4 = 172 16 | 17 | 18 | // C#4 19 | freq_cs4 = Math.pow(2.0, 1/12.0) * 261.63 20 | freq_cs4 = 277.18732937722245 21 | 22 | fnum_cs4 = Math.floor(((172 * Math.pow(2.0, 18)) / 49715) / Math.pow(2, 3)) 23 | fnum_cs4 = 113 24 | 25 | diff = (172 - 113) = 59 -------------------------------------------------------------------------------- /examples/wladx/build.bat: -------------------------------------------------------------------------------- 1 | python ..\..\furnace2json.py -o .\cmajor.json ..\cmajor.fur 2 | python ..\..\json2sms.py -o cmajor.asm -i cmajor .\cmajor.json 3 | 4 | python ..\..\furnace2json.py -o .\cmajor_sn.json ..\cmajor_sn.fur 5 | python ..\..\json2sms.py -o cmajor_sn.asm -i cmajor_sn .\cmajor_sn.json 6 | 7 | python ..\..\furnace2json.py -o .\sfx_test.json ..\sfx_test.fur 8 | python ..\..\json2sms.py -o sfx_test.asm -s 2 -i sfx_test .\sfx_test.json 9 | 10 | python ..\..\furnace2json.py -o .\sfx_test_sn.json ..\sfx_test_sn.fur 11 | python ..\..\json2sms.py -o sfx_test_sn.asm -s 2 -i sfx_test_sn .\sfx_test_sn.json 12 | 13 | wla-z80 -D BANJO_MODE=5 main.asm 14 | wlalink -s -r linkfile.txt main.sms 15 | wlalink -s -r linkfile.txt main.gg -------------------------------------------------------------------------------- /examples/wladx/build.sh: -------------------------------------------------------------------------------- 1 | python ../../furnace2json.py -o ./cmajor.json ../cmajor.fur 2 | python ../../json2sms.py -o cmajor.asm -i cmajor ./cmajor.json 3 | 4 | python ../../furnace2json.py -o ./cmajor_sn.json ../cmajor_sn.fur 5 | python ../../json2sms.py -o cmajor_sn.asm -i cmajor_sn ./cmajor_sn.json 6 | 7 | python ../../furnace2json.py -o ./sfx_test.json ../sfx_test.fur 8 | python ../../json2sms.py -o sfx_test.asm -s 2 -i sfx_test ./sfx_test.json 9 | 10 | python ../../furnace2json.py -o ./sfx_test_sn.json ../sfx_test_sn.fur 11 | python ../../json2sms.py -o sfx_test_sn.asm -s 2 -i sfx_test_sn ./sfx_test_sn.json 12 | 13 | wla-z80 -D BANJO_MODE=5 main.asm 14 | wlalink -s -r linkfile.txt main.sms 15 | wlalink -s -r linkfile.txt main.gg -------------------------------------------------------------------------------- /examples/wladx_no_queue/build.bat: -------------------------------------------------------------------------------- 1 | python ..\..\furnace2json.py -o .\cmajor.json ..\cmajor.fur 2 | python ..\..\json2sms.py -o cmajor.asm -i cmajor .\cmajor.json 3 | 4 | python ..\..\furnace2json.py -o .\cmajor_sn.json ..\cmajor_sn.fur 5 | python ..\..\json2sms.py -o cmajor_sn.asm -i cmajor_sn .\cmajor_sn.json 6 | 7 | python ..\..\furnace2json.py -o .\sfx_test.json ..\sfx_test.fur 8 | python ..\..\json2sms.py -o sfx_test.asm -s 2 -i sfx_test .\sfx_test.json 9 | 10 | python ..\..\furnace2json.py -o .\sfx_test_sn.json ..\sfx_test_sn.fur 11 | python ..\..\json2sms.py -o sfx_test_sn.asm -s 2 -i sfx_test_sn .\sfx_test_sn.json 12 | 13 | wla-z80 -D BANJO_MODE=2 -D QUEUES_OFF=1 main.asm 14 | wlalink -s -r linkfile.txt main.sms 15 | wlalink -s -r linkfile.txt main.gg -------------------------------------------------------------------------------- /examples/wladx_no_queue/build.sh: -------------------------------------------------------------------------------- 1 | python ../../furnace2json.py -o ./cmajor.json ../cmajor.fur 2 | python ../../json2sms.py -o cmajor.asm -i cmajor ./cmajor.json 3 | 4 | python ../../furnace2json.py -o ./cmajor_sn.json ../cmajor_sn.fur 5 | python ../../json2sms.py -o cmajor_sn.asm -i cmajor_sn ./cmajor_sn.json 6 | 7 | python ../../furnace2json.py -o ./sfx_test.json ../sfx_test.fur 8 | python ../../json2sms.py -o sfx_test.asm -s 2 -i sfx_test ./sfx_test.json 9 | 10 | python ../../furnace2json.py -o ./sfx_test_sn.json ../sfx_test_sn.fur 11 | python ../../json2sms.py -o sfx_test_sn.asm -s 2 -i sfx_test_sn ./sfx_test_sn.json 12 | 13 | wla-z80 -D BANJO_MODE=2 -D QUEUES_OFF=1 main.asm 14 | wlalink -s -r linkfile.txt main.sms 15 | wlalink -s -r linkfile.txt main.gg -------------------------------------------------------------------------------- /examples/visualiser/bank2.h: -------------------------------------------------------------------------------- 1 | extern const unsigned char keys_lit_tiles_bin[1792]; 2 | #define keys_lit_tiles_bin_size 1792 3 | #define keys_lit_tiles_bin_bank 2 4 | extern const unsigned char keyboard_tiles_bin[448]; 5 | #define keyboard_tiles_bin_size 448 6 | #define keyboard_tiles_bin_bank 2 7 | extern const unsigned char banjo_tiles_bin[384]; 8 | #define banjo_tiles_bin_size 384 9 | #define banjo_tiles_bin_bank 2 10 | extern const unsigned char labels_tiles_bin[288]; 11 | #define labels_tiles_bin_size 288 12 | #define labels_tiles_bin_bank 2 13 | extern const unsigned char palette_bin[16]; 14 | #define palette_bin_size 16 15 | #define palette_bin_bank 2 16 | extern const unsigned char sprite_palette_bin[16]; 17 | #define sprite_palette_bin_size 16 18 | #define sprite_palette_bin_bank 2 19 | -------------------------------------------------------------------------------- /music_driver_sdas/sn/volume_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_volume_change_sn: 6 | 7 | ; restore original hl 8 | ex de, hl 9 | 10 | ; update local volume variable 11 | inc hl 12 | ld a, (hl) 13 | ld channel.volume(ix), a 14 | 15 | ; preserve volume in c 16 | ld c, a 17 | 18 | ; don't update chip volume if 19 | ; channel is muted, volume macro is active or note-on is false 20 | ld a, channel.flags(ix) 21 | xor a, #CHAN_FLAG_NOTE_ON 22 | and a, #CHAN_FLAG_MUTED+CHAN_FLAG_NOTE_ON+CHAN_FLAG_VOLUME_MACRO 23 | jr nz, mvc_sn_dont_update_chip 24 | 25 | ; get volume back into a 26 | ; and write it to the chip 27 | ld a, c 28 | ld c, channel.port(ix) 29 | out (c), a 30 | 31 | mvc_sn_dont_update_chip: 32 | inc hl 33 | jp music_process_new_line_subloop 34 | -------------------------------------------------------------------------------- /music_driver/sn/volume_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_volume_change_sn: 6 | 7 | ; restore original hl 8 | ex de, hl 9 | 10 | ; update local volume variable 11 | inc hl 12 | ld a, (hl) 13 | ld (ix + channel.volume), a 14 | 15 | ; preserve volume in c 16 | ld c, a 17 | 18 | ; don't update chip volume if 19 | ; channel is muted, volume macro is active or note-on is false 20 | ld a, (ix + channel.flags) 21 | xor a, CHAN_FLAG_NOTE_ON 22 | and a, CHAN_FLAG_MUTED + CHAN_FLAG_NOTE_ON + CHAN_FLAG_VOLUME_MACRO 23 | jr nz, mvc_sn_dont_update_chip 24 | 25 | ; get volume back into a 26 | ; and write it to the chip 27 | ld a, c 28 | ld c, (ix + channel.port) 29 | out (c), a 30 | 31 | mvc_sn_dont_update_chip: 32 | inc hl 33 | jp music_process_new_line_subloop 34 | -------------------------------------------------------------------------------- /examples/devkitsms/song_table.h: -------------------------------------------------------------------------------- 1 | 2 | // Song tables 3 | 4 | // References to Songs which will be matched up at link time 5 | extern song_data_t const cmajor; 6 | extern song_data_t const cmajor_sn; 7 | 8 | // FM Song table 9 | // SONG_DEF(SONG_LABEL, SONG_BANK) 10 | song_t const song_table_fm[] = { 11 | SONG_DEF(cmajor, 2), 12 | }; 13 | 14 | // SN Song table 15 | // SONG_DEF(SONG_LABEL, SONG_BANK) 16 | song_t const song_table_sn[] = { 17 | SONG_DEF(cmajor_sn, 3), 18 | }; 19 | 20 | 21 | // SFX tables 22 | 23 | // References to SFX which will be matched up at link time 24 | extern song_data_t const sfx_test; 25 | extern song_data_t const sfx_test_sn; 26 | 27 | // FM SFX table 28 | // SFX_DEF(SFX_LABEL, SFX_BANK, SFX_PRIORITY) 29 | song_t const sfx_table_fm[] = { 30 | SFX_DEF(sfx_test, 2, 0), 31 | }; 32 | 33 | // SN SFX table 34 | song_t const sfx_table_sn[] = { 35 | SFX_DEF(sfx_test_sn, 3, 0), 36 | }; -------------------------------------------------------------------------------- /examples/visualiser/song_table.h: -------------------------------------------------------------------------------- 1 | 2 | // Song tables 3 | 4 | // References to Songs which will be matched up at link time 5 | extern song_data_t const cmajor; 6 | extern song_data_t const cmajor_sn; 7 | 8 | // FM Song table 9 | // SONG_DEF(SONG_LABEL, SONG_BANK) 10 | song_t const song_table_fm[] = { 11 | SONG_DEF(cmajor, 2), 12 | }; 13 | 14 | // SN Song table 15 | // SONG_DEF(SONG_LABEL, SONG_BANK) 16 | song_t const song_table_sn[] = { 17 | SONG_DEF(cmajor_sn, 3), 18 | }; 19 | 20 | 21 | // SFX tables 22 | 23 | // References to SFX which will be matched up at link time 24 | extern song_data_t const sfx_test; 25 | extern song_data_t const sfx_test_sn; 26 | 27 | // FM SFX table 28 | // SFX_DEF(SFX_LABEL, SFX_BANK, SFX_PRIORITY) 29 | song_t const sfx_table_fm[] = { 30 | SFX_DEF(sfx_test, 2, 0), 31 | }; 32 | 33 | // SN SFX table 34 | song_t const sfx_table_sn[] = { 35 | SFX_DEF(sfx_test_sn, 3, 0), 36 | }; -------------------------------------------------------------------------------- /examples/devkitsms_no_queue/song_table.h: -------------------------------------------------------------------------------- 1 | 2 | // Song tables 3 | 4 | // References to Songs which will be matched up at link time 5 | extern song_data_t const cmajor; 6 | extern song_data_t const cmajor_sn; 7 | 8 | // FM Song table 9 | // SONG_DEF(SONG_LABEL, SONG_BANK) 10 | song_t const song_table_fm[] = { 11 | SONG_DEF(cmajor, 2), 12 | }; 13 | 14 | // SN Song table 15 | // SONG_DEF(SONG_LABEL, SONG_BANK) 16 | song_t const song_table_sn[] = { 17 | SONG_DEF(cmajor_sn, 3), 18 | }; 19 | 20 | 21 | // SFX tables 22 | 23 | // References to SFX which will be matched up at link time 24 | extern song_data_t const sfx_test; 25 | extern song_data_t const sfx_test_sn; 26 | 27 | // FM SFX table 28 | // SFX_DEF(SFX_LABEL, SFX_BANK, SFX_PRIORITY) 29 | song_t const sfx_table_fm[] = { 30 | SFX_DEF(sfx_test, 2, 0), 31 | }; 32 | 33 | // SN SFX table 34 | song_t const sfx_table_sn[] = { 35 | SFX_DEF(sfx_test_sn, 3, 0), 36 | }; -------------------------------------------------------------------------------- /notes/_TODO.txt: -------------------------------------------------------------------------------- 1 | TODO LIST: 2 | note delay 3 | pitch slide - stop over/underflow of counters 4 | vibrato 5 | isn't working with arpeggios currently (freq overwritten by arpeggios in music_update_pitch_registers/music_calc_fnum) 6 | speed values are inconsistent with furnace? 7 | Should vibrato reset on new note? 8 | should banjo_sfx_stop's effect be postponed to banjo_update? 9 | optional Song/SFX looping 10 | when Song ends and all channels need muting, only mute non-Sfx tracks otherwise it'll cut off Sfx too when a song ends 11 | if an sfx is looping and the song stops and a new song starts, need to mute the channel that the sfx is on again 12 | convert remaining js scripts to python 13 | restore noise mode on sfx end in sn channel 3 14 | fixed pitch noise mode - keep track of noise mode when generating commands, if in fixed pitch mode then do (midi_note = midi_note % 12) 15 | -------------------------------------------------------------------------------- /music_driver/arpeggio.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_update_arpeggio: 6 | 7 | ; flag that we need to update the pitch 8 | set CHAN_FLAG_BIT_PITCH_CHANGED, (ix + channel.flags) 9 | 10 | ; move arpeggio pos along 11 | ; check whether arp_pos == 3 12 | ld a, (ix + channel.arpeggio_pos) 13 | inc a 14 | cp a, 3 15 | jr nz, mpct_inc_arp_pos 16 | 17 | ; if it's 3, reset arpeggio position to 0 18 | ld (ix + channel.arpeggio_pos), 0 19 | 20 | ret 21 | 22 | ; it's < 3 23 | mpct_inc_arp_pos: 24 | 25 | ; update arp position 26 | ld (ix + channel.arpeggio_pos), a 27 | 28 | ; rotate the nibbles of the arpeggio to alternate 29 | ; the note offset between tics 30 | ld a, (ix + channel.arpeggio) 31 | rrca 32 | rrca 33 | rrca 34 | rrca 35 | ld (ix + channel.arpeggio), a 36 | 37 | ret -------------------------------------------------------------------------------- /music_driver_sdas/arpeggio.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_update_arpeggio: 6 | 7 | ; flag that we need to update the pitch 8 | set CHAN_FLAG_BIT_PITCH_CHANGED, channel.flags(ix) 9 | 10 | ; move arpeggio pos along 11 | ; check whether arp_pos == 3 12 | ld a, channel.arpeggio_pos(ix) 13 | inc a 14 | cp a, #3 15 | jr nz, mpct_inc_arp_pos 16 | 17 | ; if it's 3, reset arpeggio position to 0 18 | ld channel.arpeggio_pos(ix), #0 19 | 20 | ret 21 | 22 | ; it's < 3 23 | mpct_inc_arp_pos: 24 | 25 | ; update arp position 26 | ld channel.arpeggio_pos(ix), a 27 | 28 | ; rotate the nibbles of the arpeggio to alternate 29 | ; the note offset between tics 30 | ld a, channel.arpeggio(ix) 31 | rrca 32 | rrca 33 | rrca 34 | rrca 35 | ld channel.arpeggio(ix), a 36 | 37 | ret 38 | -------------------------------------------------------------------------------- /music_driver_sdas/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | muvib_jump_table: 6 | 7 | jp music_update_vibrato_sn 8 | jp music_update_vibrato_fm 9 | jp muvib_done 10 | 11 | ; ix: current channel 12 | ; iy: music state 13 | music_update_vibrato: 14 | 15 | ; flag that we need to update the pitch 16 | set CHAN_FLAG_BIT_PITCH_CHANGED, channel.flags(ix) 17 | 18 | ; get channel frequency into de 19 | ld e, channel.freq(ix) 20 | ld d, channel.freq+1(ix) 21 | 22 | ; get address in jump table for this channel type in hl 23 | ; lower byte 24 | ld a, channel.type(ix) 25 | ld c, a 26 | add a, a 27 | add a, c 28 | add a, #muvib_jump_table 33 | sub a, l 34 | ld h, a 35 | 36 | ; jump into jump table 37 | jp (hl) 38 | 39 | muvib_done: 40 | 41 | ; update channel frequency 42 | ld channel.freq(ix), e 43 | ld channel.freq+1(ix), d 44 | 45 | ret 46 | -------------------------------------------------------------------------------- /music_driver/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | muvib_jump_table: 6 | 7 | jp music_update_vibrato_sn 8 | jp music_update_vibrato_fm 9 | jp muvib_done 10 | 11 | ; ix: current channel 12 | ; iy: music state 13 | music_update_vibrato: 14 | 15 | ; flag that we need to update the pitch 16 | set CHAN_FLAG_BIT_PITCH_CHANGED, (ix + channel.flags) 17 | 18 | ; get channel frequency into de 19 | ld e, (ix + channel.freq) 20 | ld d, (ix + channel.freq + 1) 21 | 22 | ; get address in jump table for this channel type in hl 23 | ; lower byte 24 | ld a, (ix + channel.type) 25 | ld c, a 26 | add a, a 27 | add a, c 28 | add a, muvib_jump_table 33 | sub a, l 34 | ld h, a 35 | 36 | ; jump into jump table 37 | jp (hl) 38 | 39 | muvib_done: 40 | 41 | ; update channel frequency 42 | ld (ix + channel.freq), e 43 | ld (ix + channel.freq + 1), d 44 | 45 | ret 46 | -------------------------------------------------------------------------------- /examples/devkitsms_no_queue/main.c: -------------------------------------------------------------------------------- 1 | #include "SMSlib.h" 2 | 3 | #include "banjo.h" 4 | #include "song_table.h" 5 | 6 | unsigned char tic; 7 | 8 | void main(void) 9 | { 10 | unsigned int keys; 11 | 12 | SMS_VRAMmemsetW(0, 0x0000, 16384); 13 | SMS_setBGPaletteColor(0, RGBHTML(0x0000FF)); 14 | 15 | /* Turn on the display */ 16 | SMS_displayOn(); 17 | 18 | // used to check whether the FM unit is present, and whether we're on a Game Gear in Game Gear mode 19 | banjo_check_hardware(); 20 | 21 | banjo_init(MODE_SN); 22 | 23 | SMS_mapROMBank(3); 24 | banjo_play_song(&cmajor_sn, 0xff); 25 | 26 | tic = 0; 27 | 28 | for(;;) 29 | { 30 | 31 | SMS_waitForVBlank(); 32 | 33 | keys = SMS_getKeysPressed(); 34 | 35 | if (keys & 0x20) 36 | { 37 | SMS_mapROMBank(3); 38 | banjo_play_sfx(&sfx_test_sn, 0xff); 39 | } 40 | 41 | SMS_mapROMBank(3); 42 | banjo_update_song(); 43 | 44 | SMS_mapROMBank(3); 45 | banjo_update_sfx(); 46 | 47 | tic++; 48 | } 49 | } 50 | 51 | SMS_EMBED_SEGA_ROM_HEADER_16KB(9999,0); 52 | SMS_EMBED_SDSC_HEADER_AUTO_DATE_16KB(1,0,"joe k ","banjo test",""); 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Joe Kennedy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /music_driver_sdas/build_music_driver_sdas.bat: -------------------------------------------------------------------------------- 1 | sdasz80 -o lib/music_driver_fm.rel mode/fm.asm music_driver_sdas.asm 2 | sdasz80 -o lib/music_driver_sn.rel mode/sn.asm music_driver_sdas.asm 3 | sdasz80 -o lib/music_driver_sn_fm.rel mode/sn_fm.asm music_driver_sdas.asm 4 | sdasz80 -o lib/music_driver_fm_drums.rel mode/fm_drums.asm music_driver_sdas.asm 5 | sdasz80 -o lib/music_driver_dual_sn.rel mode/dual_sn.asm music_driver_sdas.asm 6 | sdasz80 -o lib/music_driver_sn_fm_drums.rel mode/sn_fm_drums.asm music_driver_sdas.asm 7 | 8 | sdasz80 -o lib/music_driver_qo_fm.rel mode/queues_off.asm mode/fm.asm music_driver_sdas.asm 9 | sdasz80 -o lib/music_driver_qo_sn.rel mode/queues_off.asm mode/sn.asm music_driver_sdas.asm 10 | sdasz80 -o lib/music_driver_qo_sn_fm.rel mode/queues_off.asm mode/sn_fm.asm music_driver_sdas.asm 11 | sdasz80 -o lib/music_driver_qo_fm_drums.rel mode/queues_off.asm mode/fm_drums.asm music_driver_sdas.asm 12 | sdasz80 -o lib/music_driver_qo_dual_sn.rel mode/queues_off.asm mode/dual_sn.asm music_driver_sdas.asm 13 | sdasz80 -o lib/music_driver_qo_sn_fm_drums.rel mode/queues_off.asm mode/sn_fm_drums.asm music_driver_sdas.asm -------------------------------------------------------------------------------- /examples/devkitsms/build.bat: -------------------------------------------------------------------------------- 1 | 2 | python ..\..\furnace2json.py -o .\cmajor.json ..\cmajor.fur 3 | python ..\..\json2sms.py -o cmajor.c -i cmajor .\cmajor.json 4 | 5 | python ..\..\furnace2json.py -o .\cmajor_sn.json ..\cmajor_sn.fur 6 | python ..\..\json2sms.py -o cmajor_sn.c -i cmajor_sn .\cmajor_sn.json 7 | 8 | python ..\..\furnace2json.py -o .\sfx_test.json ..\sfx_test.fur 9 | python ..\..\json2sms.py -o sfx_test.c -s 2 -i sfx_test .\sfx_test.json 10 | 11 | python ..\..\furnace2json.py -o .\sfx_test_sn.json ..\sfx_test_sn.fur 12 | python ..\..\json2sms.py -o sfx_test_sn.c -s 2 -i sfx_test_sn .\sfx_test_sn.json 13 | 14 | sdcc -c -I..\..\music_driver_sdas -mz80 main.c 15 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK1 bank1.c 16 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK2 cmajor.c 17 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK3 cmajor_sn.c 18 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK2 sfx_test.c 19 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK3 sfx_test_sn.c 20 | sdcc -o dktest.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK1=0x18000 -Wl-b_BANK2=0x28000 -Wl-b_BANK3=0x38000 crt0_sms.rel SMSlib.lib ..\..\music_driver_sdas\lib\music_driver_fm_drums.rel main.rel bank1.rel sfx_test.rel sfx_test_sn.rel cmajor.rel cmajor_sn.rel 21 | makesms dktest.ihx dktest.sms -------------------------------------------------------------------------------- /examples/devkitsms/build.sh: -------------------------------------------------------------------------------- 1 | 2 | python ../../furnace2json.py -o ./cmajor.json ../cmajor.fur 3 | python ../../json2sms.py -o cmajor.c -i cmajor ./cmajor.json 4 | 5 | python ../../furnace2json.py -o ./cmajor_sn.json ../cmajor_sn.fur 6 | python ../../json2sms.py -o cmajor_sn.c -i cmajor_sn ./cmajor_sn.json 7 | 8 | python ../../furnace2json.py -o ./sfx_test.json ../sfx_test.fur 9 | python ../../json2sms.py -o sfx_test.c -s 2 -i sfx_test ./sfx_test.json 10 | 11 | python ../../furnace2json.py -o ./sfx_test_sn.json ../sfx_test_sn.fur 12 | python ../../json2sms.py -o sfx_test_sn.c -s 2 -i sfx_test_sn ./sfx_test_sn.json 13 | 14 | sdcc -c -I../../music_driver_sdas -mz80 main.c 15 | sdcc -c -I../../music_driver_sdas -mz80 --codeseg BANK1 bank1.c 16 | sdcc -c -I../../music_driver_sdas -mz80 --codeseg BANK2 cmajor.c 17 | sdcc -c -I../../music_driver_sdas -mz80 --codeseg BANK3 cmajor_sn.c 18 | sdcc -c -I../../music_driver_sdas -mz80 --codeseg BANK2 sfx_test.c 19 | sdcc -c -I../../music_driver_sdas -mz80 --codeseg BANK3 sfx_test_sn.c 20 | sdcc -o dktest.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK1=0x18000 -Wl-b_BANK2=0x28000 -Wl-b_BANK3=0x38000 crt0_sms.rel SMSlib.lib ../../music_driver_sdas/lib/music_driver_fm_drums.rel main.rel bank1.rel sfx_test.rel sfx_test_sn.rel cmajor.rel cmajor_sn.rel 21 | makesms dktest.ihx dktest.sms -------------------------------------------------------------------------------- /examples/devkitsms_no_queue/build.bat: -------------------------------------------------------------------------------- 1 | 2 | python ..\..\furnace2json.py -o .\cmajor.json ..\cmajor.fur 3 | python ..\..\json2sms.py -o cmajor.c -i cmajor .\cmajor.json 4 | 5 | python ..\..\furnace2json.py -o .\cmajor_sn.json ..\cmajor_sn.fur 6 | python ..\..\json2sms.py -o cmajor_sn.c -i cmajor_sn .\cmajor_sn.json 7 | 8 | python ..\..\furnace2json.py -o .\sfx_test.json ..\sfx_test.fur 9 | python ..\..\json2sms.py -o sfx_test.c -s 2 -i sfx_test .\sfx_test.json 10 | 11 | python ..\..\furnace2json.py -o .\sfx_test_sn.json ..\sfx_test_sn.fur 12 | python ..\..\json2sms.py -o sfx_test_sn.c -s 2 -i sfx_test_sn .\sfx_test_sn.json 13 | 14 | sdcc -c -I..\..\music_driver_sdas -mz80 main.c 15 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK1 bank1.c 16 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK2 cmajor.c 17 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK3 cmajor_sn.c 18 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK2 sfx_test.c 19 | sdcc -c -I..\..\music_driver_sdas -mz80 --codeseg BANK3 sfx_test_sn.c 20 | sdcc -o dktest.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK1=0x18000 -Wl-b_BANK2=0x28000 -Wl-b_BANK3=0x38000 crt0_sms.rel SMSlib.lib ..\..\music_driver_sdas\lib\music_driver_qo_fm_drums.rel main.rel bank1.rel sfx_test.rel sfx_test_sn.rel cmajor.rel cmajor_sn.rel 21 | makesms dktest.ihx dktest.sms -------------------------------------------------------------------------------- /examples/devkitsms/main.c: -------------------------------------------------------------------------------- 1 | #include "SMSlib.h" 2 | 3 | #include "banjo.h" 4 | #include "song_table.h" 5 | 6 | unsigned char tic; 7 | 8 | void main(void) 9 | { 10 | unsigned int keys; 11 | 12 | SMS_VRAMmemsetW(0, 0x0000, 16384); 13 | SMS_setBGPaletteColor(0, RGBHTML(0x0000FF)); 14 | 15 | /* Turn on the display */ 16 | SMS_displayOn(); 17 | 18 | // used to check whether the FM unit is present, and whether we're on a Game Gear in Game Gear mode 19 | banjo_check_hardware(); 20 | 21 | // check whether the FM unit is present 22 | // use either the FM or SN song/sfx tables defined in song_tables.h depending on the result 23 | if (banjo_fm_unit_present) 24 | { 25 | banjo_init(MODE_FM); 26 | banjo_set_song_table(song_table_fm); 27 | banjo_set_sfx_table(sfx_table_fm); 28 | } 29 | else 30 | { 31 | banjo_init(MODE_SN); 32 | banjo_set_song_table(song_table_sn); 33 | banjo_set_sfx_table(sfx_table_sn); 34 | } 35 | 36 | // play song 0 37 | banjo_queue_song(0); 38 | 39 | tic = 0; 40 | 41 | for(;;) 42 | { 43 | 44 | SMS_waitForVBlank(); 45 | 46 | keys = SMS_getKeysPressed(); 47 | 48 | if (keys & 0x20) 49 | { 50 | banjo_queue_sfx(0); 51 | } 52 | 53 | banjo_update(); 54 | 55 | tic++; 56 | } 57 | } 58 | 59 | SMS_EMBED_SEGA_ROM_HEADER_16KB(9999,0); 60 | SMS_EMBED_SDSC_HEADER_AUTO_DATE_16KB(1,0,"joe k ","banjo test",""); 61 | -------------------------------------------------------------------------------- /music_driver_sdas/process_channels_tic.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; iy: song_state 6 | ; ix: channels 7 | music_process_channels_tic: 8 | 9 | ; number of channels to process 10 | ld b, music_state.channel_count(iy) 11 | 12 | music_process_channels_tic_loop: 13 | 14 | ; check that the note is on and the channel isn't muted 15 | ld a, channel.flags(ix) 16 | and a, #CHAN_FLAG_MUTED|CHAN_FLAG_NOTE_ON 17 | cp a, #CHAN_FLAG_NOTE_ON 18 | jr nz, mpct_next_channel 19 | 20 | ; apply volume macro 21 | bit CHAN_FLAG_BIT_VOLUME_MACRO, channel.flags(ix) 22 | call nz, music_update_volume_macro 23 | 24 | ; apply pitch slides/portamento 25 | bit CHAN_FLAG_BIT_SLIDE, channel.flags(ix) 26 | call nz, music_update_pitch_slide 27 | 28 | ; apply vibrato 29 | bit CHAN_FLAG_BIT_VIBRATO, channel.flags(ix) 30 | call nz, music_update_vibrato 31 | 32 | ; check if there's an active arpeggio 33 | bit CHAN_FLAG_BIT_ARPEGGIO, channel.flags(ix) 34 | call nz, music_update_arpeggio 35 | 36 | ; check if we need to update the pitch of this channel 37 | bit CHAN_FLAG_BIT_PITCH_CHANGED, channel.flags(ix) 38 | call nz, music_update_pitch_registers 39 | 40 | mpct_next_channel: 41 | 42 | ; next channel 43 | ld de, #_sizeof_channel 44 | add ix, de 45 | 46 | djnz music_process_channels_tic_loop 47 | 48 | ret 49 | -------------------------------------------------------------------------------- /music_driver/process_channels_tic.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; iy: song_state 6 | ; ix: channels 7 | music_process_channels_tic: 8 | 9 | ; number of channels to process 10 | ld b, (iy + music_state.channel_count) 11 | 12 | music_process_channels_tic_loop: 13 | 14 | ; check that the note is on and the channel isn't muted 15 | ld a, (ix + channel.flags) 16 | and a, CHAN_FLAG_MUTED | CHAN_FLAG_NOTE_ON 17 | cp a, CHAN_FLAG_NOTE_ON 18 | jr nz, mpct_next_channel 19 | 20 | ; apply volume macro 21 | bit CHAN_FLAG_BIT_VOLUME_MACRO, (ix + channel.flags) 22 | call nz, music_update_volume_macro 23 | 24 | ; apply pitch slides/portamento 25 | bit CHAN_FLAG_BIT_SLIDE, (ix + channel.flags) 26 | call nz, music_update_pitch_slide 27 | 28 | ; apply vibrato 29 | bit CHAN_FLAG_BIT_VIBRATO, (ix + channel.flags) 30 | call nz, music_update_vibrato 31 | 32 | ; check if there's an active arpeggio 33 | bit CHAN_FLAG_BIT_ARPEGGIO, (ix + channel.flags) 34 | call nz, music_update_arpeggio 35 | 36 | ; check if we need to update the pitch of this channel 37 | bit CHAN_FLAG_BIT_PITCH_CHANGED, (ix + channel.flags) 38 | call nz, music_update_pitch_registers 39 | 40 | mpct_next_channel: 41 | 42 | ; next channel 43 | ld de, _sizeof_channel 44 | add ix, de 45 | 46 | djnz music_process_channels_tic_loop 47 | 48 | ret -------------------------------------------------------------------------------- /examples/visualiser/key_x_tile.h: -------------------------------------------------------------------------------- 1 | /* 2 | out = ""; 3 | key_x_offsets = [0, 3, 4, 7, 8, 12, 15, 16, 19, 20, 23, 24]; 4 | key_tiles = [0, 6, 2, 6, 4, 0, 6, 2, 6, 2, 6, 4]; 5 | for (i = 0; i < 128; i++) { 6 | out += 7 | ((((Math.floor(i / 12) * 28) + key_x_offsets[i % 12]) & 0xff) << 8) + 8 | key_tiles[i % 12] + ", " + ((i % 8 == 7) ? "\n" : "") 9 | } 10 | console.log(out); 11 | */ 12 | 13 | const unsigned int key_x_tile[128] = { 14 | 0, 774, 1026, 1798, 2052, 3072, 3846, 4098, 15 | 4870, 5122, 5894, 6148, 7168, 7942, 8194, 8966, 16 | 9220, 10240, 11014, 11266, 12038, 12290, 13062, 13316, 17 | 14336, 15110, 15362, 16134, 16388, 17408, 18182, 18434, 18 | 19206, 19458, 20230, 20484, 21504, 22278, 22530, 23302, 19 | 23556, 24576, 25350, 25602, 26374, 26626, 27398, 27652, 20 | 28672, 29446, 29698, 30470, 30724, 31744, 32518, 32770, 21 | 33542, 33794, 34566, 34820, 35840, 36614, 36866, 37638, 22 | 37892, 38912, 39686, 39938, 40710, 40962, 41734, 41988, 23 | 43008, 43782, 44034, 44806, 45060, 46080, 46854, 47106, 24 | 47878, 48130, 48902, 49156, 50176, 50950, 51202, 51974, 25 | 52228, 53248, 54022, 54274, 55046, 55298, 56070, 56324, 26 | 57344, 58118, 58370, 59142, 59396, 60416, 61190, 61442, 27 | 62214, 62466, 63238, 63492, 64512, 65286, 2, 774, 28 | 1028, 2048, 2822, 3074, 3846, 4098, 4870, 5124, 29 | 6144, 6918, 7170, 7942, 8196, 9216, 9990, 10242, 30 | }; -------------------------------------------------------------------------------- /sms_fm_fnums.js: -------------------------------------------------------------------------------- 1 | /* 2 | Joe Kennedy 2024 3 | Create asm include of OPLL fnums 4 | */ 5 | 6 | const fs = require('fs'); 7 | const note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 8 | 9 | const MUSIC_CLOCK = 3579545.0; 10 | const MUSIC_FM_FSAM = (MUSIC_CLOCK / 72.0); 11 | 12 | // midi note frequencies for all midi notes 13 | 14 | var notes = []; 15 | 16 | for (var oct = 0; oct < 8; oct++) 17 | { 18 | for (var i = 0; i < 12; i++) 19 | { 20 | // frequency of note at octave 4 21 | let freq = Math.pow(2.0, i/12.0) * 261.63; 22 | 23 | // f number of note at octave 4 24 | let fnum = Math.floor(((freq * Math.pow(2.0, 18)) / MUSIC_FM_FSAM) / Math.pow(2, 3)) 25 | 26 | // combine with octave 27 | fnum = fnum | (oct << 9); 28 | 29 | notes.push({ 30 | note_name: (note_names[i] + oct), 31 | freq: freq, 32 | fnum: fnum 33 | }); 34 | } 35 | } 36 | 37 | // output an include file with the sn note data 38 | let outfile = fs.openSync("fnums_fm.inc", "w+"); 39 | 40 | fs.writeSync(outfile, "fm_tone_lookup:\n"); 41 | fs.writeSync(outfile, ".ifdef INCLUDE_OPLL\n"); 42 | 43 | for (var i = 0; i < notes.length; i++) 44 | { 45 | fs.writeSync(outfile, "; " + notes[i].note_name + "\n"); 46 | fs.writeSync(outfile, ".dw " + notes[i].fnum + "\n"); 47 | } 48 | 49 | fs.writeSync(outfile, ".endif\n"); 50 | 51 | fs.closeSync(outfile); 52 | -------------------------------------------------------------------------------- /sms_sn_fnums.js: -------------------------------------------------------------------------------- 1 | /* 2 | Joe Kennedy 2024 3 | Create asm include of SN76489 fnums 4 | */ 5 | 6 | const fs = require('fs'); 7 | const note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 8 | 9 | const MUSIC_CLOCK = 3579545.0; 10 | 11 | // midi note frequencies for all midi notes 12 | 13 | var midi_note_frequencies = []; 14 | 15 | for (var i = 0; i < 128; i++) 16 | { 17 | midi_note_frequencies.push(Math.pow(2.0, (i - 69.0)/12.0) * 440.0); 18 | } 19 | 20 | 21 | // fnum values for the sn chip for the given frequencies 22 | 23 | var sn_fnums = []; 24 | 25 | for (var i = 0; i < 128; i++) 26 | { 27 | var freq = MUSIC_CLOCK / ( 2 * 16 * midi_note_frequencies[i]); 28 | 29 | sn_fnums.push(freq > 1023 ? 1023 : freq); 30 | } 31 | 32 | var output = []; 33 | 34 | for (var i = 36; i < 128; i++) 35 | { 36 | var out; 37 | var note_name = note_names[i % 12]; 38 | note_name = "midi: " + note_name + (Math.floor(i / 12) - 1) + ", fur: " + note_name + (Math.floor(i / 12) - 3); 39 | 40 | output.push({fnum: Math.round(sn_fnums[i]) & 1023, note_name: note_name}); 41 | } 42 | 43 | // output an include file with the sn note data 44 | let outfile = fs.openSync("fnums_sn.inc", "w+"); 45 | 46 | fs.writeSync(outfile, "sn_tone_lookup:\n"); 47 | 48 | for (var i = 0; i < output.length; i++) 49 | { 50 | fs.writeSync(outfile, "; " + output[i].note_name + "\n"); 51 | fs.writeSync(outfile, ".dw " + output[i].fnum + "\n"); 52 | } 53 | 54 | fs.closeSync(outfile); 55 | -------------------------------------------------------------------------------- /notes/sms_sn_music_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | sn clock frequency is 3579545 3 | 4 | fnum = 3579545 / (2 * 16 * freq) 5 | 6 | fnum * 2 * 16 * freq = 3579545 7 | freq = 3579545 / (fnum * 2 * 16) 8 | 9 | lowest possible frequency: 10 | freq = 3579545 / (1023 * 2 * 16) 11 | freq = 109 hz 12 | lowest in-tune note possible is therefore midi note A2 (110 hz) 13 | midi note number 45 14 | in furnace this is note A-0 15 | 16 | furnace goes up to F-7 17 | midi note F9 (11175.30 hz) 18 | note number 125 19 | 20 | 80 total playable notes 21 | 22 | maximum difference between consecutive fnums occurs in the lowest notes: 23 | 24 | from A2 to A#2 25 | A#2 freq = 116.54 hz 26 | fnum = 3579545 / (2 * 16 * 116.54) 27 | fnum = 3579545 / 3712 28 | fnum = 964 29 | 30 | A2 freq = 110.00 hz 31 | fnum = 3579545 / (2 * 16 * 110) 32 | fnum = 3579545 / 3520 33 | fnum = 1016 34 | 35 | difference = 52 36 | 37 | calculating for upper range: 38 | 39 | D#9 fnum = 11 40 | E9 fnum = 11 41 | 42 | freq = 3579545 / (11 * 2 * 16) 43 | freq = 10169 44 | 45 | somewhere between D#9 (9956.06 hz) and E9 (10548.08 hz) 46 | 47 | not enough resolution between fnums at higher pitches 48 | 49 | looking at accuracy: 50 | 51 | C8 (4186.01 hz) 52 | fnum = 27 53 | freq = 3579545 / (27 * 2 * 16) 54 | freq = 4142 hz 55 | 56 | B7 (3951.07 hz) 57 | fnum = 28 58 | freq = 3579545 / (28 * 2 * 16) 59 | freq = 3995 hz 60 | 61 | C7 (2093.00 hz) 62 | fnum = 53 63 | freq = 3579545 / (53 * 2 * 16) 64 | freq = 2110 hz 65 | 66 | C6 (1046.50 hz) 67 | fnum = 107 68 | freq = 3579545 / (107 * 2 * 16) 69 | freq = 1045 hz 70 | -------------------------------------------------------------------------------- /examples/visualiser/build.sh: -------------------------------------------------------------------------------- 1 | assets2banks assets 2 | 3 | python ../../furnace2json.py -o ./cmajor.json ../cmajor.fur 4 | python ../../json2sms.py -o cmajor.c -i cmajor ./cmajor.json 5 | 6 | python ../../furnace2json.py -o ./cmajor_sn.json ../cmajor_sn.fur 7 | python ../../json2sms.py -o cmajor_sn.c -i cmajor_sn ./cmajor_sn.json 8 | 9 | python ../../furnace2json.py -o ./sfx_test.json ../sfx_test.fur 10 | python ../../json2sms.py -o sfx_test.c -s 2 -i sfx_test ./sfx_test.json 11 | 12 | python ../../furnace2json.py -o ./sfx_test_sn.json ../sfx_test_sn.fur 13 | python ../../json2sms.py -o sfx_test_sn.c -s 2 -i sfx_test_sn ./sfx_test_sn.json 14 | 15 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 main.c 16 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK1 bank1.c 17 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK2 bank2.c 18 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK2 cmajor.c 19 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK3 cmajor_sn.c 20 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK2 sfx_test.c 21 | sdcc -c --peep-file peep-rules.txt -I../../music_driver_sdas -mz80 --codeseg BANK3 sfx_test_sn.c 22 | sdcc -o dktest.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK1=0x18000 -Wl-b_BANK2=0x28000 -Wl-b_BANK3=0x38000 crt0_sms.rel SMSlib.lib ../../music_driver_sdas/lib/music_driver_fm_drums.rel main.rel bank1.rel bank2.rel sfx_test.rel sfx_test_sn.rel cmajor.rel cmajor_sn.rel 23 | makesms dktest.ihx dktest.sms -------------------------------------------------------------------------------- /music_driver/opll/volume_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | music_volume_change_fm: 9 | 10 | ; restore original hl 11 | ex de, hl 12 | 13 | ; update local volume variable 14 | inc hl 15 | ld a, (hl) 16 | and a, 0xf 17 | ld (ix + channel.volume), a 18 | 19 | ; preserve volume in c 20 | ld c, a 21 | 22 | ; don't update chip volume if 23 | ; channel is muted, volume macro is active or note-on is false 24 | ld a, (ix + channel.flags) 25 | xor a, CHAN_FLAG_NOTE_ON 26 | and a, CHAN_FLAG_MUTED + CHAN_FLAG_NOTE_ON + CHAN_FLAG_VOLUME_MACRO 27 | jr nz, mvc_fm_dont_update_chip 28 | 29 | ; select volume/patch register 30 | ld a, (ix + channel.subchannel) 31 | add a, 0x30 32 | out (OPLL_REG_PORT), a 33 | 34 | ; get volume back into a 35 | ; combine with patch number and write to chip 36 | ld a, c 37 | or a, (ix + channel.fm_patch_shifted) 38 | out (OPLL_DATA_PORT), a 39 | 40 | mvc_fm_dont_update_chip: 41 | inc hl 42 | jp music_process_new_line_subloop 43 | 44 | music_volume_change_fm_drums: 45 | 46 | ; restore original hl 47 | ex de, hl 48 | 49 | ; update local volume variable 50 | inc hl 51 | ld a, (hl) 52 | ld (ix + channel.volume), a 53 | 54 | inc hl 55 | jp music_process_new_line_subloop 56 | 57 | ; don't include full opll code 58 | .else 59 | 60 | music_volume_change_fm: 61 | music_volume_change_fm_drums: 62 | inc hl 63 | jp music_process_new_line_subloop 64 | 65 | .endif -------------------------------------------------------------------------------- /examples/visualiser/build.bat: -------------------------------------------------------------------------------- 1 | assets2banks assets 2 | 3 | python ..\..\furnace2json.py -o .\cmajor.json ..\cmajor.fur 4 | python ..\..\json2sms.py -o cmajor.c -i cmajor .\cmajor.json 5 | 6 | python ..\..\furnace2json.py -o .\cmajor_sn.json ..\cmajor_sn.fur 7 | python ..\..\json2sms.py -o cmajor_sn.c -i cmajor_sn .\cmajor_sn.json 8 | 9 | python ..\..\furnace2json.py -o .\sfx_test.json ..\sfx_test.fur 10 | python ..\..\json2sms.py -o sfx_test.c -s 2 -i sfx_test .\sfx_test.json 11 | 12 | python ..\..\furnace2json.py -o .\sfx_test_sn.json ..\sfx_test_sn.fur 13 | python ..\..\json2sms.py -o sfx_test_sn.c -s 2 -i sfx_test_sn .\sfx_test_sn.json 14 | 15 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 -c main.c 16 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK1 -c bank1.c 17 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK2 -c bank2.c 18 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK2 -c cmajor.c 19 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK3 -c cmajor_sn.c 20 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK2 -c sfx_test.c 21 | sdcc --peep-file peep-rules.txt -I..\..\music_driver_sdas -mz80 --codeseg BANK3 -c sfx_test_sn.c 22 | sdcc -o visualiser.ihx -mz80 --no-std-crt0 --data-loc 0xC000 -Wl-b_BANK1=0x18000 -Wl-b_BANK2=0x28000 -Wl-b_BANK3=0x38000 crt0_sms.rel ..\..\music_driver_sdas\lib\music_driver_fm_drums.rel main.rel SMSlib.lib bank1.rel bank2.rel sfx_test.rel sfx_test_sn.rel cmajor.rel cmajor_sn.rel 23 | makesms visualiser.ihx visualiser.sms -------------------------------------------------------------------------------- /music_driver_sdas/opll/volume_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | music_volume_change_fm: 9 | 10 | ; restore original hl 11 | ex de, hl 12 | 13 | ; update local volume variable 14 | inc hl 15 | ld a, (hl) 16 | and a, #0xf 17 | ld channel.volume(ix), a 18 | 19 | ; preserve volume in c 20 | ld c, a 21 | 22 | ; don't update chip volume if 23 | ; channel is muted, volume macro is active or note-on is false 24 | ld a, channel.flags(ix) 25 | xor a, #CHAN_FLAG_NOTE_ON 26 | and a, #CHAN_FLAG_MUTED+CHAN_FLAG_NOTE_ON+CHAN_FLAG_VOLUME_MACRO 27 | jr nz, mvc_fm_dont_update_chip 28 | 29 | ; select volume/patch register 30 | ld a, channel.subchannel(ix) 31 | add a, #0x30 32 | out (#OPLL_REG_PORT), a 33 | 34 | ; get volume back into a 35 | ; combine with patch number and write to chip 36 | ld a, c 37 | or a, channel.fm_patch_shifted(ix) 38 | out (#OPLL_DATA_PORT), a 39 | 40 | mvc_fm_dont_update_chip: 41 | inc hl 42 | jp music_process_new_line_subloop 43 | 44 | music_volume_change_fm_drums: 45 | 46 | ; restore original hl 47 | ex de, hl 48 | 49 | ; update local volume variable 50 | inc hl 51 | ld a, (hl) 52 | ld channel.volume(ix), a 53 | 54 | inc hl 55 | jp music_process_new_line_subloop 56 | 57 | ; don't include full opll code 58 | .else 59 | 60 | music_volume_change_fm: 61 | music_volume_change_fm_drums: 62 | inc hl 63 | jp music_process_new_line_subloop 64 | 65 | .endif 66 | -------------------------------------------------------------------------------- /convert_music_driver_sdas.sh: -------------------------------------------------------------------------------- 1 | node convert_wladx_sdas.js music_driver/arpeggio.inc music_driver_sdas/arpeggio.inc 2 | node convert_wladx_sdas.js music_driver/music_init.inc music_driver_sdas/music_init.inc 3 | node convert_wladx_sdas.js music_driver/music_play.inc music_driver_sdas/music_play.inc 4 | node convert_wladx_sdas.js music_driver/music_stop.inc music_driver_sdas/music_stop.inc 5 | node convert_wladx_sdas.js music_driver/music_update.inc music_driver_sdas/music_update.inc 6 | node convert_wladx_sdas.js music_driver/update_pitch_registers.inc music_driver_sdas/update_pitch_registers.inc 7 | node convert_wladx_sdas.js music_driver/instrument_change.inc music_driver_sdas/instrument_change.inc 8 | node convert_wladx_sdas.js music_driver/process_channels_tic.inc music_driver_sdas/process_channels_tic.inc 9 | node convert_wladx_sdas.js music_driver/process_new_line.inc music_driver_sdas/process_new_line.inc 10 | node convert_wladx_sdas.js music_driver/pitch_slide.inc music_driver_sdas/pitch_slide.inc 11 | node convert_wladx_sdas.js music_driver/note_on_off.inc music_driver_sdas/note_on_off.inc 12 | node convert_wladx_sdas.js music_driver/mute_unmute.inc music_driver_sdas/mute_unmute.inc 13 | node convert_wladx_sdas.js music_driver/vibrato.inc music_driver_sdas/vibrato.inc 14 | node convert_wladx_sdas.js music_driver/volume_change.inc music_driver_sdas/volume_change.inc 15 | node convert_wladx_sdas.js music_driver/volume_macro.inc music_driver_sdas/volume_macro.inc 16 | node convert_wladx_sdas.js music_driver/fnums_sn.inc music_driver_sdas/fnums_sn.inc 17 | node convert_wladx_sdas.js music_driver/fnums_fm.inc music_driver_sdas/fnums_fm.inc -------------------------------------------------------------------------------- /music_driver/music_stop.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; stop the currently playing song and mute all channels 6 | banjo_song_stop: 7 | 8 | ; do nothing if a song isn't playing 9 | ld a, (song_playing) 10 | or a, a 11 | ret z 12 | 13 | ld iy, song_state 14 | 15 | ; does this song have fm channels? if so, mute them 16 | ld a, (iy + music_state.has_fm) 17 | or a, (iy + music_state.has_fm_drums) 18 | call nz, music_mute_all_fm 19 | 20 | ; does this song have sn channels? if so, mute them 21 | bit 0, (iy + music_state.has_sn) 22 | call nz, music_mute_all_sn 23 | 24 | ld a, 0 25 | ld (song_playing), a 26 | 27 | ret 28 | 29 | ; stop the currently playing sfx, muting its channel 30 | ; if a song is playing, unmute the corresponding channel 31 | banjo_sfx_stop: 32 | 33 | ; do nothing if an sfx isn't playing 34 | ld a, (sfx_playing) 35 | or a, a 36 | jr z, banjo_sfx_stop_done 37 | 38 | ; mute the sfx channel 39 | ld hl, sfx_channel 40 | call music_mute_channel 41 | 42 | ; check if a song is playing 43 | ld a, (song_playing) 44 | or a, a 45 | jr z, banjo_sfx_stop_done 46 | 47 | ; get the sfx channel 48 | ld iy, sfx_state 49 | ld a, (iy + music_state.sfx_channel) 50 | 51 | ; unmute the channel in the song 52 | call banjo_unmute_song_channel 53 | 54 | banjo_sfx_stop_done: 55 | 56 | ld a, 0 57 | ld (sfx_playing), a 58 | ld (sfx_priority), a 59 | 60 | ret -------------------------------------------------------------------------------- /music_driver_sdas/music_stop.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; stop the currently playing song and mute all channels 6 | _banjo_song_stop: 7 | 8 | ; do nothing if a song isn't playing 9 | ld a, (song_playing) 10 | or a, a 11 | ret z 12 | 13 | ld iy, #_song_state 14 | 15 | ; does this song have fm channels? if so, mute them 16 | ld a, music_state.has_fm(iy) 17 | or a, music_state.has_fm_drums(iy) 18 | call nz, music_mute_all_fm 19 | 20 | ; does this song have sn channels? if so, mute them 21 | bit 0, music_state.has_sn(iy) 22 | call nz, music_mute_all_sn 23 | 24 | ld a, #0 25 | ld (song_playing), a 26 | 27 | ret 28 | 29 | ; stop the currently playing sfx, muting its channel 30 | ; if a song is playing, unmute the corresponding channel 31 | _banjo_sfx_stop: 32 | 33 | ; do nothing if an sfx isn't playing 34 | ld a, (sfx_playing) 35 | or a, a 36 | jr z, banjo_sfx_stop_done 37 | 38 | ; mute the sfx channel 39 | ld hl, #_sfx_channel 40 | call music_mute_channel 41 | 42 | ; check if a song is playing 43 | ld a, (song_playing) 44 | or a, a 45 | jr z, banjo_sfx_stop_done 46 | 47 | ; get the sfx channel 48 | ld iy, #_sfx_state 49 | ld a, music_state.sfx_channel(iy) 50 | 51 | ; unmute the channel in the song 52 | call _banjo_unmute_song_channel 53 | 54 | banjo_sfx_stop_done: 55 | 56 | ld a, #0 57 | ld (sfx_playing), a 58 | ld (sfx_priority), a 59 | 60 | ret 61 | -------------------------------------------------------------------------------- /music_driver_sdas/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | music_update_pitch_slide_jump_table: 6 | 7 | ; sn slide type jump table 8 | jp music_update_pitch_slide_done 9 | jp music_update_sn_pitch_slide_upward 10 | jp music_update_sn_pitch_slide_downward 11 | jp music_update_sn_portamento 12 | 13 | ; fm slide type jump table 14 | jp music_update_pitch_slide_done 15 | jp music_update_fm_pitch_slide_upward 16 | jp music_update_fm_pitch_slide_downward 17 | jp music_update_fm_portamento 18 | 19 | ; fm drums slide type jump table 20 | jp music_update_pitch_slide_done 21 | jp music_update_pitch_slide_done 22 | jp music_update_pitch_slide_done 23 | jp music_update_pitch_slide_done 24 | 25 | ; update the pitch slides or portamento for a channel 26 | ; ix: channel 27 | ; iy: state 28 | music_update_pitch_slide: 29 | 30 | ; flag that we need to update the pitch 31 | set CHAN_FLAG_BIT_PITCH_CHANGED, channel.flags(ix) 32 | 33 | ; get current freq into de 34 | ld e, channel.freq(ix) 35 | ld d, channel.freq+1(ix) 36 | 37 | ; offset into jump table by channel.type * 4 38 | ld a, channel.type(ix) 39 | rlca 40 | rlca 41 | 42 | ; offset into jump table by slide type 43 | add a, channel.slide_type(ix) 44 | 45 | ; triple it as jump instructions are 3 bytes in size 46 | ld c, a 47 | add a, a 48 | add a, c 49 | 50 | ; get address in table to jump to 51 | add a, #music_update_pitch_slide_jump_table 54 | sub a, l 55 | ld h, a 56 | 57 | ; jump to it 58 | jp (hl) 59 | 60 | music_update_pitch_slide_done: 61 | 62 | ; update freq with new value 63 | ld channel.freq(ix), e 64 | ld channel.freq+1(ix), d 65 | 66 | ret 67 | -------------------------------------------------------------------------------- /music_driver/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | music_update_pitch_slide_jump_table: 6 | 7 | ; sn slide type jump table 8 | jp music_update_pitch_slide_done 9 | jp music_update_sn_pitch_slide_upward 10 | jp music_update_sn_pitch_slide_downward 11 | jp music_update_sn_portamento 12 | 13 | ; fm slide type jump table 14 | jp music_update_pitch_slide_done 15 | jp music_update_fm_pitch_slide_upward 16 | jp music_update_fm_pitch_slide_downward 17 | jp music_update_fm_portamento 18 | 19 | ; fm drums slide type jump table 20 | jp music_update_pitch_slide_done 21 | jp music_update_pitch_slide_done 22 | jp music_update_pitch_slide_done 23 | jp music_update_pitch_slide_done 24 | 25 | ; update the pitch slides or portamento for a channel 26 | ; ix: channel 27 | ; iy: state 28 | music_update_pitch_slide: 29 | 30 | ; flag that we need to update the pitch 31 | set CHAN_FLAG_BIT_PITCH_CHANGED, (ix + channel.flags) 32 | 33 | ; get current freq into de 34 | ld e, (ix + channel.freq) 35 | ld d, (ix + channel.freq + 1) 36 | 37 | ; offset into jump table by channel.type * 4 38 | ld a, (ix + channel.type) 39 | rlca 40 | rlca 41 | 42 | ; offset into jump table by slide type 43 | add a, (ix + channel.slide_type) 44 | 45 | ; triple it as jump instructions are 3 bytes in size 46 | ld c, a 47 | add a, a 48 | add a, c 49 | 50 | ; get address in table to jump to 51 | add a, music_update_pitch_slide_jump_table 54 | sub a, l 55 | ld h, a 56 | 57 | ; jump to it 58 | jp (hl) 59 | 60 | music_update_pitch_slide_done: 61 | 62 | ; update freq with new value 63 | ld (ix + channel.freq), e 64 | ld (ix + channel.freq + 1), d 65 | 66 | ret 67 | -------------------------------------------------------------------------------- /music_driver_sdas/sn/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; ix: current channel 6 | ; iy: music state 7 | ; de: current channel.freq 8 | ; outputs updated freq in de 9 | music_update_vibrato_sn: 10 | 11 | ; add counter_add to counter and update variable 12 | ; ensure the value we load is between 0-15 by doing % 16 13 | ld a, channel.vibrato_counter(ix) 14 | and a, #0xf 15 | add a, channel.vibrato_counter_add(ix) 16 | ld channel.vibrato_counter(ix), a 17 | 18 | ; is the new counter value < 16? if so, we're done for now 19 | cp a, #16 20 | jp c, muvib_done 21 | 22 | ; counter has "overflowed" 23 | 24 | ; get amount to add by rotating top bits of counter into lowest bits 25 | ; store in hl for now 26 | rlca 27 | rlca 28 | rlca 29 | rlca 30 | and a, #0xf 31 | ld l, a 32 | ld h, #0 33 | 34 | ; get target vibrato amplitude into c 35 | ld c, channel.vibrato_target(ix) 36 | 37 | ; check whether target is > VIBRATO_CENTRE to decide vibrato direction 38 | ld a, #VIBRATO_CENTRE 39 | cp a, c 40 | jr nc, musnv_neg 41 | 42 | ; for sn, subtract from current frequency to increase pitch 43 | or a, a 44 | ex de, hl 45 | sbc hl, de 46 | ex de, hl 47 | 48 | ; add to current vibrato amplitude and save it 49 | ld a, channel.vibrato_current(ix) 50 | add a, l 51 | ld channel.vibrato_current(ix), a 52 | 53 | ; check if current >= target 54 | cp a, c 55 | jp nc, musnv_target_reached 56 | jp muvib_done 57 | 58 | musnv_neg: 59 | 60 | ; for sn, add to current frequency to decrease pitch 61 | ex de, hl 62 | add hl, de 63 | ex de, hl 64 | 65 | ; sub from vibrato amplitude and save it 66 | ld a, channel.vibrato_current(ix) 67 | sub a, l 68 | ld channel.vibrato_current(ix), a 69 | 70 | ; check if current <= target 71 | cp a, c 72 | jr z, musnv_target_reached 73 | jr c, musnv_target_reached 74 | jp muvib_done 75 | 76 | musnv_target_reached: 77 | 78 | ; negate target amplitude around VIBRATO_CENTRE 79 | ld a, #VIBRATO_CENTRE*2 80 | sub a, c 81 | ld channel.vibrato_target(ix), a 82 | 83 | jp muvib_done 84 | -------------------------------------------------------------------------------- /music_driver/sn/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; ix: current channel 6 | ; iy: music state 7 | ; de: current channel.freq 8 | ; outputs updated freq in de 9 | music_update_vibrato_sn: 10 | 11 | ; add counter_add to counter and update variable 12 | ; ensure the value we load is between 0-15 by doing % 16 13 | ld a, (ix + channel.vibrato_counter) 14 | and a, 0xf 15 | add a, (ix + channel.vibrato_counter_add) 16 | ld (ix + channel.vibrato_counter), a 17 | 18 | ; is the new counter value < 16? if so, we're done for now 19 | cp a, 16 20 | jp c, muvib_done 21 | 22 | ; counter has "overflowed" 23 | 24 | ; get amount to add by rotating top bits of counter into lowest bits 25 | ; store in hl for now 26 | rlca 27 | rlca 28 | rlca 29 | rlca 30 | and a, 0xf 31 | ld l, a 32 | ld h, 0 33 | 34 | ; get target vibrato amplitude into c 35 | ld c, (ix + channel.vibrato_target) 36 | 37 | ; check whether target is > VIBRATO_CENTRE to decide vibrato direction 38 | ld a, VIBRATO_CENTRE 39 | cp a, c 40 | jr nc, musnv_neg 41 | 42 | ; for sn, subtract from current frequency to increase pitch 43 | or a, a 44 | ex de, hl 45 | sbc hl, de 46 | ex de, hl 47 | 48 | ; add to current vibrato amplitude and save it 49 | ld a, (ix + channel.vibrato_current) 50 | add a, l 51 | ld (ix + channel.vibrato_current), a 52 | 53 | ; check if current >= target 54 | cp a, c 55 | jp nc, musnv_target_reached 56 | jp muvib_done 57 | 58 | musnv_neg: 59 | 60 | ; for sn, add to current frequency to decrease pitch 61 | ex de, hl 62 | add hl, de 63 | ex de, hl 64 | 65 | ; sub from vibrato amplitude and save it 66 | ld a, (ix + channel.vibrato_current) 67 | sub a, l 68 | ld (ix + channel.vibrato_current), a 69 | 70 | ; check if current <= target 71 | cp a, c 72 | jr z, musnv_target_reached 73 | jr c, musnv_target_reached 74 | jp muvib_done 75 | 76 | musnv_target_reached: 77 | 78 | ; negate target amplitude around VIBRATO_CENTRE 79 | ld a, VIBRATO_CENTRE * 2 80 | sub a, c 81 | ld (ix + channel.vibrato_target), a 82 | 83 | jp muvib_done -------------------------------------------------------------------------------- /music_driver/opll/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; ix: current channel 9 | ; iy: music state 10 | ; de: current channel.freq 11 | ; outputs updated freq in de 12 | music_update_vibrato_fm: 13 | 14 | ; add counter_add to counter and update variable 15 | ; ensure the value we load is between 0-15 by doing % 16 16 | ld a, (ix + channel.vibrato_counter) 17 | and a, 0xf 18 | add a, (ix + channel.vibrato_counter_add) 19 | ld (ix + channel.vibrato_counter), a 20 | 21 | ; is the new counter value < 16? if so, we're done for now 22 | cp a, 16 23 | jp c, muvib_done 24 | 25 | ; counter has "overflowed" 26 | 27 | ; get amount to add by rotating top bits of counter into lowest bits 28 | ; store in hl for now 29 | rlca 30 | rlca 31 | rlca 32 | rlca 33 | and a, 0xf 34 | ld l, a 35 | ld h, 0 36 | 37 | ; get target vibrato amplitude into c 38 | ld c, (ix + channel.vibrato_target) 39 | 40 | ; check whether target is > VIBRATO_CENTRE to decide vibrato direction 41 | ld a, VIBRATO_CENTRE 42 | cp a, c 43 | jr nc, mufmv_neg 44 | 45 | ; add to current frequency 46 | ex de, hl 47 | add hl, de 48 | ex de, hl 49 | 50 | ; add to current vibrato amplitude and save it 51 | ld a, (ix + channel.vibrato_current) 52 | add a, l 53 | ld (ix + channel.vibrato_current), a 54 | 55 | ; check if current >= target 56 | cp a, c 57 | jr nc, mufmv_target_reached 58 | jp muvib_done 59 | 60 | mufmv_neg: 61 | 62 | ; subtract from current frequency 63 | or a, a 64 | ex de, hl 65 | sbc hl, de 66 | ex de, hl 67 | 68 | ; sub from current vibrato amplitude and save it 69 | ld a, (ix + channel.vibrato_current) 70 | sub a, l 71 | ld (ix + channel.vibrato_current), a 72 | 73 | ; check if current <= target 74 | cp a, c 75 | jr z, mufmv_target_reached 76 | jr c, mufmv_target_reached 77 | jp muvib_done 78 | 79 | mufmv_target_reached: 80 | 81 | ; negate target amplitude around VIBRATO_CENTRE 82 | ld a, VIBRATO_CENTRE * 2 83 | sub a, c 84 | ld (ix + channel.vibrato_target), a 85 | 86 | jp muvib_done 87 | 88 | ; don't include full opll code 89 | .else 90 | 91 | music_update_vibrato_fm: 92 | jp muvib_done 93 | 94 | .endif -------------------------------------------------------------------------------- /music_driver_sdas/opll/vibrato.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; ix: current channel 9 | ; iy: music state 10 | ; de: current channel.freq 11 | ; outputs updated freq in de 12 | music_update_vibrato_fm: 13 | 14 | ; add counter_add to counter and update variable 15 | ; ensure the value we load is between 0-15 by doing % 16 16 | ld a, channel.vibrato_counter(ix) 17 | and a, #0xf 18 | add a, channel.vibrato_counter_add(ix) 19 | ld channel.vibrato_counter(ix), a 20 | 21 | ; is the new counter value < 16? if so, we're done for now 22 | cp a, #16 23 | jp c, muvib_done 24 | 25 | ; counter has "overflowed" 26 | 27 | ; get amount to add by rotating top bits of counter into lowest bits 28 | ; store in hl for now 29 | rlca 30 | rlca 31 | rlca 32 | rlca 33 | and a, #0xf 34 | ld l, a 35 | ld h, #0 36 | 37 | ; get target vibrato amplitude into c 38 | ld c, channel.vibrato_target(ix) 39 | 40 | ; check whether target is > VIBRATO_CENTRE to decide vibrato direction 41 | ld a, #VIBRATO_CENTRE 42 | cp a, c 43 | jr nc, mufmv_neg 44 | 45 | ; add to current frequency 46 | ex de, hl 47 | add hl, de 48 | ex de, hl 49 | 50 | ; add to current vibrato amplitude and save it 51 | ld a, channel.vibrato_current(ix) 52 | add a, l 53 | ld channel.vibrato_current(ix), a 54 | 55 | ; check if current >= target 56 | cp a, c 57 | jr nc, mufmv_target_reached 58 | jp muvib_done 59 | 60 | mufmv_neg: 61 | 62 | ; subtract from current frequency 63 | or a, a 64 | ex de, hl 65 | sbc hl, de 66 | ex de, hl 67 | 68 | ; sub from current vibrato amplitude and save it 69 | ld a, channel.vibrato_current(ix) 70 | sub a, l 71 | ld channel.vibrato_current(ix), a 72 | 73 | ; check if current <= target 74 | cp a, c 75 | jr z, mufmv_target_reached 76 | jr c, mufmv_target_reached 77 | jp muvib_done 78 | 79 | mufmv_target_reached: 80 | 81 | ; negate target amplitude around VIBRATO_CENTRE 82 | ld a, #VIBRATO_CENTRE*2 83 | sub a, c 84 | ld channel.vibrato_target(ix), a 85 | 86 | jp muvib_done 87 | 88 | ; don't include full opll code 89 | .else 90 | 91 | music_update_vibrato_fm: 92 | jp muvib_done 93 | 94 | .endif 95 | -------------------------------------------------------------------------------- /convert_music_driver_sdas.bat: -------------------------------------------------------------------------------- 1 | node convert_wladx_sdas.js music_driver\arpeggio.inc music_driver_sdas\arpeggio.inc 2 | node convert_wladx_sdas.js music_driver\instrument_change.inc music_driver_sdas\instrument_change.inc 3 | node convert_wladx_sdas.js music_driver\music_init.inc music_driver_sdas\music_init.inc 4 | node convert_wladx_sdas.js music_driver\music_play.inc music_driver_sdas\music_play.inc 5 | node convert_wladx_sdas.js music_driver\music_queue.inc music_driver_sdas\music_queue.inc 6 | node convert_wladx_sdas.js music_driver\music_stop.inc music_driver_sdas\music_stop.inc 7 | node convert_wladx_sdas.js music_driver\music_update.inc music_driver_sdas\music_update.inc 8 | node convert_wladx_sdas.js music_driver\mute_unmute.inc music_driver_sdas\mute_unmute.inc 9 | node convert_wladx_sdas.js music_driver\pitch_slide.inc music_driver_sdas\pitch_slide.inc 10 | node convert_wladx_sdas.js music_driver\process_channels_tic.inc music_driver_sdas\process_channels_tic.inc 11 | node convert_wladx_sdas.js music_driver\process_new_line.inc music_driver_sdas\process_new_line.inc 12 | node convert_wladx_sdas.js music_driver\update_pitch_registers.inc music_driver_sdas\update_pitch_registers.inc 13 | node convert_wladx_sdas.js music_driver\vibrato.inc music_driver_sdas\vibrato.inc 14 | node convert_wladx_sdas.js music_driver\volume_macro.inc music_driver_sdas\volume_macro.inc 15 | 16 | node convert_wladx_sdas.js music_driver\opll\drum_tables.inc music_driver_sdas\opll\drum_tables.inc 17 | node convert_wladx_sdas.js music_driver\opll\note_on_off.inc music_driver_sdas\opll\note_on_off.inc 18 | node convert_wladx_sdas.js music_driver\opll\pitch_slide.inc music_driver_sdas\opll\pitch_slide.inc 19 | node convert_wladx_sdas.js music_driver\opll\vibrato.inc music_driver_sdas\opll\vibrato.inc 20 | node convert_wladx_sdas.js music_driver\opll\volume_change.inc music_driver_sdas\opll\volume_change.inc 21 | node convert_wladx_sdas.js music_driver\opll\fnums_fm.inc music_driver_sdas\opll\fnums_fm.inc 22 | 23 | node convert_wladx_sdas.js music_driver\sn\note_on_off.inc music_driver_sdas\sn\note_on_off.inc 24 | node convert_wladx_sdas.js music_driver\sn\pitch_slide.inc music_driver_sdas\sn\pitch_slide.inc 25 | node convert_wladx_sdas.js music_driver\sn\vibrato.inc music_driver_sdas\sn\vibrato.inc 26 | node convert_wladx_sdas.js music_driver\sn\volume_change.inc music_driver_sdas\sn\volume_change.inc 27 | node convert_wladx_sdas.js music_driver\sn\fnums_sn.inc music_driver_sdas\sn\fnums_sn.inc -------------------------------------------------------------------------------- /music_driver_sdas/sn/note_on_off.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; b : current channel number 6 | ; hl : pointer to current instruction in pattern 7 | ; ix : channel pointer 8 | ; iy : pointer to music state 9 | music_sn_note_on: 10 | 11 | ; restore original hl 12 | ex de, hl 13 | 14 | ; update note-on status 15 | set CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 16 | 17 | ; update volume level on chip 18 | ld a, channel.volume(ix) 19 | ld c, channel.port(ix) 20 | out (c), a 21 | 22 | ; update midi number field 23 | inc hl 24 | ld a, (hl) 25 | ld channel.midi_note(ix), a 26 | 27 | ; multiply note number by 2 to get offset into lookup table 28 | ld de, #sn_tone_lookup 29 | add a, a 30 | add a, e 31 | ld e, a 32 | adc a, d 33 | sub a, e 34 | ld d, a 35 | 36 | ; decide where to put fnum depending on portamento mode 37 | ld a, channel.slide_type(ix) 38 | cp a, #SLIDE_TYPE_PORTA 39 | jr z, msnno_portamento 40 | 41 | msnno_no_portamento: 42 | 43 | ; reset current vibrato level 44 | ld channel.vibrato_current(ix), #VIBRATO_CENTRE 45 | 46 | ; portamento off, put fnum in freq 47 | ld a, (de) 48 | ld channel.freq(ix), a 49 | 50 | inc de 51 | ld a, (de) 52 | ld channel.freq+1(ix), a 53 | 54 | jr msnno_done 55 | 56 | msnno_portamento: 57 | 58 | ; portamento on, put fnum in target_freq 59 | ld a, (de) 60 | ld channel.target_freq(ix), a 61 | 62 | inc de 63 | ld a, (de) 64 | ld channel.target_freq+1(ix), a 65 | 66 | dec de 67 | 68 | ; check high byte of freq 69 | ; if it's 0xff that implies no notes have played so far 70 | ; so we write to freq too 71 | ld a, channel.freq+1(ix) 72 | cp a, #0xff 73 | jr z, msnno_no_portamento 74 | 75 | msnno_done: 76 | inc hl 77 | jp music_process_new_line_subloop 78 | 79 | ; b : current channel number 80 | ; hl : pointer to current instruction in pattern 81 | ; ix : pointer to current channel 82 | ; iy : pointer to music state 83 | music_sn_note_off: 84 | 85 | ; restore original hl 86 | ex de, hl 87 | 88 | ; clear note on flag 89 | res CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 90 | 91 | ; get channel (already shifted to the correct position) 92 | ld a, channel.subchannel(ix) 93 | 94 | ; set latch = 1, type = volume, volume = 0 95 | or a, #0x9f 96 | ld c, channel.port(ix) 97 | out (c), a 98 | 99 | inc hl 100 | jp music_process_new_line_subloop 101 | 102 | -------------------------------------------------------------------------------- /music_driver/sn/note_on_off.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; b : current channel number 6 | ; hl : pointer to current instruction in pattern 7 | ; ix : channel pointer 8 | ; iy : pointer to music state 9 | music_sn_note_on: 10 | 11 | ; restore original hl 12 | ex de, hl 13 | 14 | ; update note-on status 15 | set CHAN_FLAG_BIT_NOTE_ON, (ix + channel.flags) 16 | 17 | ; update volume level on chip 18 | ld a, (ix + channel.volume) 19 | ld c, (ix + channel.port) 20 | out (c), a 21 | 22 | ; update midi number field 23 | inc hl 24 | ld a, (hl) 25 | ld (ix + channel.midi_note), a 26 | 27 | ; multiply note number by 2 to get offset into lookup table 28 | ld de, sn_tone_lookup 29 | add a, a 30 | add a, e 31 | ld e, a 32 | adc a, d 33 | sub a, e 34 | ld d, a 35 | 36 | ; decide where to put fnum depending on portamento mode 37 | ld a, (ix + channel.slide_type) 38 | cp a, SLIDE_TYPE_PORTA 39 | jr z, msnno_portamento 40 | 41 | msnno_no_portamento: 42 | 43 | ; reset current vibrato level 44 | ld (ix + channel.vibrato_current), VIBRATO_CENTRE 45 | 46 | ; portamento off, put fnum in freq 47 | ld a, (de) 48 | ld (ix + channel.freq), a 49 | 50 | inc de 51 | ld a, (de) 52 | ld (ix + channel.freq + 1), a 53 | 54 | jr msnno_done 55 | 56 | msnno_portamento: 57 | 58 | ; portamento on, put fnum in target_freq 59 | ld a, (de) 60 | ld (ix + channel.target_freq), a 61 | 62 | inc de 63 | ld a, (de) 64 | ld (ix + channel.target_freq + 1), a 65 | 66 | dec de 67 | 68 | ; check high byte of freq 69 | ; if it's 0xff that implies no notes have played so far 70 | ; so we write to freq too 71 | ld a, (ix + channel.freq + 1) 72 | cp a, 0xff 73 | jr z, msnno_no_portamento 74 | 75 | msnno_done: 76 | inc hl 77 | jp music_process_new_line_subloop 78 | 79 | ; b : current channel number 80 | ; hl : pointer to current instruction in pattern 81 | ; ix : pointer to current channel 82 | ; iy : pointer to music state 83 | music_sn_note_off: 84 | 85 | ; restore original hl 86 | ex de, hl 87 | 88 | ; clear note on flag 89 | res CHAN_FLAG_BIT_NOTE_ON, (ix + channel.flags) 90 | 91 | ; get channel (already shifted to the correct position) 92 | ld a, (ix + channel.subchannel) 93 | 94 | ; set latch = 1, type = volume, volume = 0 95 | or a, 0x9f 96 | ld c, (ix + channel.port) 97 | out (c), a 98 | 99 | inc hl 100 | jp music_process_new_line_subloop 101 | 102 | -------------------------------------------------------------------------------- /music_driver/opll/fnums_fm.inc: -------------------------------------------------------------------------------- 1 | fm_tone_lookup: 2 | .ifdef INCLUDE_OPLL 3 | ; C0 4 | .dw 172 5 | ; C#0 6 | .dw 182 7 | ; D0 8 | .dw 193 9 | ; D#0 10 | .dw 205 11 | ; E0 12 | .dw 217 13 | ; F0 14 | .dw 230 15 | ; F#0 16 | .dw 243 17 | ; G0 18 | .dw 258 19 | ; G#0 20 | .dw 273 21 | ; A0 22 | .dw 290 23 | ; A#0 24 | .dw 307 25 | ; B0 26 | .dw 325 27 | ; C1 28 | .dw 684 29 | ; C#1 30 | .dw 694 31 | ; D1 32 | .dw 705 33 | ; D#1 34 | .dw 717 35 | ; E1 36 | .dw 729 37 | ; F1 38 | .dw 742 39 | ; F#1 40 | .dw 755 41 | ; G1 42 | .dw 770 43 | ; G#1 44 | .dw 785 45 | ; A1 46 | .dw 802 47 | ; A#1 48 | .dw 819 49 | ; B1 50 | .dw 837 51 | ; C2 52 | .dw 1196 53 | ; C#2 54 | .dw 1206 55 | ; D2 56 | .dw 1217 57 | ; D#2 58 | .dw 1229 59 | ; E2 60 | .dw 1241 61 | ; F2 62 | .dw 1254 63 | ; F#2 64 | .dw 1267 65 | ; G2 66 | .dw 1282 67 | ; G#2 68 | .dw 1297 69 | ; A2 70 | .dw 1314 71 | ; A#2 72 | .dw 1331 73 | ; B2 74 | .dw 1349 75 | ; C3 76 | .dw 1708 77 | ; C#3 78 | .dw 1718 79 | ; D3 80 | .dw 1729 81 | ; D#3 82 | .dw 1741 83 | ; E3 84 | .dw 1753 85 | ; F3 86 | .dw 1766 87 | ; F#3 88 | .dw 1779 89 | ; G3 90 | .dw 1794 91 | ; G#3 92 | .dw 1809 93 | ; A3 94 | .dw 1826 95 | ; A#3 96 | .dw 1843 97 | ; B3 98 | .dw 1861 99 | ; C4 100 | .dw 2220 101 | ; C#4 102 | .dw 2230 103 | ; D4 104 | .dw 2241 105 | ; D#4 106 | .dw 2253 107 | ; E4 108 | .dw 2265 109 | ; F4 110 | .dw 2278 111 | ; F#4 112 | .dw 2291 113 | ; G4 114 | .dw 2306 115 | ; G#4 116 | .dw 2321 117 | ; A4 118 | .dw 2338 119 | ; A#4 120 | .dw 2355 121 | ; B4 122 | .dw 2373 123 | ; C5 124 | .dw 2732 125 | ; C#5 126 | .dw 2742 127 | ; D5 128 | .dw 2753 129 | ; D#5 130 | .dw 2765 131 | ; E5 132 | .dw 2777 133 | ; F5 134 | .dw 2790 135 | ; F#5 136 | .dw 2803 137 | ; G5 138 | .dw 2818 139 | ; G#5 140 | .dw 2833 141 | ; A5 142 | .dw 2850 143 | ; A#5 144 | .dw 2867 145 | ; B5 146 | .dw 2885 147 | ; C6 148 | .dw 3244 149 | ; C#6 150 | .dw 3254 151 | ; D6 152 | .dw 3265 153 | ; D#6 154 | .dw 3277 155 | ; E6 156 | .dw 3289 157 | ; F6 158 | .dw 3302 159 | ; F#6 160 | .dw 3315 161 | ; G6 162 | .dw 3330 163 | ; G#6 164 | .dw 3345 165 | ; A6 166 | .dw 3362 167 | ; A#6 168 | .dw 3379 169 | ; B6 170 | .dw 3397 171 | ; C7 172 | .dw 3756 173 | ; C#7 174 | .dw 3766 175 | ; D7 176 | .dw 3777 177 | ; D#7 178 | .dw 3789 179 | ; E7 180 | .dw 3801 181 | ; F7 182 | .dw 3814 183 | ; F#7 184 | .dw 3827 185 | ; G7 186 | .dw 3842 187 | ; G#7 188 | .dw 3857 189 | ; A7 190 | .dw 3874 191 | ; A#7 192 | .dw 3891 193 | ; B7 194 | .dw 3909 195 | .endif 196 | -------------------------------------------------------------------------------- /music_driver_sdas/opll/fnums_fm.inc: -------------------------------------------------------------------------------- 1 | fm_tone_lookup: 2 | .ifdef INCLUDE_OPLL 3 | ; C0 4 | .dw 172 5 | ; C#0 6 | .dw 182 7 | ; D0 8 | .dw 193 9 | ; D#0 10 | .dw 205 11 | ; E0 12 | .dw 217 13 | ; F0 14 | .dw 230 15 | ; F#0 16 | .dw 243 17 | ; G0 18 | .dw 258 19 | ; G#0 20 | .dw 273 21 | ; A0 22 | .dw 290 23 | ; A#0 24 | .dw 307 25 | ; B0 26 | .dw 325 27 | ; C1 28 | .dw 684 29 | ; C#1 30 | .dw 694 31 | ; D1 32 | .dw 705 33 | ; D#1 34 | .dw 717 35 | ; E1 36 | .dw 729 37 | ; F1 38 | .dw 742 39 | ; F#1 40 | .dw 755 41 | ; G1 42 | .dw 770 43 | ; G#1 44 | .dw 785 45 | ; A1 46 | .dw 802 47 | ; A#1 48 | .dw 819 49 | ; B1 50 | .dw 837 51 | ; C2 52 | .dw 1196 53 | ; C#2 54 | .dw 1206 55 | ; D2 56 | .dw 1217 57 | ; D#2 58 | .dw 1229 59 | ; E2 60 | .dw 1241 61 | ; F2 62 | .dw 1254 63 | ; F#2 64 | .dw 1267 65 | ; G2 66 | .dw 1282 67 | ; G#2 68 | .dw 1297 69 | ; A2 70 | .dw 1314 71 | ; A#2 72 | .dw 1331 73 | ; B2 74 | .dw 1349 75 | ; C3 76 | .dw 1708 77 | ; C#3 78 | .dw 1718 79 | ; D3 80 | .dw 1729 81 | ; D#3 82 | .dw 1741 83 | ; E3 84 | .dw 1753 85 | ; F3 86 | .dw 1766 87 | ; F#3 88 | .dw 1779 89 | ; G3 90 | .dw 1794 91 | ; G#3 92 | .dw 1809 93 | ; A3 94 | .dw 1826 95 | ; A#3 96 | .dw 1843 97 | ; B3 98 | .dw 1861 99 | ; C4 100 | .dw 2220 101 | ; C#4 102 | .dw 2230 103 | ; D4 104 | .dw 2241 105 | ; D#4 106 | .dw 2253 107 | ; E4 108 | .dw 2265 109 | ; F4 110 | .dw 2278 111 | ; F#4 112 | .dw 2291 113 | ; G4 114 | .dw 2306 115 | ; G#4 116 | .dw 2321 117 | ; A4 118 | .dw 2338 119 | ; A#4 120 | .dw 2355 121 | ; B4 122 | .dw 2373 123 | ; C5 124 | .dw 2732 125 | ; C#5 126 | .dw 2742 127 | ; D5 128 | .dw 2753 129 | ; D#5 130 | .dw 2765 131 | ; E5 132 | .dw 2777 133 | ; F5 134 | .dw 2790 135 | ; F#5 136 | .dw 2803 137 | ; G5 138 | .dw 2818 139 | ; G#5 140 | .dw 2833 141 | ; A5 142 | .dw 2850 143 | ; A#5 144 | .dw 2867 145 | ; B5 146 | .dw 2885 147 | ; C6 148 | .dw 3244 149 | ; C#6 150 | .dw 3254 151 | ; D6 152 | .dw 3265 153 | ; D#6 154 | .dw 3277 155 | ; E6 156 | .dw 3289 157 | ; F6 158 | .dw 3302 159 | ; F#6 160 | .dw 3315 161 | ; G6 162 | .dw 3330 163 | ; G#6 164 | .dw 3345 165 | ; A6 166 | .dw 3362 167 | ; A#6 168 | .dw 3379 169 | ; B6 170 | .dw 3397 171 | ; C7 172 | .dw 3756 173 | ; C#7 174 | .dw 3766 175 | ; D7 176 | .dw 3777 177 | ; D#7 178 | .dw 3789 179 | ; E7 180 | .dw 3801 181 | ; F7 182 | .dw 3814 183 | ; F#7 184 | .dw 3827 185 | ; G7 186 | .dw 3842 187 | ; G#7 188 | .dw 3857 189 | ; A7 190 | .dw 3874 191 | ; A#7 192 | .dw 3891 193 | ; B7 194 | .dw 3909 195 | .endif 196 | -------------------------------------------------------------------------------- /music_driver/opll/drum_tables.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; bits which correspond to noteons in rhythm register 9 | fm_drum_triggers: 10 | ; kick 11 | .db 0x10 12 | ; snare 13 | .db 0x08 14 | ; tom 15 | .db 0x04 16 | ; cymbal 17 | .db 0x02 18 | ; hi-hat 19 | .db 0x01 20 | 21 | ; registers for each rhythm channel (snare/hat and tom/cymbal share) 22 | fm_drum_pitch_registers: 23 | ; kick 24 | .db 0x16 25 | ; snare 26 | .db 0x17 27 | ; tom 28 | .db 0x18 29 | ; cymbal 30 | .db 0x18 31 | ; hi-hat 32 | .db 0x17 33 | 34 | ; masks used to preserve volumes for other drums which share the byte 35 | fm_drum_volume_masks: 36 | ; kick 37 | .db 0xf0 38 | ; snare 39 | .db 0xf0 40 | ; tom 41 | .db 0x0f 42 | ; cymbal 43 | .db 0xf0 44 | ; hi-hat 45 | .db 0x0f 46 | 47 | music_set_default_fm_drum_pitches: 48 | 49 | ; tom? 50 | ld a, 0x16 51 | out (OPLL_REG_PORT), a 52 | ld a, 0x20 53 | out (OPLL_DATA_PORT), a 54 | 55 | ; delay after opll data write 56 | push hl 57 | pop hl 58 | push hl 59 | pop hl 60 | push hl 61 | pop hl 62 | 63 | ld a, 0x26 64 | out (OPLL_REG_PORT), a 65 | ld a, 0x05 66 | out (OPLL_DATA_PORT), a 67 | 68 | ; delay after opll data write 69 | push hl 70 | pop hl 71 | push hl 72 | pop hl 73 | push hl 74 | pop hl 75 | 76 | ; snare? 77 | ld a, 0x17 78 | out (OPLL_REG_PORT), a 79 | ld a, 0x50 80 | out (OPLL_DATA_PORT), a 81 | 82 | ; delay after opll data write 83 | push hl 84 | pop hl 85 | push hl 86 | pop hl 87 | push hl 88 | pop hl 89 | 90 | ld a, 0x27 91 | out (OPLL_REG_PORT), a 92 | ld a, 0x05 93 | out (OPLL_DATA_PORT), a 94 | 95 | ; delay after opll data write 96 | push hl 97 | pop hl 98 | push hl 99 | pop hl 100 | push hl 101 | pop hl 102 | 103 | ; kick? 104 | ld a, 0x18 105 | out (OPLL_REG_PORT), a 106 | ld a, 0xc0 107 | out (OPLL_DATA_PORT), a 108 | 109 | ; delay after opll data write 110 | push hl 111 | pop hl 112 | push hl 113 | pop hl 114 | push hl 115 | pop hl 116 | 117 | ld a, 0x28 118 | out (OPLL_REG_PORT), a 119 | ld a, 0x01 120 | out (OPLL_DATA_PORT), a 121 | 122 | ; delay after opll data write 123 | push hl 124 | pop hl 125 | push hl 126 | pop hl 127 | push hl 128 | pop hl 129 | 130 | ret 131 | 132 | 133 | ; don't include full opll code 134 | .else 135 | 136 | music_set_default_fm_drum_pitches: 137 | ret 138 | 139 | fm_drum_triggers: 140 | fm_drum_pitch_registers: 141 | fm_drum_volume_masks: 142 | 143 | .endif -------------------------------------------------------------------------------- /music_driver_sdas/opll/drum_tables.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; bits which correspond to noteons in rhythm register 9 | fm_drum_triggers: 10 | ; kick 11 | .db 0x10 12 | ; snare 13 | .db 0x08 14 | ; tom 15 | .db 0x04 16 | ; cymbal 17 | .db 0x02 18 | ; hi-hat 19 | .db 0x01 20 | 21 | ; registers for each rhythm channel (snare/hat and tom/cymbal share) 22 | fm_drum_pitch_registers: 23 | ; kick 24 | .db 0x16 25 | ; snare 26 | .db 0x17 27 | ; tom 28 | .db 0x18 29 | ; cymbal 30 | .db 0x18 31 | ; hi-hat 32 | .db 0x17 33 | 34 | ; masks used to preserve volumes for other drums which share the byte 35 | fm_drum_volume_masks: 36 | ; kick 37 | .db 0xf0 38 | ; snare 39 | .db 0xf0 40 | ; tom 41 | .db 0x0f 42 | ; cymbal 43 | .db 0xf0 44 | ; hi-hat 45 | .db 0x0f 46 | 47 | music_set_default_fm_drum_pitches: 48 | 49 | ; tom? 50 | ld a, #0x16 51 | out (#OPLL_REG_PORT), a 52 | ld a, #0x20 53 | out (#OPLL_DATA_PORT), a 54 | 55 | ; delay after opll data write 56 | push hl 57 | pop hl 58 | push hl 59 | pop hl 60 | push hl 61 | pop hl 62 | 63 | ld a, #0x26 64 | out (#OPLL_REG_PORT), a 65 | ld a, #0x05 66 | out (#OPLL_DATA_PORT), a 67 | 68 | ; delay after opll data write 69 | push hl 70 | pop hl 71 | push hl 72 | pop hl 73 | push hl 74 | pop hl 75 | 76 | ; snare? 77 | ld a, #0x17 78 | out (#OPLL_REG_PORT), a 79 | ld a, #0x50 80 | out (#OPLL_DATA_PORT), a 81 | 82 | ; delay after opll data write 83 | push hl 84 | pop hl 85 | push hl 86 | pop hl 87 | push hl 88 | pop hl 89 | 90 | ld a, #0x27 91 | out (#OPLL_REG_PORT), a 92 | ld a, #0x05 93 | out (#OPLL_DATA_PORT), a 94 | 95 | ; delay after opll data write 96 | push hl 97 | pop hl 98 | push hl 99 | pop hl 100 | push hl 101 | pop hl 102 | 103 | ; kick? 104 | ld a, #0x18 105 | out (#OPLL_REG_PORT), a 106 | ld a, #0xc0 107 | out (#OPLL_DATA_PORT), a 108 | 109 | ; delay after opll data write 110 | push hl 111 | pop hl 112 | push hl 113 | pop hl 114 | push hl 115 | pop hl 116 | 117 | ld a, #0x28 118 | out (#OPLL_REG_PORT), a 119 | ld a, #0x01 120 | out (#OPLL_DATA_PORT), a 121 | 122 | ; delay after opll data write 123 | push hl 124 | pop hl 125 | push hl 126 | pop hl 127 | push hl 128 | pop hl 129 | 130 | ret 131 | 132 | 133 | ; don't include full opll code 134 | .else 135 | 136 | music_set_default_fm_drum_pitches: 137 | ret 138 | 139 | fm_drum_triggers: 140 | fm_drum_pitch_registers: 141 | fm_drum_volume_masks: 142 | 143 | .endif 144 | -------------------------------------------------------------------------------- /music_driver_sdas/music_driver_sdas.asm: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | .include "defines_sdas.inc" 6 | 7 | .area _SMS_SRAM (REL,CON) 8 | 9 | ; console framerate 50/60hz 10 | music_framerate: .ds 1 11 | 12 | ; write bytes here to queue up music or sfx 13 | queue_sfx: .ds 1 14 | queue_song: .ds 1 15 | 16 | ; used to modify the loop type 17 | queue_sfx_loop: .ds 1 18 | queue_song_loop: .ds 1 19 | 20 | _banjo_fm_unit_present: .ds 1 21 | _banjo_game_gear_mode: .ds 1 22 | _banjo_system_e: .ds 1 23 | _banjo_mode: .ds 1 24 | 25 | ; state and channel data for song 26 | song_playing: .ds 1 27 | _song_state: .ds _sizeof_music_state 28 | _song_channels: .ds _sizeof_music_state * CHANNEL_COUNT 29 | song_channel_ptrs: .ds CHANNEL_COUNT * 2 30 | 31 | ; state and channel data for sfx 32 | sfx_playing: .ds 1 33 | sfx_priority: .ds 1 34 | _sfx_state: .ds _sizeof_music_state 35 | _sfx_channel: .ds _sizeof_channel 36 | 37 | ; pointers to song and sfx tables 38 | song_table_ptr: .ds 2 39 | sfx_table_ptr: .ds 2 40 | 41 | fm_drum_note_ons: .ds 1 42 | fm_drum_volumes: .ds 3 43 | 44 | .area _CODE (REL,CON) 45 | 46 | .globl _song_state, _song_channels 47 | .globl _sfx_state, _sfx_channel 48 | .globl _banjo_set_song_table, _banjo_set_sfx_table 49 | .globl _banjo_song_stop, _banjo_sfx_stop 50 | .globl _banjo_init, _banjo_update 51 | .globl _banjo_queue_song, _banjo_queue_sfx 52 | .globl _banjo_play_song, _banjo_play_sfx 53 | .globl _banjo_update_song, _banjo_update_sfx 54 | .globl _banjo_mute_song_channel, _banjo_unmute_song_channel 55 | .globl _banjo_check_hardware, _banjo_fm_unit_present, _banjo_mode, _banjo_game_gear_mode, _banjo_system_e 56 | .globl _banjo_queue_song_loop_mode, _banjo_queue_sfx_loop_mode 57 | 58 | .globl queue_song, song_playing 59 | .globl queue_sfx, sfx_playing, sfx_priority 60 | 61 | .include "arpeggio.inc" 62 | .include "instrument_change.inc" 63 | .include "music_init.inc" 64 | .include "music_play.inc" 65 | .include "music_queue.inc" 66 | .include "music_stop.inc" 67 | .include "music_update.inc" 68 | .include "mute_unmute.inc" 69 | .include "pitch_slide.inc" 70 | .include "process_channels_tic.inc" 71 | .include "process_new_line.inc" 72 | .include "update_pitch_registers.inc" 73 | .include "vibrato.inc" 74 | .include "volume_macro.inc" 75 | 76 | .include "opll/drum_tables.inc" 77 | .include "opll/note_on_off.inc" 78 | .include "opll/pitch_slide.inc" 79 | .include "opll/vibrato.inc" 80 | .include "opll/volume_change.inc" 81 | .include "opll/fnums_fm.inc" 82 | 83 | .include "sn/note_on_off.inc" 84 | .include "sn/pitch_slide.inc" 85 | .include "sn/vibrato.inc" 86 | .include "sn/volume_change.inc" 87 | .include "sn/fnums_sn.inc" -------------------------------------------------------------------------------- /music_driver/sn/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; SN Pitch Slides 6 | 7 | ; slide type == 1, upward slide 8 | music_update_sn_pitch_slide_upward: 9 | 10 | ; sub slide_amount from freq 11 | ld l, (ix + channel.slide_amount) 12 | ld h, 0 13 | ex de, hl 14 | sbc hl, de 15 | 16 | ; if carry, de has gone < 0, so cap the pitch 17 | jr nc, mu_sn_psu_done 18 | 19 | ld hl, 10 20 | 21 | ; done 22 | mu_sn_psu_done: 23 | ex de, hl 24 | jp music_update_pitch_slide_done 25 | 26 | ; slide type == 2, downward slide 27 | music_update_sn_pitch_slide_downward: 28 | 29 | ; add slide_amount to freq 30 | ld l, (ix + channel.slide_amount) 31 | ld h, 0 32 | add hl, de 33 | 34 | ; check if h < 1024 so has gone beyond the bottom pitch 35 | ld a, h 36 | cp a, 4 37 | jr c, mu_sn_psd_done 38 | 39 | ; cap pitch 40 | ld hl, 1017 41 | 42 | ; done 43 | mu_sn_psd_done: 44 | ex de, hl 45 | jp music_update_pitch_slide_done 46 | 47 | ; handle SN chip portamento 48 | ; ix: current channel 49 | ; iy: music state 50 | ; de: current channel.freq 51 | ; outputs updated freq in de 52 | music_update_sn_portamento: 53 | 54 | ; get target_freq in hl 55 | ld l, (ix + channel.target_freq) 56 | ld h, (ix + channel.target_freq + 1) 57 | 58 | ; subtract current freq from target_freq 59 | sbc hl, de 60 | jr z, musn_porta_end_portamento 61 | jr c, musn_porta_current_higher 62 | 63 | musn_porta_current_lower: 64 | 65 | ; get target_freq back into hl 66 | add hl, de 67 | 68 | ; add slide amount to freq 69 | ld a, (ix + channel.slide_amount) 70 | add a, e 71 | ld e, a 72 | adc a, d 73 | sub a, e 74 | ld d, a 75 | 76 | ; check if we've passed target_freq 77 | ; if z then freq == target and we're done 78 | ; if c then freq > target and we're done 79 | ; if nc then we carry on next loop 80 | sbc hl, de 81 | jr z, musn_porta_end_portamento 82 | jr c, musn_porta_end_portamento 83 | jr nc, musn_porta_done 84 | 85 | musn_porta_current_higher: 86 | 87 | ; get target_freq back into hl 88 | add hl, de 89 | 90 | ; sub slide amount from freq 91 | ld a, e 92 | sub a, (ix + channel.slide_amount) 93 | ld e, a 94 | ld a, d 95 | sbc a, 0 96 | ld d, a 97 | 98 | ; check if we've passed target_freq 99 | ; if z then freq == target and we're done 100 | ; if nc then freq < target and we're done 101 | ; if nc then we carry on next loop 102 | sbc hl, de 103 | jr z, musn_porta_end_portamento 104 | jr nc, musn_porta_end_portamento 105 | jr c, musn_porta_done 106 | 107 | musn_porta_end_portamento: 108 | 109 | ; restore target_freq into hl 110 | ; exhange target_freq into de 111 | add hl, de 112 | ex de, hl 113 | 114 | ; cancel the portamento 115 | ld a, 0 116 | ld (ix + channel.slide_type), a 117 | 118 | musn_porta_done: 119 | 120 | jp music_update_pitch_slide_done -------------------------------------------------------------------------------- /music_driver_sdas/sn/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; SN Pitch Slides 6 | 7 | ; slide type == 1, upward slide 8 | music_update_sn_pitch_slide_upward: 9 | 10 | ; sub slide_amount from freq 11 | ld l, channel.slide_amount(ix) 12 | ld h, #0 13 | ex de, hl 14 | sbc hl, de 15 | 16 | ; if carry, de has gone < 0, so cap the pitch 17 | jr nc, mu_sn_psu_done 18 | 19 | ld hl, #10 20 | 21 | ; done 22 | mu_sn_psu_done: 23 | ex de, hl 24 | jp music_update_pitch_slide_done 25 | 26 | ; slide type == 2, downward slide 27 | music_update_sn_pitch_slide_downward: 28 | 29 | ; add slide_amount to freq 30 | ld l, channel.slide_amount(ix) 31 | ld h, #0 32 | add hl, de 33 | 34 | ; check if h < 1024 so has gone beyond the bottom pitch 35 | ld a, h 36 | cp a, #4 37 | jr c, mu_sn_psd_done 38 | 39 | ; cap pitch 40 | ld hl, #1017 41 | 42 | ; done 43 | mu_sn_psd_done: 44 | ex de, hl 45 | jp music_update_pitch_slide_done 46 | 47 | ; handle SN chip portamento 48 | ; ix: current channel 49 | ; iy: music state 50 | ; de: current channel.freq 51 | ; outputs updated freq in de 52 | music_update_sn_portamento: 53 | 54 | ; get target_freq in hl 55 | ld l, channel.target_freq(ix) 56 | ld h, channel.target_freq+1(ix) 57 | 58 | ; subtract current freq from target_freq 59 | sbc hl, de 60 | jr z, musn_porta_end_portamento 61 | jr c, musn_porta_current_higher 62 | 63 | musn_porta_current_lower: 64 | 65 | ; get target_freq back into hl 66 | add hl, de 67 | 68 | ; add slide amount to freq 69 | ld a, channel.slide_amount(ix) 70 | add a, e 71 | ld e, a 72 | adc a, d 73 | sub a, e 74 | ld d, a 75 | 76 | ; check if we've passed target_freq 77 | ; if z then freq == target and we're done 78 | ; if c then freq > target and we're done 79 | ; if nc then we carry on next loop 80 | sbc hl, de 81 | jr z, musn_porta_end_portamento 82 | jr c, musn_porta_end_portamento 83 | jr nc, musn_porta_done 84 | 85 | musn_porta_current_higher: 86 | 87 | ; get target_freq back into hl 88 | add hl, de 89 | 90 | ; sub slide amount from freq 91 | ld a, e 92 | sub a, channel.slide_amount(ix) 93 | ld e, a 94 | ld a, d 95 | sbc a, #0 96 | ld d, a 97 | 98 | ; check if we've passed target_freq 99 | ; if z then freq == target and we're done 100 | ; if nc then freq < target and we're done 101 | ; if nc then we carry on next loop 102 | sbc hl, de 103 | jr z, musn_porta_end_portamento 104 | jr nc, musn_porta_end_portamento 105 | jr c, musn_porta_done 106 | 107 | musn_porta_end_portamento: 108 | 109 | ; restore target_freq into hl 110 | ; exhange target_freq into de 111 | add hl, de 112 | ex de, hl 113 | 114 | ; cancel the portamento 115 | ld a, #0 116 | ld channel.slide_type(ix), a 117 | 118 | musn_porta_done: 119 | 120 | jp music_update_pitch_slide_done 121 | -------------------------------------------------------------------------------- /music_driver/music_driver.asm: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | .include "defines_wladx.inc" 6 | 7 | .macro SFX_DEF(SFX_LABEL, SFX_PRIORITY) 8 | .db SFX_PRIORITY 9 | .db :SFX_LABEL 10 | .dw SFX_LABEL 11 | .endm 12 | 13 | .macro SONG_DEF(SONG_LABEL) 14 | .db 0 15 | .db :SONG_LABEL 16 | .dw SONG_LABEL 17 | .endm 18 | 19 | ; number of channels to allocate as space for songs 20 | .ifndef BANJO_MODE 21 | .define BANJO_MODE MODE_SN 22 | .endif 23 | 24 | ; FM: 9 channels 25 | .if BANJO_MODE == MODE_FM 26 | 27 | .define CHANNEL_COUNT 9 28 | .define INCLUDE_OPLL 1 29 | 30 | ; SN: 4 channels 31 | .elif BANJO_MODE == MODE_SN 32 | 33 | .define CHANNEL_COUNT 4 34 | 35 | ; Combined SN and FM: 13 channels 36 | .elif BANJO_MODE == MODE_SN_FM 37 | 38 | .define CHANNEL_COUNT 13 39 | .define INCLUDE_OPLL 1 40 | 41 | ; FM drums: 11 channels 42 | .elif BANJO_MODE == MODE_FM_DRUMS 43 | 44 | .define CHANNEL_COUNT 11 45 | .define INCLUDE_OPLL 1 46 | 47 | ; 2x SN: 8 channels 48 | .elif BANJO_MODE == MODE_DUAL_SN 49 | 50 | .define CHANNEL_COUNT 8 51 | 52 | ; Combined SN and FM drums: 15 channels 53 | .elif BANJO_MODE == MODE_SN_FM_DRUMS 54 | 55 | .define CHANNEL_COUNT 15 56 | .define INCLUDE_OPLL 1 57 | 58 | .endif 59 | 60 | 61 | .RAMSECTION "Music Vars" slot 3 62 | 63 | ; console framerate 50/60hz 64 | music_framerate: db 65 | 66 | ; write bytes here to queue up music or sfx 67 | queue_sfx: db 68 | queue_song: db 69 | 70 | ; used to modify the loop type 71 | queue_sfx_loop: db 72 | queue_song_loop: db 73 | 74 | banjo_fm_unit_present: db 75 | banjo_game_gear_mode: db 76 | banjo_system_e: db 77 | banjo_mode: db 78 | 79 | ; state and channel data for song 80 | song_playing: db 81 | song_state: INSTANCEOF music_state 82 | song_channels: INSTANCEOF channel CHANNEL_COUNT 83 | song_channel_ptrs: ds CHANNEL_COUNT * 2 84 | 85 | ; state and channel data for sfx 86 | sfx_playing: db 87 | sfx_priority: db 88 | sfx_state INSTANCEOF music_state 89 | sfx_channel INSTANCEOF channel 90 | 91 | ; pointers to song and sfx tables 92 | song_table_ptr: dw 93 | sfx_table_ptr: dw 94 | 95 | fm_drum_note_ons: db 96 | fm_drum_volumes: ds 3 97 | 98 | .ENDS 99 | 100 | .include "music_init.inc" 101 | .include "music_play.inc" 102 | .include "music_queue.inc" 103 | .include "music_stop.inc" 104 | .include "music_update.inc" 105 | .include "update_pitch_registers.inc" 106 | .include "instrument_change.inc" 107 | .include "process_channels_tic.inc" 108 | .include "process_new_line.inc" 109 | .include "arpeggio.inc" 110 | .include "mute_unmute.inc" 111 | .include "pitch_slide.inc" 112 | .include "vibrato.inc" 113 | .include "volume_macro.inc" 114 | 115 | .include "opll/fnums_fm.inc" 116 | .include "opll/drum_tables.inc" 117 | .include "opll/note_on_off.inc" 118 | .include "opll/pitch_slide.inc" 119 | .include "opll/vibrato.inc" 120 | .include "opll/volume_change.inc" 121 | 122 | .include "sn/fnums_sn.inc" 123 | .include "sn/note_on_off.inc" 124 | .include "sn/pitch_slide.inc" 125 | .include "sn/vibrato.inc" 126 | .include "sn/volume_change.inc" -------------------------------------------------------------------------------- /music_driver/music_queue.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | .ifndef QUEUES_OFF 6 | 7 | ; a: song number to queue 8 | banjo_queue_song: 9 | 10 | ld (queue_song), a 11 | 12 | ; clear loop mode modifier variable 13 | ld a, 0xff 14 | ld (queue_song_loop), a 15 | 16 | ret 17 | 18 | ; a: sfx number to queue 19 | banjo_queue_sfx: 20 | 21 | ld (queue_sfx), a 22 | 23 | ; clear loop mode modifier variable 24 | ld a, 0xff 25 | ld (queue_sfx_loop), a 26 | 27 | ret 28 | 29 | ; a: loop mode for queued sfx 30 | banjo_queue_song_loop_mode: 31 | 32 | ld (queue_song_loop), a 33 | 34 | ret 35 | 36 | ; a: loop mode for queued sfx 37 | banjo_queue_sfx_loop_mode: 38 | 39 | ld (queue_sfx_loop), a 40 | 41 | ret 42 | 43 | music_handle_sfx_queue: 44 | 45 | ld a, (queue_sfx) 46 | 47 | ; get address of queued sfx in table 48 | ; (multiply a by 4 and add to hl) 49 | ld hl, (sfx_table_ptr) 50 | add a, a 51 | add a, a 52 | add a, l 53 | ld l, a 54 | adc a, h 55 | sub a, l 56 | ld h, a 57 | 58 | ; check priority of current sfx vs new priority 59 | ; don't play if if the priority is lower 60 | ld a, (sfx_priority) 61 | ld b, a 62 | ld a, (hl) 63 | cp a, b 64 | jr c, mhsfq_done 65 | 66 | ; update priority variable 67 | ld a, b 68 | ld (sfx_priority), a 69 | 70 | ; get bank and change slot 2 bank to this bank 71 | inc hl 72 | ld a, (hl) 73 | ld (SLOT_2_BANK_CHANGE), a 74 | 75 | ; keep it in c 76 | ld c, a 77 | 78 | ; get address of sfx into hl 79 | inc hl 80 | ld a, (hl) 81 | inc hl 82 | ld h, (hl) 83 | ld l, a 84 | 85 | ; check if it's a valid song from the header 86 | ld a, (hl) 87 | cp a, BANJO_MAGIC_BYTE 88 | jr nz, mhsfq_done 89 | 90 | ld a, (queue_sfx_loop) 91 | ld b, a 92 | ld ix, sfx_channel 93 | ld iy, sfx_state 94 | call music_play 95 | 96 | ; sfx is now playing 97 | ld a, 1 98 | ld (sfx_playing), a 99 | 100 | mhsfq_done: 101 | 102 | ld a, 0xff 103 | ld (queue_sfx), a 104 | 105 | ret 106 | 107 | music_handle_song_queue: 108 | 109 | ; check whether a song has been queued 110 | ld a, (queue_song) 111 | 112 | ; add offset to song table 113 | ld hl, (song_table_ptr) 114 | add a, a 115 | add a, a 116 | add a, l 117 | ld l, a 118 | adc a, h 119 | sub a, l 120 | ld h, a 121 | 122 | ; get bank and change slot 2 bank to this bank 123 | inc hl 124 | ld a, (hl) 125 | ld (SLOT_2_BANK_CHANGE), a 126 | 127 | ; keep it in c 128 | ld c, a 129 | 130 | ; get address of song into hl 131 | inc hl 132 | ld a, (hl) 133 | inc hl 134 | ld h, (hl) 135 | ld l, a 136 | 137 | ; check if it's a valid song from the header 138 | ld a, (hl) 139 | cp a, BANJO_MAGIC_BYTE 140 | jr nz, mhsoq_done 141 | 142 | ; get channnel count 143 | ; abort if this song uses > CHANNEL_COUNT 144 | inc hl 145 | inc hl 146 | ld a, CHANNEL_COUNT 147 | cp a, (hl) 148 | ret c 149 | 150 | ; move pointer back 151 | dec hl 152 | dec hl 153 | 154 | ld a, (queue_song_loop) 155 | ld b, a 156 | ld ix, song_channels 157 | ld iy, song_state 158 | call music_play 159 | 160 | ; song is now playing 161 | ld a, 1 162 | ld (song_playing), a 163 | 164 | mhsoq_done: 165 | ; clear song queue 166 | ld a, 0xff 167 | ld (queue_song), a 168 | 169 | ret 170 | 171 | .else 172 | 173 | ; a: song number to queue 174 | banjo_queue_song: 175 | banjo_queue_sfx: 176 | banjo_queue_song_loop_mode: 177 | banjo_queue_sfx_loop_mode: 178 | music_handle_sfx_queue: 179 | music_handle_song_queue: 180 | ret 181 | 182 | .endif -------------------------------------------------------------------------------- /music_driver_sdas/volume_macro.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_update_volume_macro: 6 | 7 | ; get current pointer to volume macro into hl 8 | ld l, channel.volume_macro_ptr(ix) 9 | ld h, channel.volume_macro_ptr+1(ix) 10 | 11 | ; check if we've reached the end of the macro and do nothing if we have 12 | ld a, channel.volume_macro_pos(ix) 13 | ld c, channel.volume_macro_len(ix) 14 | cp a, c 15 | jr nz, mpct_volume_macro_continue 16 | 17 | ; if loop == 0xff there's no loop so we're done 18 | ld a, channel.volume_macro_loop(ix) 19 | cp a, #0xff 20 | ret z 21 | 22 | ; keep loop value to subtract in c 23 | ld c, a 24 | 25 | ; store new volume macro position 26 | ld a, channel.volume_macro_pos(ix) 27 | sub a, c 28 | ld channel.volume_macro_pos(ix), a 29 | 30 | ; get lower byte of volume macro pointer in a 31 | ld a, l 32 | 33 | ; subtract loop point from current pointer 34 | ; to get new volume_macro_ptr 35 | sub a, c 36 | ld l, a 37 | ld a, h 38 | sbc a, #0 39 | ld h, a 40 | 41 | mpct_volume_macro_continue: 42 | 43 | ; load new volume level into d 44 | ld d, (hl) 45 | 46 | ; check channel type 47 | ld a, channel.type(ix) 48 | 49 | ; TODO: apply volume macro to drums 50 | cp a, #CHAN_OPLL_DRUMS 51 | jr z, mpct_volume_macro_done 52 | 53 | ; set SN channel volume 54 | cp a, #CHAN_SN76489 55 | jr nz, mpct_opll_channel 56 | 57 | ; add channel volume and macro volume together 58 | ld a, channel.volume(ix) 59 | and a, #0xf 60 | add a, d 61 | cp a, #0x10 62 | jr c, mpct_sn_macro_add_done 63 | 64 | ; max out volume value at 0xf 65 | ld a, #0x0f 66 | 67 | mpct_sn_macro_add_done: 68 | 69 | ; get channel (pre-shifted into correct position) 70 | or a, channel.subchannel(ix) 71 | 72 | ; set latch and volume bit 73 | or a, #0x90 74 | 75 | ; load the port into c 76 | ld c, channel.port(ix) 77 | ; update chip 78 | out (c), a 79 | 80 | jr mpct_volume_macro_done 81 | 82 | ; set OPLL channel volume 83 | mpct_opll_channel: 84 | cp a, #CHAN_OPLL 85 | jr nz, mpct_volume_macro_done 86 | 87 | ; choose volume register 88 | ld a, channel.subchannel(ix) 89 | add a, #0x30 90 | out (#OPLL_REG_PORT), a 91 | 92 | ; add channel volume and macro volume together 93 | ld a, channel.volume(ix) 94 | and a, #0xf 95 | add a, d 96 | cp a, #0x10 97 | jr c, mpct_fm_macro_add_done 98 | 99 | ; max out volume value at 0xf 100 | ld a, #0x0f 101 | 102 | mpct_fm_macro_add_done: 103 | 104 | ; get current patch which is the upper nibble of the volume reg 105 | ; and combine with new volume level 106 | or a, channel.fm_patch_shifted(ix) 107 | 108 | ; write new value 109 | out (#OPLL_DATA_PORT), a 110 | 111 | mpct_volume_macro_done: 112 | 113 | ; move volume macro position along and save it 114 | inc channel.volume_macro_pos(ix) 115 | 116 | ; move volume macro pointer along and save it 117 | inc hl 118 | ld channel.volume_macro_ptr(ix), l 119 | ld channel.volume_macro_ptr+1(ix), h 120 | 121 | ret 122 | -------------------------------------------------------------------------------- /music_driver/sn/fnums_sn.inc: -------------------------------------------------------------------------------- 1 | sn_tone_lookup: 2 | ; midi: C2, fur: C0 3 | .dw 1023 4 | ; midi: C#2, fur: C#0 5 | .dw 1023 6 | ; midi: D2, fur: D0 7 | .dw 1023 8 | ; midi: D#2, fur: D#0 9 | .dw 1023 10 | ; midi: E2, fur: E0 11 | .dw 1023 12 | ; midi: F2, fur: F0 13 | .dw 1023 14 | ; midi: F#2, fur: F#0 15 | .dw 1023 16 | ; midi: G2, fur: G0 17 | .dw 1023 18 | ; midi: G#2, fur: G#0 19 | .dw 1023 20 | ; midi: A2, fur: A0 21 | .dw 1017 22 | ; midi: A#2, fur: A#0 23 | .dw 960 24 | ; midi: B2, fur: B0 25 | .dw 906 26 | ; midi: C3, fur: C1 27 | .dw 855 28 | ; midi: C#3, fur: C#1 29 | .dw 807 30 | ; midi: D3, fur: D1 31 | .dw 762 32 | ; midi: D#3, fur: D#1 33 | .dw 719 34 | ; midi: E3, fur: E1 35 | .dw 679 36 | ; midi: F3, fur: F1 37 | .dw 641 38 | ; midi: F#3, fur: F#1 39 | .dw 605 40 | ; midi: G3, fur: G1 41 | .dw 571 42 | ; midi: G#3, fur: G#1 43 | .dw 539 44 | ; midi: A3, fur: A1 45 | .dw 508 46 | ; midi: A#3, fur: A#1 47 | .dw 480 48 | ; midi: B3, fur: B1 49 | .dw 453 50 | ; midi: C4, fur: C2 51 | .dw 428 52 | ; midi: C#4, fur: C#2 53 | .dw 404 54 | ; midi: D4, fur: D2 55 | .dw 381 56 | ; midi: D#4, fur: D#2 57 | .dw 360 58 | ; midi: E4, fur: E2 59 | .dw 339 60 | ; midi: F4, fur: F2 61 | .dw 320 62 | ; midi: F#4, fur: F#2 63 | .dw 302 64 | ; midi: G4, fur: G2 65 | .dw 285 66 | ; midi: G#4, fur: G#2 67 | .dw 269 68 | ; midi: A4, fur: A2 69 | .dw 254 70 | ; midi: A#4, fur: A#2 71 | .dw 240 72 | ; midi: B4, fur: B2 73 | .dw 226 74 | ; midi: C5, fur: C3 75 | .dw 214 76 | ; midi: C#5, fur: C#3 77 | .dw 202 78 | ; midi: D5, fur: D3 79 | .dw 190 80 | ; midi: D#5, fur: D#3 81 | .dw 180 82 | ; midi: E5, fur: E3 83 | .dw 170 84 | ; midi: F5, fur: F3 85 | .dw 160 86 | ; midi: F#5, fur: F#3 87 | .dw 151 88 | ; midi: G5, fur: G3 89 | .dw 143 90 | ; midi: G#5, fur: G#3 91 | .dw 135 92 | ; midi: A5, fur: A3 93 | .dw 127 94 | ; midi: A#5, fur: A#3 95 | .dw 120 96 | ; midi: B5, fur: B3 97 | .dw 113 98 | ; midi: C6, fur: C4 99 | .dw 107 100 | ; midi: C#6, fur: C#4 101 | .dw 101 102 | ; midi: D6, fur: D4 103 | .dw 95 104 | ; midi: D#6, fur: D#4 105 | .dw 90 106 | ; midi: E6, fur: E4 107 | .dw 85 108 | ; midi: F6, fur: F4 109 | .dw 80 110 | ; midi: F#6, fur: F#4 111 | .dw 76 112 | ; midi: G6, fur: G4 113 | .dw 71 114 | ; midi: G#6, fur: G#4 115 | .dw 67 116 | ; midi: A6, fur: A4 117 | .dw 64 118 | ; midi: A#6, fur: A#4 119 | .dw 60 120 | ; midi: B6, fur: B4 121 | .dw 57 122 | ; midi: C7, fur: C5 123 | .dw 53 124 | ; midi: C#7, fur: C#5 125 | .dw 50 126 | ; midi: D7, fur: D5 127 | .dw 48 128 | ; midi: D#7, fur: D#5 129 | .dw 45 130 | ; midi: E7, fur: E5 131 | .dw 42 132 | ; midi: F7, fur: F5 133 | .dw 40 134 | ; midi: F#7, fur: F#5 135 | .dw 38 136 | ; midi: G7, fur: G5 137 | .dw 36 138 | ; midi: G#7, fur: G#5 139 | .dw 34 140 | ; midi: A7, fur: A5 141 | .dw 32 142 | ; midi: A#7, fur: A#5 143 | .dw 30 144 | ; midi: B7, fur: B5 145 | .dw 28 146 | ; midi: C8, fur: C6 147 | .dw 27 148 | ; midi: C#8, fur: C#6 149 | .dw 25 150 | ; midi: D8, fur: D6 151 | .dw 24 152 | ; midi: D#8, fur: D#6 153 | .dw 22 154 | ; midi: E8, fur: E6 155 | .dw 21 156 | ; midi: F8, fur: F6 157 | .dw 20 158 | ; midi: F#8, fur: F#6 159 | .dw 19 160 | ; midi: G8, fur: G6 161 | .dw 18 162 | ; midi: G#8, fur: G#6 163 | .dw 17 164 | ; midi: A8, fur: A6 165 | .dw 16 166 | ; midi: A#8, fur: A#6 167 | .dw 15 168 | ; midi: B8, fur: B6 169 | .dw 14 170 | ; midi: C9, fur: C7 171 | .dw 13 172 | ; midi: C#9, fur: C#7 173 | .dw 13 174 | ; midi: D9, fur: D7 175 | .dw 12 176 | ; midi: D#9, fur: D#7 177 | .dw 11 178 | ; midi: E9, fur: E7 179 | .dw 11 180 | ; midi: F9, fur: F7 181 | .dw 10 182 | ; midi: F#9, fur: F#7 183 | .dw 9 184 | ; midi: G9, fur: G7 185 | .dw 9 186 | -------------------------------------------------------------------------------- /music_driver_sdas/sn/fnums_sn.inc: -------------------------------------------------------------------------------- 1 | sn_tone_lookup: 2 | ; midi: C2, fur: C0 3 | .dw 1023 4 | ; midi: C#2, fur: C#0 5 | .dw 1023 6 | ; midi: D2, fur: D0 7 | .dw 1023 8 | ; midi: D#2, fur: D#0 9 | .dw 1023 10 | ; midi: E2, fur: E0 11 | .dw 1023 12 | ; midi: F2, fur: F0 13 | .dw 1023 14 | ; midi: F#2, fur: F#0 15 | .dw 1023 16 | ; midi: G2, fur: G0 17 | .dw 1023 18 | ; midi: G#2, fur: G#0 19 | .dw 1023 20 | ; midi: A2, fur: A0 21 | .dw 1017 22 | ; midi: A#2, fur: A#0 23 | .dw 960 24 | ; midi: B2, fur: B0 25 | .dw 906 26 | ; midi: C3, fur: C1 27 | .dw 855 28 | ; midi: C#3, fur: C#1 29 | .dw 807 30 | ; midi: D3, fur: D1 31 | .dw 762 32 | ; midi: D#3, fur: D#1 33 | .dw 719 34 | ; midi: E3, fur: E1 35 | .dw 679 36 | ; midi: F3, fur: F1 37 | .dw 641 38 | ; midi: F#3, fur: F#1 39 | .dw 605 40 | ; midi: G3, fur: G1 41 | .dw 571 42 | ; midi: G#3, fur: G#1 43 | .dw 539 44 | ; midi: A3, fur: A1 45 | .dw 508 46 | ; midi: A#3, fur: A#1 47 | .dw 480 48 | ; midi: B3, fur: B1 49 | .dw 453 50 | ; midi: C4, fur: C2 51 | .dw 428 52 | ; midi: C#4, fur: C#2 53 | .dw 404 54 | ; midi: D4, fur: D2 55 | .dw 381 56 | ; midi: D#4, fur: D#2 57 | .dw 360 58 | ; midi: E4, fur: E2 59 | .dw 339 60 | ; midi: F4, fur: F2 61 | .dw 320 62 | ; midi: F#4, fur: F#2 63 | .dw 302 64 | ; midi: G4, fur: G2 65 | .dw 285 66 | ; midi: G#4, fur: G#2 67 | .dw 269 68 | ; midi: A4, fur: A2 69 | .dw 254 70 | ; midi: A#4, fur: A#2 71 | .dw 240 72 | ; midi: B4, fur: B2 73 | .dw 226 74 | ; midi: C5, fur: C3 75 | .dw 214 76 | ; midi: C#5, fur: C#3 77 | .dw 202 78 | ; midi: D5, fur: D3 79 | .dw 190 80 | ; midi: D#5, fur: D#3 81 | .dw 180 82 | ; midi: E5, fur: E3 83 | .dw 170 84 | ; midi: F5, fur: F3 85 | .dw 160 86 | ; midi: F#5, fur: F#3 87 | .dw 151 88 | ; midi: G5, fur: G3 89 | .dw 143 90 | ; midi: G#5, fur: G#3 91 | .dw 135 92 | ; midi: A5, fur: A3 93 | .dw 127 94 | ; midi: A#5, fur: A#3 95 | .dw 120 96 | ; midi: B5, fur: B3 97 | .dw 113 98 | ; midi: C6, fur: C4 99 | .dw 107 100 | ; midi: C#6, fur: C#4 101 | .dw 101 102 | ; midi: D6, fur: D4 103 | .dw 95 104 | ; midi: D#6, fur: D#4 105 | .dw 90 106 | ; midi: E6, fur: E4 107 | .dw 85 108 | ; midi: F6, fur: F4 109 | .dw 80 110 | ; midi: F#6, fur: F#4 111 | .dw 76 112 | ; midi: G6, fur: G4 113 | .dw 71 114 | ; midi: G#6, fur: G#4 115 | .dw 67 116 | ; midi: A6, fur: A4 117 | .dw 64 118 | ; midi: A#6, fur: A#4 119 | .dw 60 120 | ; midi: B6, fur: B4 121 | .dw 57 122 | ; midi: C7, fur: C5 123 | .dw 53 124 | ; midi: C#7, fur: C#5 125 | .dw 50 126 | ; midi: D7, fur: D5 127 | .dw 48 128 | ; midi: D#7, fur: D#5 129 | .dw 45 130 | ; midi: E7, fur: E5 131 | .dw 42 132 | ; midi: F7, fur: F5 133 | .dw 40 134 | ; midi: F#7, fur: F#5 135 | .dw 38 136 | ; midi: G7, fur: G5 137 | .dw 36 138 | ; midi: G#7, fur: G#5 139 | .dw 34 140 | ; midi: A7, fur: A5 141 | .dw 32 142 | ; midi: A#7, fur: A#5 143 | .dw 30 144 | ; midi: B7, fur: B5 145 | .dw 28 146 | ; midi: C8, fur: C6 147 | .dw 27 148 | ; midi: C#8, fur: C#6 149 | .dw 25 150 | ; midi: D8, fur: D6 151 | .dw 24 152 | ; midi: D#8, fur: D#6 153 | .dw 22 154 | ; midi: E8, fur: E6 155 | .dw 21 156 | ; midi: F8, fur: F6 157 | .dw 20 158 | ; midi: F#8, fur: F#6 159 | .dw 19 160 | ; midi: G8, fur: G6 161 | .dw 18 162 | ; midi: G#8, fur: G#6 163 | .dw 17 164 | ; midi: A8, fur: A6 165 | .dw 16 166 | ; midi: A#8, fur: A#6 167 | .dw 15 168 | ; midi: B8, fur: B6 169 | .dw 14 170 | ; midi: C9, fur: C7 171 | .dw 13 172 | ; midi: C#9, fur: C#7 173 | .dw 13 174 | ; midi: D9, fur: D7 175 | .dw 12 176 | ; midi: D#9, fur: D#7 177 | .dw 11 178 | ; midi: E9, fur: E7 179 | .dw 11 180 | ; midi: F9, fur: F7 181 | .dw 10 182 | ; midi: F#9, fur: F#7 183 | .dw 9 184 | ; midi: G9, fur: G7 185 | .dw 9 186 | -------------------------------------------------------------------------------- /music_driver/volume_macro.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | music_update_volume_macro: 6 | 7 | ; get current pointer to volume macro into hl 8 | ld l, (ix + channel.volume_macro_ptr) 9 | ld h, (ix + channel.volume_macro_ptr + 1) 10 | 11 | ; check if we've reached the end of the macro and do nothing if we have 12 | ld a, (ix + channel.volume_macro_pos) 13 | ld c, (ix + channel.volume_macro_len) 14 | cp a, c 15 | jr nz, mpct_volume_macro_continue 16 | 17 | ; if loop == 0xff there's no loop so we're done 18 | ld a, (ix + channel.volume_macro_loop) 19 | cp a, 0xff 20 | ret z 21 | 22 | ; keep loop value to subtract in c 23 | ld c, a 24 | 25 | ; store new volume macro position 26 | ld a, (ix + channel.volume_macro_pos) 27 | sub a, c 28 | ld (ix + channel.volume_macro_pos), a 29 | 30 | ; get lower byte of volume macro pointer in a 31 | ld a, l 32 | 33 | ; subtract loop point from current pointer 34 | ; to get new volume_macro_ptr 35 | sub a, c 36 | ld l, a 37 | ld a, h 38 | sbc a, 0 39 | ld h, a 40 | 41 | mpct_volume_macro_continue: 42 | 43 | ; load new volume level into d 44 | ld d, (hl) 45 | 46 | ; check channel type 47 | ld a, (ix + channel.type) 48 | 49 | ; TODO: apply volume macro to drums 50 | cp a, CHAN_OPLL_DRUMS 51 | jr z, mpct_volume_macro_done 52 | 53 | ; set SN channel volume 54 | cp a, CHAN_SN76489 55 | jr nz, mpct_opll_channel 56 | 57 | ; add channel volume and macro volume together 58 | ld a, (ix + channel.volume) 59 | and a, 0xf 60 | add a, d 61 | cp a, 0x10 62 | jr c, mpct_sn_macro_add_done 63 | 64 | ; max out volume value at 0xf 65 | ld a, 0x0f 66 | 67 | mpct_sn_macro_add_done: 68 | 69 | ; get channel (pre-shifted into correct position) 70 | or a, (ix + channel.subchannel) 71 | 72 | ; set latch and volume bit 73 | or a, 0x90 74 | 75 | ; load the port into c 76 | ld c, (ix + channel.port) 77 | ; update chip 78 | out (c), a 79 | 80 | jr mpct_volume_macro_done 81 | 82 | ; set OPLL channel volume 83 | mpct_opll_channel: 84 | cp a, CHAN_OPLL 85 | jr nz, mpct_volume_macro_done 86 | 87 | ; choose volume register 88 | ld a, (ix + channel.subchannel) 89 | add a, 0x30 90 | out (OPLL_REG_PORT), a 91 | 92 | ; add channel volume and macro volume together 93 | ld a, (ix + channel.volume) 94 | and a, 0xf 95 | add a, d 96 | cp a, 0x10 97 | jr c, mpct_fm_macro_add_done 98 | 99 | ; max out volume value at 0xf 100 | ld a, 0x0f 101 | 102 | mpct_fm_macro_add_done: 103 | 104 | ; get current patch which is the upper nibble of the volume reg 105 | ; and combine with new volume level 106 | or a, (ix + channel.fm_patch_shifted) 107 | 108 | ; write new value 109 | out (OPLL_DATA_PORT), a 110 | 111 | mpct_volume_macro_done: 112 | 113 | ; move volume macro position along and save it 114 | inc (ix + channel.volume_macro_pos) 115 | 116 | ; move volume macro pointer along and save it 117 | inc hl 118 | ld (ix + channel.volume_macro_ptr), l 119 | ld (ix + channel.volume_macro_ptr + 1), h 120 | 121 | ret -------------------------------------------------------------------------------- /music_driver_sdas/music_queue.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | .ifndef QUEUES_OFF 6 | 7 | ; a: song number to queue 8 | _banjo_queue_song: 9 | 10 | ld (queue_song), a 11 | 12 | ; clear loop mode modifier variable 13 | ld a, #0xff 14 | ld (queue_song_loop), a 15 | 16 | ret 17 | 18 | ; a: sfx number to queue 19 | _banjo_queue_sfx: 20 | 21 | ld (queue_sfx), a 22 | 23 | ; clear loop mode modifier variable 24 | ld a, #0xff 25 | ld (queue_sfx_loop), a 26 | 27 | ret 28 | 29 | ; a: loop mode for queued sfx 30 | _banjo_queue_song_loop_mode: 31 | 32 | ld (queue_song_loop), a 33 | 34 | ret 35 | 36 | ; a: loop mode for queued sfx 37 | _banjo_queue_sfx_loop_mode: 38 | 39 | ld (queue_sfx_loop), a 40 | 41 | ret 42 | 43 | music_handle_sfx_queue: 44 | 45 | ld a, (queue_sfx) 46 | 47 | ; get address of queued sfx in table 48 | ; (multiply a by 4 and add to hl) 49 | ld hl, (sfx_table_ptr) 50 | add a, a 51 | add a, a 52 | add a, l 53 | ld l, a 54 | adc a, h 55 | sub a, l 56 | ld h, a 57 | 58 | ; check priority of current sfx vs new priority 59 | ; don't play if if the priority is lower 60 | ld a, (sfx_priority) 61 | ld b, a 62 | ld a, (hl) 63 | cp a, b 64 | jr c, mhsfq_done 65 | 66 | ; update priority variable 67 | ld a, b 68 | ld (sfx_priority), a 69 | 70 | ; get bank and change slot 2 bank to this bank 71 | inc hl 72 | ld a, (hl) 73 | ld (SLOT_2_BANK_CHANGE), a 74 | 75 | ; keep it in c 76 | ld c, a 77 | 78 | ; get address of sfx into hl 79 | inc hl 80 | ld a, (hl) 81 | inc hl 82 | ld h, (hl) 83 | ld l, a 84 | 85 | ; check if it's a valid song from the header 86 | ld a, (hl) 87 | cp a, #BANJO_MAGIC_BYTE 88 | jr nz, mhsfq_done 89 | 90 | ld a, (queue_sfx_loop) 91 | ld b, a 92 | ld ix, #_sfx_channel 93 | ld iy, #_sfx_state 94 | call music_play 95 | 96 | ; sfx is now playing 97 | ld a, #1 98 | ld (sfx_playing), a 99 | 100 | mhsfq_done: 101 | 102 | ld a, #0xff 103 | ld (queue_sfx), a 104 | 105 | ret 106 | 107 | music_handle_song_queue: 108 | 109 | ; check whether a song has been queued 110 | ld a, (queue_song) 111 | 112 | ; add offset to song table 113 | ld hl, (song_table_ptr) 114 | add a, a 115 | add a, a 116 | add a, l 117 | ld l, a 118 | adc a, h 119 | sub a, l 120 | ld h, a 121 | 122 | ; get bank and change slot 2 bank to this bank 123 | inc hl 124 | ld a, (hl) 125 | ld (SLOT_2_BANK_CHANGE), a 126 | 127 | ; keep it in c 128 | ld c, a 129 | 130 | ; get address of song into hl 131 | inc hl 132 | ld a, (hl) 133 | inc hl 134 | ld h, (hl) 135 | ld l, a 136 | 137 | ; check if it's a valid song from the header 138 | ld a, (hl) 139 | cp a, #BANJO_MAGIC_BYTE 140 | jr nz, mhsoq_done 141 | 142 | ; get channnel count 143 | ; abort if this song uses > CHANNEL_COUNT 144 | inc hl 145 | inc hl 146 | ld a, #CHANNEL_COUNT 147 | cp a, (hl) 148 | ret c 149 | 150 | ; move pointer back 151 | dec hl 152 | dec hl 153 | 154 | ld a, (queue_song_loop) 155 | ld b, a 156 | ld ix, #_song_channels 157 | ld iy, #_song_state 158 | call music_play 159 | 160 | ; song is now playing 161 | ld a, #1 162 | ld (song_playing), a 163 | 164 | mhsoq_done: 165 | ; clear song queue 166 | ld a, #0xff 167 | ld (queue_song), a 168 | 169 | ret 170 | 171 | .else 172 | 173 | ; a: song number to queue 174 | _banjo_queue_song: 175 | _banjo_queue_sfx: 176 | _banjo_queue_song_loop_mode: 177 | _banjo_queue_sfx_loop_mode: 178 | music_handle_sfx_queue: 179 | music_handle_song_queue: 180 | ret 181 | 182 | .endif 183 | -------------------------------------------------------------------------------- /music_driver/defines_wladx.inc: -------------------------------------------------------------------------------- 1 | .define NOTE_ON 0x0 2 | .define NOTE_OFF 0x1 3 | .define INSTRUMENT_CHANGE 0x2 4 | .define VOLUME_CHANGE 0x3 5 | .define FM_DRUM 0x4 6 | .define SN_NOISE_MODE 0x5 7 | .define SLIDE_UP 0x6 8 | .define SLIDE_DOWN 0x7 9 | .define SLIDE_PORTA 0x8 10 | .define SLIDE_OFF 0x9 11 | .define ARPEGGIO 0xa 12 | .define ARPEGGIO_OFF 0xb 13 | .define VIBRATO 0xc 14 | .define VIBRATO_OFF 0xd 15 | .define LEGATO_ON 0xe 16 | .define LEGATO_OFF 0xf 17 | .define GAME_GEAR_PAN 0x10 18 | .define END_LINE 0x80 19 | 20 | .define GAME_GEAR_PORT_0 0x0 21 | .define GAME_GEAR_PAN_PORT 0x6 22 | .define SN76489_PORT 0x7f 23 | .define SN76489_2_PORT 0x7b 24 | .define OPLL_REG_PORT 0xf0 25 | .define OPLL_DATA_PORT 0xf1 26 | .define AUDIO_CONTROL_PORT 0xf2 27 | 28 | .define CHAN_SN76489 0x0 29 | .define CHAN_OPLL 0x1 30 | .define CHAN_OPLL_DRUMS 0x2 31 | 32 | .define SLIDE_TYPE_NONE 0x0 33 | .define SLIDE_TYPE_UP 0x1 34 | .define SLIDE_TYPE_DOWN 0x2 35 | .define SLIDE_TYPE_PORTA 0x3 36 | 37 | .define CHAN_FLAG_MUTED 0x1 38 | .define CHAN_FLAG_NOTE_ON 0x2 39 | .define CHAN_FLAG_LEGATO 0x4 40 | .define CHAN_FLAG_PITCH_CHANGED 0x8 41 | .define CHAN_FLAG_VOLUME_MACRO 0x10 42 | .define CHAN_FLAG_VIBRATO 0x20 43 | .define CHAN_FLAG_ARPEGGIO 0x40 44 | .define CHAN_FLAG_SLIDE 0x80 45 | 46 | .define CHAN_FLAG_BIT_MUTED 0x0 47 | .define CHAN_FLAG_BIT_NOTE_ON 0x1 48 | .define CHAN_FLAG_BIT_LEGATO 0x2 49 | .define CHAN_FLAG_BIT_PITCH_CHANGED 0x3 50 | .define CHAN_FLAG_BIT_VOLUME_MACRO 0x4 51 | .define CHAN_FLAG_BIT_VIBRATO 0x5 52 | .define CHAN_FLAG_BIT_ARPEGGIO 0x6 53 | .define CHAN_FLAG_BIT_SLIDE 0x7 54 | 55 | .define BANJO_MAGIC_BYTE 0xba 56 | 57 | .define SLOT_2_BANK_CHANGE 0xffff 58 | 59 | .define MODE_FM 0x1 60 | .define MODE_SN 0x2 61 | .define MODE_SN_FM 0x3 62 | .define MODE_FM_DRUMS 0x5 63 | .define MODE_DUAL_SN 0x6 64 | .define MODE_SN_FM_DRUMS 0x7 65 | 66 | .define VIBRATO_CENTRE 0x40 67 | 68 | .STRUCT instrument 69 | volume_macro_len: db 70 | volume_macro_loop: db 71 | volume_macro_ptr: dw 72 | fm_preset: db 73 | fm_patch: ds 8 74 | padding: ds 2 75 | .ENDST 76 | 77 | .STRUCT channel 78 | flags: db ; 1: muted - 2: note on - 4: legato - 8: pitch changed 79 | type: db ; type of chip 80 | subchannel: db ; channel number within the chip 81 | port: db ; output port for multiples of SN chip 82 | freq: dw ; current fnum/tone of the voice 83 | target_freq: dw ; target fnum/tone used for portamento 84 | volume: db 85 | midi_note: db 86 | instrument_num: db 87 | slide_amount: db ; how much to add/subtract per tic 88 | slide_type: db ; type of slide (up/down/portamento) 89 | vibrato_current: db 90 | vibrato_target: db 91 | vibrato_counter: db 92 | vibrato_counter_add: db 93 | arpeggio_pos: db 94 | arpeggio: db 95 | order_table_ptr: dw ; pointer to the current order 96 | pattern_ptr: dw ; pointer to the current pattern 97 | line_wait: db ; wait for this many lines 98 | volume_macro_len: db 99 | volume_macro_pos: db 100 | volume_macro_loop: db 101 | volume_macro_ptr: dw 102 | fm_patch_shifted: db 103 | fm_drum_trigger: db 104 | fm_drum_volume_mask: db 105 | .ENDST 106 | 107 | .STRUCT music_state 108 | magic_byte: db 109 | bank: db 110 | channel_count: db 111 | loop: db 112 | sfx_channel: db 113 | has_sn: db 114 | has_fm: db 115 | has_fm_drums: db 116 | time_base: db 117 | speed_1: db 118 | speed_2: db 119 | pattern_length: db 120 | orders_length: db 121 | instrument_ptrs: dw 122 | order_ptrs: dw 123 | subtic: db 124 | tic: db 125 | line: db 126 | order: db 127 | process_new_line: db 128 | noise_mode: db 129 | panning: db 130 | channel_types: ds 32 131 | .ENDST 132 | 133 | -------------------------------------------------------------------------------- /examples/visualiser/main.c: -------------------------------------------------------------------------------- 1 | #include "SMSlib.h" 2 | 3 | #include "banjo.h" 4 | #include "song_table.h" 5 | 6 | #include "bank2.h" 7 | #include "key_x_tile.h" 8 | 9 | unsigned char tic; 10 | const unsigned char channel_lit_tiles[16] = {0, 8, 16, 24, 32, 40, 48, 0, 8, 16, 24, 32, 40, 48, 0, 8}; 11 | 12 | void draw_logo(unsigned char tile_x, unsigned char tile_y) 13 | { 14 | unsigned char i; 15 | 16 | // draw top row of piano key tiles 17 | SMS_setNextTileatXY(tile_x, tile_y); 18 | 19 | for (i = 0; i < 6; i++) 20 | { 21 | SMS_setTile(i + 32); 22 | } 23 | 24 | // draw bottom row of piano key tiles 25 | SMS_setNextTileatXY(tile_x, tile_y + 1); 26 | 27 | for (i = 0; i < 6; i++) 28 | { 29 | SMS_setTile(i + 32 + 6); 30 | } 31 | } 32 | 33 | void draw_keyboard(unsigned char tile_y, unsigned char type) 34 | { 35 | unsigned char i; 36 | type = (type * 2) + 4; 37 | 38 | // draw top row of piano key tiles 39 | SMS_setNextTileatXY(0, tile_y); 40 | 41 | SMS_setTile(1); 42 | SMS_setTile(type); 43 | SMS_setTile(type + 1); 44 | 45 | for (i = 0; i < 28; i++) 46 | { 47 | SMS_setTile(2); 48 | } 49 | 50 | SMS_setTile(3); 51 | 52 | // draw top row of piano key tiles 53 | SMS_setNextTileatXY(0, tile_y + 1); 54 | 55 | for (i = 0; i < 32; i++) 56 | { 57 | SMS_setTile((i % 7) + 16); 58 | } 59 | 60 | // draw bottom row of piano key tiles 61 | SMS_setNextTileatXY(0, tile_y + 2); 62 | 63 | for (i = 0; i < 32; i++) 64 | { 65 | SMS_setTile((i % 7) + 16 + 7); 66 | } 67 | } 68 | 69 | void main(void) 70 | { 71 | channel_t *channel; 72 | 73 | unsigned char i, key_lit_tile; 74 | 75 | SMS_VRAMmemsetW(0, 0x0000, 16384); 76 | SMS_setSpriteMode(1); 77 | 78 | SMS_initSprites(); 79 | 80 | SMS_loadTiles(labels_tiles_bin, 1, labels_tiles_bin_size); 81 | SMS_loadTiles(banjo_tiles_bin, 32, banjo_tiles_bin_size); 82 | SMS_loadTiles(keyboard_tiles_bin, 16, keyboard_tiles_bin_size); 83 | SMS_loadTiles(keys_lit_tiles_bin, 256, keys_lit_tiles_bin_size); 84 | SMS_loadBGPalette(palette_bin); 85 | SMS_loadSpritePalette(sprite_palette_bin); 86 | 87 | draw_keyboard(4, 0); 88 | draw_keyboard(8, 1); 89 | draw_keyboard(12, 2); 90 | 91 | draw_logo(1, 1); 92 | 93 | /* Turn on the display */ 94 | SMS_displayOn(); 95 | 96 | // used to check whether the FM unit is present, and whether we're on a Game Gear in Game Gear mode 97 | banjo_check_hardware(); 98 | 99 | // check whether the FM unit is present 100 | // use either the FM or SN song/sfx tables defined in song_tables.h depending on the result 101 | if (banjo_fm_unit_present) 102 | { 103 | banjo_init(MODE_FM); 104 | banjo_set_song_table(song_table_fm); 105 | banjo_set_sfx_table(sfx_table_fm); 106 | } 107 | else 108 | { 109 | banjo_init(MODE_SN); 110 | banjo_set_song_table(song_table_sn); 111 | banjo_set_sfx_table(sfx_table_sn); 112 | } 113 | 114 | // play song 0 115 | banjo_queue_song(0); 116 | 117 | tic = 0; 118 | 119 | for(;;) 120 | { 121 | 122 | SMS_waitForVBlank(); 123 | 124 | SMS_initSprites(); 125 | 126 | // check we're playing a valid song 127 | if (song_state.magic_byte == 0xBA) 128 | { 129 | // go through all song channels 130 | for (i = 0; i < song_state.channel_count; i++) 131 | { 132 | channel = &song_channels[i]; 133 | 134 | // does the channel have a note-on? 135 | if (channel->flags & CHAN_FLAG_NOTE_ON) 136 | { 137 | key_lit_tile = channel_lit_tiles[i]; 138 | 139 | // draw key sprite for this note-on 140 | SMS_addSprite_f(40 + (channel->type * 32), key_x_tile[channel->midi_note] + key_lit_tile); 141 | } 142 | } 143 | } 144 | 145 | SMS_copySpritestoSAT(); 146 | 147 | banjo_update(); 148 | 149 | tic++; 150 | } 151 | } 152 | 153 | SMS_EMBED_SEGA_ROM_HEADER_16KB(9999,0); 154 | SMS_EMBED_SDSC_HEADER_AUTO_DATE_16KB(1,0,"joe k ","banjo test",""); 155 | -------------------------------------------------------------------------------- /music_driver/mute_unmute.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; a: channel to mute 6 | banjo_mute_song_channel: 7 | 8 | ; use a to get an offset into song_channel_ptrs into hl 9 | add a, a 10 | add a, song_channel_ptrs 13 | sub a, l 14 | ld h, a 15 | 16 | ; get actual pointer into channel in hl 17 | ld a, (hl) 18 | inc hl 19 | ld h, (hl) 20 | ld l, a 21 | 22 | ; this falls through! 23 | 24 | ; hl: pointer to channel to mute 25 | music_mute_channel: 26 | 27 | ; mute channel by setting channel.muted = 1 28 | set CHAN_FLAG_BIT_MUTED, (hl) 29 | 30 | ; get chip type 31 | inc hl 32 | ld a, (hl) 33 | 34 | ; SN 35 | cp a, CHAN_SN76489 36 | jr nz, mmc_check_opll 37 | 38 | ; get subchannel 39 | inc hl 40 | ld a, (hl) 41 | or a, 0x9f 42 | 43 | ; get port 44 | inc hl 45 | ld c, (hl) 46 | 47 | ; write to chip 48 | out (c), a 49 | jr mmc_done 50 | 51 | ; FM - mute channel by sending key-off 52 | mmc_check_opll: 53 | cp a, CHAN_OPLL 54 | jr nz, mmc_done 55 | 56 | ; get subchannel 57 | inc hl 58 | ld a, (hl) 59 | 60 | add a, 0x20 61 | out (OPLL_REG_PORT), a 62 | ld a, 0 63 | out (OPLL_DATA_PORT), a 64 | 65 | jr mmc_done 66 | 67 | mmc_done: 68 | ret 69 | 70 | ; a: channel to mute 71 | banjo_unmute_song_channel: 72 | 73 | ; use a to get an offset into song_channel_ptrs into hl 74 | add a, a 75 | add a, song_channel_ptrs 78 | sub a, l 79 | ld h, a 80 | 81 | ; get actual pointer into channel in hl 82 | ld a, (hl) 83 | inc hl 84 | ld h, (hl) 85 | ld l, a 86 | 87 | ; unmute channel by setting channel.muted = 0 88 | res CHAN_FLAG_BIT_MUTED, (hl) 89 | 90 | ; get the type of channel 91 | inc hl 92 | ld a, (hl) 93 | 94 | ; if it's an opll channel we want to restore to instrument 95 | cp a, CHAN_OPLL 96 | jr nz, music_unmute_channel_done 97 | 98 | ; preserve ix and iy 99 | push ix 100 | push iy 101 | 102 | ; get channel in ix 103 | dec hl 104 | push hl 105 | pop ix 106 | 107 | ; get song state in iy 108 | ld iy, song_state 109 | 110 | .ifndef QUEUES_OFF 111 | ; change to song bank 112 | ld a, (iy + music_state.bank) 113 | ld (SLOT_2_BANK_CHANGE), a 114 | .endif 115 | 116 | ; get the current instrument number for this channel 117 | ld a, (ix + channel.instrument_num) 118 | call music_instrument_change 119 | 120 | ; restore ix and iy 121 | pop iy 122 | pop ix 123 | 124 | music_unmute_channel_done: 125 | ret 126 | 127 | 128 | ; mute all FM channels by sending key-offs 129 | music_mute_all_fm: 130 | 131 | ld b, 9 132 | ld d, 0x20 133 | 134 | music_mute_fm_noteoff: 135 | 136 | ld a, d 137 | out (OPLL_REG_PORT), a 138 | ld a, 0 139 | out (OPLL_DATA_PORT), a 140 | 141 | ; delay after opll data write 142 | push hl 143 | pop hl 144 | push hl 145 | pop hl 146 | push hl 147 | pop hl 148 | 149 | inc d 150 | djnz music_mute_fm_noteoff 151 | 152 | ret 153 | 154 | ; mute all SN channels 155 | ; writes to both the top SN port for the SMS 156 | ; and the port for the second SN in the System E 157 | ; on SMS this second SN port just goes to the SN again 158 | music_mute_all_sn: 159 | 160 | ; mute all psg channels 161 | ld a, 0x9f | (0 << 5) 162 | out (SN76489_PORT), a 163 | out (SN76489_2_PORT), a 164 | 165 | ld a, 0x9f | (1 << 5) 166 | out (SN76489_PORT), a 167 | out (SN76489_2_PORT), a 168 | 169 | ld a, 0x9f | (2 << 5) 170 | out (SN76489_PORT), a 171 | out (SN76489_2_PORT), a 172 | 173 | ld a, 0x9f | (3 << 5) 174 | out (SN76489_PORT), a 175 | out (SN76489_2_PORT), a 176 | 177 | ret -------------------------------------------------------------------------------- /music_driver_sdas/mute_unmute.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; a: channel to mute 6 | _banjo_mute_song_channel: 7 | 8 | ; use a to get an offset into song_channel_ptrs into hl 9 | add a, a 10 | add a, #song_channel_ptrs 13 | sub a, l 14 | ld h, a 15 | 16 | ; get actual pointer into channel in hl 17 | ld a, (hl) 18 | inc hl 19 | ld h, (hl) 20 | ld l, a 21 | 22 | ; this falls through! 23 | 24 | ; hl: pointer to channel to mute 25 | music_mute_channel: 26 | 27 | ; mute channel by setting channel.muted = 1 28 | set CHAN_FLAG_BIT_MUTED, (hl) 29 | 30 | ; get chip type 31 | inc hl 32 | ld a, (hl) 33 | 34 | ; SN 35 | cp a, #CHAN_SN76489 36 | jr nz, mmc_check_opll 37 | 38 | ; get subchannel 39 | inc hl 40 | ld a, (hl) 41 | or a, #0x9f 42 | 43 | ; get port 44 | inc hl 45 | ld c, (hl) 46 | 47 | ; write to chip 48 | out (c), a 49 | jr mmc_done 50 | 51 | ; FM - mute channel by sending key-off 52 | mmc_check_opll: 53 | cp a, #CHAN_OPLL 54 | jr nz, mmc_done 55 | 56 | ; get subchannel 57 | inc hl 58 | ld a, (hl) 59 | 60 | add a, #0x20 61 | out (#OPLL_REG_PORT), a 62 | ld a, #0 63 | out (#OPLL_DATA_PORT), a 64 | 65 | jr mmc_done 66 | 67 | mmc_done: 68 | ret 69 | 70 | ; a: channel to mute 71 | _banjo_unmute_song_channel: 72 | 73 | ; use a to get an offset into song_channel_ptrs into hl 74 | add a, a 75 | add a, #song_channel_ptrs 78 | sub a, l 79 | ld h, a 80 | 81 | ; get actual pointer into channel in hl 82 | ld a, (hl) 83 | inc hl 84 | ld h, (hl) 85 | ld l, a 86 | 87 | ; unmute channel by setting channel.muted = 0 88 | res CHAN_FLAG_BIT_MUTED, (hl) 89 | 90 | ; get the type of channel 91 | inc hl 92 | ld a, (hl) 93 | 94 | ; if it's an opll channel we want to restore to instrument 95 | cp a, #CHAN_OPLL 96 | jr nz, music_unmute_channel_done 97 | 98 | ; preserve ix and iy 99 | push ix 100 | push iy 101 | 102 | ; get channel in ix 103 | dec hl 104 | push hl 105 | pop ix 106 | 107 | ; get song state in iy 108 | ld iy, #_song_state 109 | 110 | .ifndef QUEUES_OFF 111 | ; change to song bank 112 | ld a, music_state.bank(iy) 113 | ld (SLOT_2_BANK_CHANGE), a 114 | .endif 115 | 116 | ; get the current instrument number for this channel 117 | ld a, channel.instrument_num(ix) 118 | call music_instrument_change 119 | 120 | ; restore ix and iy 121 | pop iy 122 | pop ix 123 | 124 | music_unmute_channel_done: 125 | ret 126 | 127 | 128 | ; mute all FM channels by sending key-offs 129 | music_mute_all_fm: 130 | 131 | ld b, #9 132 | ld d, #0x20 133 | 134 | music_mute_fm_noteoff: 135 | 136 | ld a, d 137 | out (#OPLL_REG_PORT), a 138 | ld a, #0 139 | out (#OPLL_DATA_PORT), a 140 | 141 | ; delay after opll data write 142 | push hl 143 | pop hl 144 | push hl 145 | pop hl 146 | push hl 147 | pop hl 148 | 149 | inc d 150 | djnz music_mute_fm_noteoff 151 | 152 | ret 153 | 154 | ; mute all SN channels 155 | ; writes to both the top SN port for the SMS 156 | ; and the port for the second SN in the System E 157 | ; on SMS this second SN port just goes to the SN again 158 | music_mute_all_sn: 159 | 160 | ; mute all psg channels 161 | ld a, #0x9f|(0<<5) 162 | out (#SN76489_PORT), a 163 | out (#SN76489_2_PORT), a 164 | 165 | ld a, #0x9f|(1<<5) 166 | out (#SN76489_PORT), a 167 | out (#SN76489_2_PORT), a 168 | 169 | ld a, #0x9f|(2<<5) 170 | out (#SN76489_PORT), a 171 | out (#SN76489_2_PORT), a 172 | 173 | ld a, #0x9f|(3<<5) 174 | out (#SN76489_PORT), a 175 | out (#SN76489_2_PORT), a 176 | 177 | ret 178 | -------------------------------------------------------------------------------- /old_js_scripts/sms_sn_fnums_with_fine_pitch.js: -------------------------------------------------------------------------------- 1 | /* 2 | Joe Kennedy 2024 3 | Create asm include of SN76489 fnums and fine pitch offsets 4 | */ 5 | 6 | const fs = require('fs'); 7 | var note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 8 | 9 | // midi note frequencies for all midi notes 10 | 11 | var midi_note_frequencies = []; 12 | 13 | for (var i = 0; i < 128; i++) 14 | { 15 | midi_note_frequencies.push(Math.pow(2.0, (i - 69.0)/12.0) * 440.0); 16 | } 17 | 18 | 19 | // fnum values for the sn chip for the given frequencies 20 | 21 | var sn_fnums = []; 22 | 23 | for (var i = 0; i < 128; i++) 24 | { 25 | sn_fnums.push(3579545.0 / ( 2 * 16 * midi_note_frequencies[i])); 26 | } 27 | 28 | /* 29 | prepare data for the playable range of notes 30 | 31 | range is midi A2 - F9, furnace A0 to F7 32 | total of 80 notes from midi note numbers 45 to 125 33 | 34 | furnace seems to use 32 steps between notes for the fine pitch bend/slide 35 | rather than storing a byte for all 32 steps, we can store 5 bytes 36 | each byte will have a power of two division of the difference between fnums 37 | these will correspond to a bit in the fine pitch amount 38 | 39 | e.g. if the difference between fnums is 30 the bytes will thus contain 40 | 41 | [30/32, 30/16, 30/8, 30/4, 30/2] 42 | [0, 1, 3, 7, 15] 43 | 44 | fnum is 2 bytes, and each component of the diff is 1 byte, for a total of 7 bytes 45 | add 1 byte of padding to get to 8 bytes for easy access 46 | 8 * 80 = 640 byte table 47 | 48 | to get the note in between two notes we then: 49 | 50 | 1) get the starting fnum 51 | 2) look at each bit of the fine pitch (which will be 0-31) and sum the diff value corresponding to each bit 52 | 53 | e.g. going from A2 to A#2 54 | /32 /16 /8 /4 /2 55 | A2 fnum: 1017 diffs: 2, 4, 7, 14, 29 (full diff: 57) 56 | A#2 fnum: 960 diffs: 2, 3, 7, 14, 27 (full diff: 54) 57 | 58 | if the fine pitch is 23, it's 10111 in binary, which corresponds to 59 | 60 | 1 1 1 0 1 61 | 2 4 7 14 29 62 | 63 | 2 + 4 + 7 + 29 64 | 65 | which totals 42 66 | 67 | 3) subtract the sum of those from the fnum (we subtracting because the fnum gets smaller as the pitch gets higher!) 68 | 69 | 1017 - (2 + 4 + 7 + 29) = 975 70 | 71 | */ 72 | 73 | var output = []; 74 | 75 | for (var i = 45; i < 126; i++) 76 | { 77 | var out; 78 | var note_name = note_names[i % 12]; 79 | note_name = "midi: " + note_name + (Math.floor(i / 12) - 1) + ", fur: " + note_name + (Math.floor(i / 12) - 3); 80 | 81 | var diff = Math.round(sn_fnums[i]) - Math.round(sn_fnums[i + 1]); 82 | 83 | // more than 1 difference between fnums 84 | if (diff > 1) 85 | { 86 | out = [ 87 | Math.round(sn_fnums[i]), 88 | Math.round(diff/32), 89 | Math.round(diff/16), 90 | Math.round(diff/8), 91 | Math.round(diff/4), 92 | Math.round(diff/2), 93 | note_name, 94 | ]; 95 | } 96 | // 1 or less difference between fnums, make it so there's no step between notes 97 | // at these sorts of fnums, pitches are pretty out of tune and there's barely any pitch resolution 98 | else 99 | { 100 | out = [ 101 | Math.round(sn_fnums[i]), 102 | 0, 103 | 0, 104 | 0, 105 | 0, 106 | 0, 107 | note_name, 108 | ]; 109 | } 110 | 111 | // this should be very close or equal to the next fnum 112 | // out[6] = out[0] - (out[1] + out[2] + out[3] + out[4] + out[5]); 113 | 114 | output.push(out); 115 | } 116 | 117 | // output an include file with the sn note data 118 | let outfile = fs.openSync("sn_fnums.inc", "w+"); 119 | 120 | for (var i = 0; i < output.length; i++) 121 | { 122 | fs.writeSync(outfile, "; " + output[i][6] + "(" + output[i][0] + ")\n"); 123 | fs.writeSync(outfile, "; fnum " + output[i][0] + "\n"); 124 | fs.writeSync(outfile, ".db " + (output[i][0] & 0xf) + ", " + ((output[i][0] >> 4) & 0x3f) + "\n"); 125 | 126 | fs.writeSync(outfile, ".db "); 127 | 128 | for (var j = 1; j < 6; j ++) 129 | { 130 | fs.writeSync(outfile, output[i][j] + ", "); 131 | } 132 | 133 | fs.writeSync(outfile, "0\n"); 134 | } 135 | 136 | fs.closeSync(outfile); 137 | -------------------------------------------------------------------------------- /music_driver_sdas/defines_sdas.inc: -------------------------------------------------------------------------------- 1 | NOTE_ON .equ 0x0 2 | NOTE_OFF .equ 0x1 3 | INSTRUMENT_CHANGE .equ 0x2 4 | VOLUME_CHANGE .equ 0x3 5 | FM_DRUM .equ 0x4 6 | SN_NOISE_MODE .equ 0x5 7 | SLIDE_UP .equ 0x6 8 | SLIDE_DOWN .equ 0x7 9 | SLIDE_PORTA .equ 0x8 10 | SLIDE_OFF .equ 0x9 11 | ARPEGGIO .equ 0xa 12 | ARPEGGIO_OFF .equ 0xb 13 | VIBRATO .equ 0xc 14 | VIBRATO_OFF .equ 0xd 15 | LEGATO_ON .equ 0xe 16 | LEGATO_OFF .equ 0xf 17 | GAME_GEAR_PAN .equ 0x10 18 | END_LINE .equ 0x80 19 | 20 | GAME_GEAR_PORT_0 .equ 0x0 21 | GAME_GEAR_PAN_PORT .equ 0x6 22 | SN76489_PORT .equ 0x7f 23 | SN76489_2_PORT .equ 0x7b 24 | OPLL_REG_PORT .equ 0xf0 25 | OPLL_DATA_PORT .equ 0xf1 26 | AUDIO_CONTROL_PORT .equ 0xf2 27 | 28 | CHAN_SN76489 .equ 0x0 29 | CHAN_OPLL .equ 0x1 30 | CHAN_OPLL_DRUMS .equ 0x2 31 | 32 | SLIDE_TYPE_NONE .equ 0x0 33 | SLIDE_TYPE_UP .equ 0x1 34 | SLIDE_TYPE_DOWN .equ 0x2 35 | SLIDE_TYPE_PORTA .equ 0x3 36 | 37 | CHAN_FLAG_MUTED .equ 0x1 38 | CHAN_FLAG_NOTE_ON .equ 0x2 39 | CHAN_FLAG_LEGATO .equ 0x4 40 | CHAN_FLAG_PITCH_CHANGED .equ 0x8 41 | CHAN_FLAG_VOLUME_MACRO .equ 0x10 42 | CHAN_FLAG_VIBRATO .equ 0x20 43 | CHAN_FLAG_ARPEGGIO .equ 0x40 44 | CHAN_FLAG_SLIDE .equ 0x80 45 | 46 | CHAN_FLAG_BIT_MUTED .equ 0x0 47 | CHAN_FLAG_BIT_NOTE_ON .equ 0x1 48 | CHAN_FLAG_BIT_LEGATO .equ 0x2 49 | CHAN_FLAG_BIT_PITCH_CHANGED .equ 0x3 50 | CHAN_FLAG_BIT_VOLUME_MACRO .equ 0x4 51 | CHAN_FLAG_BIT_VIBRATO .equ 0x5 52 | CHAN_FLAG_BIT_ARPEGGIO .equ 0x6 53 | CHAN_FLAG_BIT_SLIDE .equ 0x7 54 | 55 | BANJO_MAGIC_BYTE .equ 0xba 56 | 57 | SLOT_2_BANK_CHANGE .equ 0xffff 58 | 59 | MODE_FM .equ 0x1 60 | MODE_SN .equ 0x2 61 | MODE_SN_FM .equ 0x3 62 | MODE_FM_DRUMS .equ 0x5 63 | MODE_DUAL_SN .equ 0x6 64 | MODE_SN_FM_DRUMS .equ 0x7 65 | 66 | VIBRATO_CENTRE .equ 0x40 67 | 68 | instrument.volume_macro_len .equ 0 69 | instrument.volume_macro_loop .equ 1 70 | instrument.volume_macro_ptr .equ 2 71 | instrument.fm_preset .equ 4 72 | instrument.fm_patch .equ 5 73 | instrument.padding .equ 13 74 | _sizeof_instrument .equ 15 75 | 76 | channel.flags .equ 0 ; 1: muted - 2: note on - 4: legato - 8: pitch changed 77 | channel.type .equ 1 ; type of chip 78 | channel.subchannel .equ 2 ; channel number within the chip 79 | channel.port .equ 3 ; output port for multiples of SN chip 80 | channel.freq .equ 4 ; current fnum/tone of the voice 81 | channel.target_freq .equ 6 ; target fnum/tone used for portamento 82 | channel.volume .equ 8 83 | channel.midi_note .equ 9 84 | channel.instrument_num .equ 10 85 | channel.slide_amount .equ 11 ; how much to add/subtract per tic 86 | channel.slide_type .equ 12 ; type of slide (up/down/portamento) 87 | channel.vibrato_current .equ 13 88 | channel.vibrato_target .equ 14 89 | channel.vibrato_counter .equ 15 90 | channel.vibrato_counter_add .equ 16 91 | channel.arpeggio_pos .equ 17 92 | channel.arpeggio .equ 18 93 | channel.order_table_ptr .equ 19 ; pointer to the current order 94 | channel.pattern_ptr .equ 21 ; pointer to the current pattern 95 | channel.line_wait .equ 23 ; wait for this many lines 96 | channel.volume_macro_len .equ 24 97 | channel.volume_macro_pos .equ 25 98 | channel.volume_macro_loop .equ 26 99 | channel.volume_macro_ptr .equ 27 100 | channel.fm_patch_shifted .equ 29 101 | channel.fm_drum_trigger .equ 30 102 | channel.fm_drum_volume_mask .equ 31 103 | _sizeof_channel .equ 32 104 | 105 | music_state.magic_byte .equ 0 106 | music_state.bank .equ 1 107 | music_state.channel_count .equ 2 108 | music_state.loop .equ 3 109 | music_state.sfx_channel .equ 4 110 | music_state.has_sn .equ 5 111 | music_state.has_fm .equ 6 112 | music_state.has_fm_drums .equ 7 113 | music_state.time_base .equ 8 114 | music_state.speed_1 .equ 9 115 | music_state.speed_2 .equ 10 116 | music_state.pattern_length .equ 11 117 | music_state.orders_length .equ 12 118 | music_state.instrument_ptrs .equ 13 119 | music_state.order_ptrs .equ 15 120 | music_state.subtic .equ 17 121 | music_state.tic .equ 18 122 | music_state.line .equ 19 123 | music_state.order .equ 20 124 | music_state.process_new_line .equ 21 125 | music_state.noise_mode .equ 22 126 | music_state.panning .equ 23 127 | music_state.channel_types .equ 24 128 | _sizeof_music_state .equ 56 129 | 130 | -------------------------------------------------------------------------------- /music_driver/music_init.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; see MODE_* definitions for indexes 6 | ; e.g. MODE_FM == 1 == 9 channels 7 | banjo_mode_channel_counts: 8 | .db 0, 9, 4, 13, 8, 11, 0, 15 9 | 10 | ; initialize music system variables 11 | ; a: mode 12 | banjo_init: 13 | 14 | ; preserve the mode in variable and in c 15 | ld (banjo_mode), a 16 | ld c, a 17 | 18 | ; get the number of channels for this mode 19 | ld hl, banjo_mode_channel_counts 20 | add a, l 21 | ld l, a 22 | adc a, h 23 | sub a, l 24 | ld h, a 25 | 26 | ; store it in b 27 | ld b, (hl) 28 | 29 | ; set up array of pointers to song channels 30 | ; initialise pointers to channels 31 | ld de, song_channels 32 | ld hl, song_channel_ptrs 33 | 34 | bi_song_channel_pointer_loop: 35 | 36 | ; write de to (hl) 37 | ld (hl), e 38 | inc hl 39 | ld (hl), d 40 | inc hl 41 | 42 | ; move de to the next channel 43 | ld a, _sizeof_channel 44 | add a, e 45 | ld e, a 46 | adc a, d 47 | sub a, e 48 | ld d, a 49 | 50 | djnz bi_song_channel_pointer_loop 51 | 52 | ; initialise queues 53 | ld a, 0xff 54 | ld (queue_sfx), a 55 | ld (queue_sfx_loop), a 56 | ld (queue_song), a 57 | ld (queue_song_loop), a 58 | 59 | ; no songs or sfx should be playing intially 60 | ld a, 0 61 | ld (song_playing), a 62 | ld (sfx_playing), a 63 | ld (sfx_priority), a 64 | 65 | ; assume the console is at 60hz by default 66 | ld a, 60 67 | ld (music_framerate), a 68 | 69 | ; mute channels depending on the mode 70 | 71 | ; mode bit 0 == 1 means fm 72 | bit 0, c 73 | call nz, music_mute_all_fm 74 | 75 | ; mode bit 1 == 1 means sn 76 | bit 1, c 77 | call nz, music_mute_all_sn 78 | 79 | ; enable/disable the FM and SN depending on the mode 80 | ld a, c 81 | and a, 0x3 82 | out (AUDIO_CONTROL_PORT), a 83 | 84 | ret 85 | 86 | banjo_set_song_table: 87 | ld (song_table_ptr), hl 88 | ret 89 | 90 | banjo_set_sfx_table: 91 | ld (sfx_table_ptr), hl 92 | ret 93 | 94 | banjo_check_hardware: 95 | 96 | ; check for fm unit 97 | 98 | ; get current state of control port and keep in d 99 | in a, (AUDIO_CONTROL_PORT) 100 | and a, 0x3 101 | ld d, a 102 | 103 | ; 4 loops, testing values [3, 2, 1, 0] 104 | ld b, 4 105 | 106 | ; count of correct read/write matches 107 | ld e, 0 108 | 109 | detect_fm_loop: 110 | 111 | ; output 112 | ld a, b 113 | dec a 114 | ld c, a 115 | out (AUDIO_CONTROL_PORT), a 116 | 117 | ; and read it back 118 | in a, (AUDIO_CONTROL_PORT) 119 | and a, 0x3 120 | 121 | ; matched? 122 | cp a, c 123 | jr nz, detect_fm_no_match 124 | 125 | ; increase count 126 | inc e 127 | 128 | detect_fm_no_match: 129 | djnz detect_fm_loop 130 | 131 | ; output original control port state 132 | ld a, d 133 | out (AUDIO_CONTROL_PORT), a 134 | 135 | ; return 1 if the fm unit is present 136 | ; count = 2 implies mark 3 with fm (0, 1 will match) 137 | ; count = 4 implies sms with fm unit (0, 1, 2, 3 will match) 138 | ; counts of 1 or 3 imply no unit 139 | ld a, e 140 | cpl 141 | and a, 0x1 142 | ld (banjo_fm_unit_present), a 143 | 144 | ; check for game gear mode 145 | 146 | ; read from i/o port 0 147 | ; if there's anything in the lowest 6 bits, we're not in game gear mode 148 | ; on sms 1 the port should read 0x78 which is the last byte of the "in a, (c)" mpnl_instruction_check_done 149 | ; on sms 2 the port should read 0xff 150 | ; on game gear bits 0-5 should be empty and bits 6 and 7 depend on region and whether the start button is being held down 151 | 152 | ld c, GAME_GEAR_PORT_0 153 | in a, (c) 154 | 155 | ; isolate lower bits 156 | and a, 0x3f 157 | jr nz, bch_not_game_gear 158 | 159 | ld a, 1 160 | jr bch_done 161 | 162 | bch_not_game_gear: 163 | 164 | ld a, 0 165 | 166 | bch_done: 167 | ld (banjo_game_gear_mode), a 168 | 169 | 170 | ; check for Sega System E by seeing if there's 16kb of RAM 171 | ; rather than 8kb with 0xe000 being a mirror of 0xc000 172 | 173 | ld de, banjo_system_e + 8192 174 | ld hl, banjo_system_e 175 | 176 | ; number of values to write for test 177 | ld b, 8 178 | 179 | ; number of matched mirror read/writes 180 | ld c, 0 181 | 182 | bch_check_system_e: 183 | 184 | ; write b to (banjo_system_e) variable 185 | ld (hl), b 186 | 187 | ; read (banjo_system_e variable + 8192) and compare it 188 | ld a, (de) 189 | cp a, (hl) 190 | jr z, bch_not_mirrored 191 | 192 | ; (banjo_system_e) write != (banjo_system_e variable + 8192) read 193 | inc c 194 | 195 | bch_not_mirrored: 196 | djnz bch_check_system_e 197 | 198 | ; c == 0 if all (banjo_system_e variable + 8192) reads matched the (banjo_system_e) writes 199 | ld a, c 200 | or a, a 201 | jr z, bch_check_system_e_done 202 | 203 | ld a, 1 204 | 205 | bch_check_system_e_done: 206 | 207 | ; update variable with flag 208 | ld (banjo_system_e), a 209 | 210 | ret -------------------------------------------------------------------------------- /music_driver_sdas/music_init.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; see MODE_* definitions for indexes 6 | ; e.g. MODE_FM == 1 == 9 channels 7 | banjo_mode_channel_counts: 8 | .db 0, 9,4,13,8,11,0,15 9 | 10 | ; initialize music system variables 11 | ; a: mode 12 | _banjo_init: 13 | 14 | ; preserve the mode in variable and in c 15 | ld (_banjo_mode), a 16 | ld c, a 17 | 18 | ; get the number of channels for this mode 19 | ld hl, #banjo_mode_channel_counts 20 | add a, l 21 | ld l, a 22 | adc a, h 23 | sub a, l 24 | ld h, a 25 | 26 | ; store it in b 27 | ld b, (hl) 28 | 29 | ; set up array of pointers to song channels 30 | ; initialise pointers to channels 31 | ld de, #_song_channels 32 | ld hl, #song_channel_ptrs 33 | 34 | bi_song_channel_pointer_loop: 35 | 36 | ; write de to (hl) 37 | ld (hl), e 38 | inc hl 39 | ld (hl), d 40 | inc hl 41 | 42 | ; move de to the next channel 43 | ld a, #_sizeof_channel 44 | add a, e 45 | ld e, a 46 | adc a, d 47 | sub a, e 48 | ld d, a 49 | 50 | djnz bi_song_channel_pointer_loop 51 | 52 | ; initialise queues 53 | ld a, #0xff 54 | ld (queue_sfx), a 55 | ld (queue_sfx_loop), a 56 | ld (queue_song), a 57 | ld (queue_song_loop), a 58 | 59 | ; no songs or sfx should be playing intially 60 | ld a, #0 61 | ld (song_playing), a 62 | ld (sfx_playing), a 63 | ld (sfx_priority), a 64 | 65 | ; assume the console is at 60hz by default 66 | ld a, #60 67 | ld (music_framerate), a 68 | 69 | ; mute channels depending on the mode 70 | 71 | ; mode bit 0 == 1 means fm 72 | bit 0, c 73 | call nz, music_mute_all_fm 74 | 75 | ; mode bit 1 == 1 means sn 76 | bit 1, c 77 | call nz, music_mute_all_sn 78 | 79 | ; enable/disable the FM and SN depending on the mode 80 | ld a, c 81 | and a, #0x3 82 | out (#AUDIO_CONTROL_PORT), a 83 | 84 | ret 85 | 86 | _banjo_set_song_table: 87 | ld (song_table_ptr), hl 88 | ret 89 | 90 | _banjo_set_sfx_table: 91 | ld (sfx_table_ptr), hl 92 | ret 93 | 94 | _banjo_check_hardware: 95 | 96 | ; check for fm unit 97 | 98 | ; get current state of control port and keep in d 99 | in a, (#AUDIO_CONTROL_PORT) 100 | and a, #0x3 101 | ld d, a 102 | 103 | ; 4 loops, testing values [3, 2, 1, 0] 104 | ld b, #4 105 | 106 | ; count of correct read/write matches 107 | ld e, #0 108 | 109 | detect_fm_loop: 110 | 111 | ; output 112 | ld a, b 113 | dec a 114 | ld c, a 115 | out (#AUDIO_CONTROL_PORT), a 116 | 117 | ; and read it back 118 | in a, (#AUDIO_CONTROL_PORT) 119 | and a, #0x3 120 | 121 | ; matched? 122 | cp a, c 123 | jr nz, detect_fm_no_match 124 | 125 | ; increase count 126 | inc e 127 | 128 | detect_fm_no_match: 129 | djnz detect_fm_loop 130 | 131 | ; output original control port state 132 | ld a, d 133 | out (#AUDIO_CONTROL_PORT), a 134 | 135 | ; return 1 if the fm unit is present 136 | ; count = 2 implies mark 3 with fm (0, 1 will match) 137 | ; count = 4 implies sms with fm unit (0, 1, 2, 3 will match) 138 | ; counts of 1 or 3 imply no unit 139 | ld a, e 140 | cpl 141 | and a, #0x1 142 | ld (_banjo_fm_unit_present), a 143 | 144 | ; check for game gear mode 145 | 146 | ; read from i/o port 0 147 | ; if there's anything in the lowest 6 bits, we're not in game gear mode 148 | ; on sms 1 the port should read 0x78 which is the last byte of the "in a, (c)" mpnl_instruction_check_done 149 | ; on sms 2 the port should read 0xff 150 | ; on game gear bits 0-5 should be empty and bits 6 and 7 depend on region and whether the start button is being held down 151 | 152 | ld c, #GAME_GEAR_PORT_0 153 | in a, (c) 154 | 155 | ; isolate lower bits 156 | and a, #0x3f 157 | jr nz, bch_not_game_gear 158 | 159 | ld a, #1 160 | jr bch_done 161 | 162 | bch_not_game_gear: 163 | 164 | ld a, #0 165 | 166 | bch_done: 167 | ld (_banjo_game_gear_mode), a 168 | 169 | 170 | ; check for Sega System E by seeing if there's 16kb of RAM 171 | ; rather than 8kb with 0xe000 being a mirror of 0xc000 172 | 173 | ld de, #_banjo_system_e+8192 174 | ld hl, #_banjo_system_e 175 | 176 | ; number of values to write for test 177 | ld b, #8 178 | 179 | ; number of matched mirror read/writes 180 | ld c, #0 181 | 182 | bch_check_system_e: 183 | 184 | ; write b to (banjo_system_e) variable 185 | ld (hl), b 186 | 187 | ; read (banjo_system_e variable + 8192) and compare it 188 | ld a, (de) 189 | cp a, (hl) 190 | jr z, bch_not_mirrored 191 | 192 | ; (banjo_system_e) write != (banjo_system_e variable + 8192) read 193 | inc c 194 | 195 | bch_not_mirrored: 196 | djnz bch_check_system_e 197 | 198 | ; c == 0 if all (banjo_system_e variable + 8192) reads matched the (banjo_system_e) writes 199 | ld a, c 200 | or a, a 201 | jr z, bch_check_system_e_done 202 | 203 | ld a, #1 204 | 205 | bch_check_system_e_done: 206 | 207 | ; update variable with flag 208 | ld (_banjo_system_e), a 209 | 210 | ret 211 | -------------------------------------------------------------------------------- /examples/wladx_no_queue/main.asm: -------------------------------------------------------------------------------- 1 | 2 | .define VDP_CONTROL_PORT 0xbf 3 | .define VDP_DATA_PORT 0xbe 4 | 5 | .define VDP_WRITE_ADDRESS 0x4000 6 | .define VDP_WRITE_CRAM 0xc000 7 | .define VDP_WRITE_REGISTER 0x8000 8 | 9 | 10 | .MEMORYMAP 11 | SLOTSIZE $4000 12 | DEFAULTSLOT 0 13 | SLOT 0 $0000 ; ROM slot 0. 14 | SLOT 1 $4000 ; ROM slot 1. 15 | SLOT 2 $8000 ; ROM slot 2 16 | SLOT 3 $C000 ; RAM 17 | .ENDME 18 | 19 | .ROMBANKMAP 20 | BANKSTOTAL 4 21 | BANKSIZE $4000 22 | BANKS 4 23 | .ENDRO 24 | 25 | .org 0x0000 26 | jp init 27 | 28 | .RAMSECTION "Main Vars" bank 0 slot 3 29 | 30 | tic: db 31 | input_last: db 32 | input_pressed: db 33 | 34 | .ENDS 35 | 36 | .org 0x0038 37 | 38 | vblank_interrupt: 39 | 40 | push af 41 | exx 42 | 43 | ld c, VDP_CONTROL_PORT 44 | 45 | ; acknowledge interrupt 46 | in a, (c) 47 | 48 | exx 49 | pop af 50 | 51 | ei 52 | reti 53 | 54 | .org 0x0066 55 | retn 56 | 57 | 58 | init: 59 | di 60 | im 1 61 | 62 | ld sp, 0xdfff 63 | 64 | ; set vdp mode 4 65 | ld hl, VDP_WRITE_REGISTER | (0 << 8) | 0b00000100 66 | 67 | ld c, VDP_CONTROL_PORT 68 | out (c), l 69 | out (c), h 70 | 71 | ; disable vdp screens for now 72 | ld hl, VDP_WRITE_REGISTER | (1 << 8) | 0b10000000 73 | 74 | ld c, VDP_CONTROL_PORT 75 | out (c), l 76 | out (c), h 77 | 78 | ; set vdp nametable base addresses 79 | ld hl, VDP_WRITE_REGISTER | (2 << 8) | 0xff 80 | 81 | ld c, VDP_CONTROL_PORT 82 | out (c), l 83 | out (c), h 84 | 85 | ; set cram base addresses 86 | ld hl, VDP_WRITE_REGISTER | (3 << 8) | 0xff 87 | 88 | ld c, VDP_CONTROL_PORT 89 | out (c), l 90 | out (c), h 91 | 92 | ; set bg tile addresses 93 | ld hl, VDP_WRITE_REGISTER | (4 << 8) | 0x7 94 | 95 | ld c, VDP_CONTROL_PORT 96 | out (c), l 97 | out (c), h 98 | 99 | ; set sprite table addresses 100 | ld hl, VDP_WRITE_REGISTER | (5 << 8) | 0xff 101 | 102 | ld c, VDP_CONTROL_PORT 103 | out (c), l 104 | out (c), h 105 | 106 | ; set sprite tile addresses 107 | ld hl, VDP_WRITE_REGISTER | (6 << 8) | 0xff 108 | 109 | ld c, VDP_CONTROL_PORT 110 | out (c), l 111 | out (c), h 112 | 113 | ; clear vram 114 | ld hl, VDP_WRITE_ADDRESS | 0x0000 115 | ld c, VDP_CONTROL_PORT 116 | out (c), l 117 | out (c), h 118 | 119 | ld c, VDP_DATA_PORT 120 | 121 | ld de, 0x4000 122 | clear_vram_loop: 123 | xor a, a 124 | out (c), a 125 | dec de 126 | ld a, d 127 | or a, e 128 | jr nz, clear_vram_loop 129 | 130 | ; check whether we're on a game gear in game gear mode 131 | ; and whether there's an fm unit installed 132 | call banjo_check_hardware 133 | 134 | ld a, 2 135 | call banjo_init 136 | 137 | ; change bank 138 | ld a, :cmajor_sn 139 | ld (SLOT_2_BANK_CHANGE), a 140 | 141 | ; get pointer in hl and loop mode on stack 142 | ld hl, cmajor_sn 143 | ld a, 1 144 | push af 145 | inc sp 146 | call banjo_play_song 147 | 148 | ; enable vdp screen 1 and enable vblank interrupt 149 | ld hl, VDP_WRITE_REGISTER | (1 << 8) | 0b11100000 150 | 151 | ld c, VDP_CONTROL_PORT 152 | out (c), l 153 | out (c), h 154 | 155 | ei 156 | 157 | wait_vblank: 158 | 159 | halt 160 | 161 | ld a, (tic) 162 | inc a 163 | ld (tic), a 164 | 165 | ; get controller 1 input 166 | in a, (0xdc) 167 | ld c, a 168 | 169 | ; this will combine the new input and the last input 170 | ; so that only a bit which is 0 this frame and was 1 last frame 171 | ; will be 0 this frame (i.e. the button was just pressed) 172 | ld a, (input_last) 173 | xor a, c 174 | cpl 175 | or a, c 176 | ld (input_pressed), a 177 | 178 | ; update input_last 179 | ld a, c 180 | ld (input_last), a 181 | 182 | ; set background colour 183 | ld c, VDP_CONTROL_PORT 184 | 185 | ; set background colour for debugging 186 | ld hl, VDP_WRITE_REGISTER | (0x7 << 8) | 0x1 187 | out (c), l 188 | out (c), h 189 | 190 | ; button 1 stops or starts play 191 | ld a, (input_pressed) 192 | and a, 0x10 193 | jr nz, + 194 | 195 | ld a, (song_playing) 196 | or a, a 197 | call z, banjo_song_resume 198 | call nz, banjo_song_stop 199 | 200 | +; 201 | 202 | ; button 2 plays an sfx 203 | ld a, (input_pressed) 204 | and a, 0x20 205 | jr nz, dont_play_sfx 206 | 207 | ; change bank 208 | ld a, :sfx_test_sn 209 | ld (SLOT_2_BANK_CHANGE), a 210 | 211 | ; get pointer in hl and loop mode on stack 212 | ld hl, sfx_test_sn 213 | ld a, 0 214 | push af 215 | inc sp 216 | call banjo_play_sfx 217 | 218 | dont_play_sfx: 219 | 220 | ; change bank 221 | ld a, :cmajor_sn 222 | ld (SLOT_2_BANK_CHANGE), a 223 | call banjo_update_song 224 | 225 | ; change bank 226 | ld a, :sfx_test_sn 227 | ld (SLOT_2_BANK_CHANGE), a 228 | call banjo_update_sfx 229 | 230 | ; reset background colour 231 | ld c, VDP_CONTROL_PORT 232 | 233 | ld hl, VDP_WRITE_REGISTER | (0x7 << 8) | 0x0 234 | out (c), l 235 | out (c), h 236 | 237 | jr wait_vblank 238 | 239 | .incdir "../../music_driver/" 240 | .include "music_driver.asm" 241 | 242 | ; song table 243 | ; SONG_DEF(SONG_LABEL) 244 | song_table_fm: 245 | SONG_DEF(cmajor) 246 | 247 | song_table_sn: 248 | SONG_DEF(cmajor_sn) 249 | 250 | ; sfx table 251 | ; SFX_DEF(SFX_LABEL, SFX_PRIORITY) 252 | sfx_table_fm: 253 | SFX_DEF(sfx_test, 10) 254 | 255 | sfx_table_sn: 256 | SFX_DEF(sfx_test_sn, 10) 257 | 258 | 259 | .BANK 2 260 | .SLOT 2 261 | .SECTION "Songs 2" 262 | .include "cmajor.asm" 263 | .include "sfx_test.asm" 264 | .ENDS 265 | 266 | .BANK 3 267 | .SLOT 2 268 | .SECTION "Songs 3" 269 | .include "cmajor_sn.asm" 270 | .include "sfx_test_sn.asm" 271 | .ENDS -------------------------------------------------------------------------------- /examples/wladx/main.asm: -------------------------------------------------------------------------------- 1 | 2 | .define VDP_CONTROL_PORT 0xbf 3 | .define VDP_DATA_PORT 0xbe 4 | 5 | .define VDP_WRITE_ADDRESS 0x4000 6 | .define VDP_WRITE_CRAM 0xc000 7 | .define VDP_WRITE_REGISTER 0x8000 8 | 9 | 10 | .MEMORYMAP 11 | SLOTSIZE $4000 12 | DEFAULTSLOT 0 13 | SLOT 0 $0000 ; ROM slot 0. 14 | SLOT 1 $4000 ; ROM slot 1. 15 | SLOT 2 $8000 ; ROM slot 2 16 | SLOT 3 $C000 ; RAM 17 | .ENDME 18 | 19 | .ROMBANKMAP 20 | BANKSTOTAL 4 21 | BANKSIZE $4000 22 | BANKS 4 23 | .ENDRO 24 | 25 | .org 0x0000 26 | jp init 27 | 28 | .RAMSECTION "Main Vars" bank 0 slot 3 29 | 30 | tic: db 31 | input_last: db 32 | input_pressed: db 33 | 34 | .ENDS 35 | 36 | .org 0x0038 37 | 38 | vblank_interrupt: 39 | 40 | push af 41 | exx 42 | 43 | ld c, VDP_CONTROL_PORT 44 | 45 | ; acknowledge interrupt 46 | in a, (c) 47 | 48 | exx 49 | pop af 50 | 51 | ei 52 | reti 53 | 54 | .org 0x0066 55 | retn 56 | 57 | 58 | init: 59 | di 60 | im 1 61 | 62 | ld sp, 0xdfff 63 | 64 | ; set vdp mode 4 65 | ld hl, VDP_WRITE_REGISTER | (0 << 8) | 0b00000100 66 | 67 | ld c, VDP_CONTROL_PORT 68 | out (c), l 69 | out (c), h 70 | 71 | ; disable vdp screens for now 72 | ld hl, VDP_WRITE_REGISTER | (1 << 8) | 0b10000000 73 | 74 | ld c, VDP_CONTROL_PORT 75 | out (c), l 76 | out (c), h 77 | 78 | ; set vdp nametable base addresses 79 | ld hl, VDP_WRITE_REGISTER | (2 << 8) | 0xff 80 | 81 | ld c, VDP_CONTROL_PORT 82 | out (c), l 83 | out (c), h 84 | 85 | ; set cram base addresses 86 | ld hl, VDP_WRITE_REGISTER | (3 << 8) | 0xff 87 | 88 | ld c, VDP_CONTROL_PORT 89 | out (c), l 90 | out (c), h 91 | 92 | ; set bg tile addresses 93 | ld hl, VDP_WRITE_REGISTER | (4 << 8) | 0x7 94 | 95 | ld c, VDP_CONTROL_PORT 96 | out (c), l 97 | out (c), h 98 | 99 | ; set sprite table addresses 100 | ld hl, VDP_WRITE_REGISTER | (5 << 8) | 0xff 101 | 102 | ld c, VDP_CONTROL_PORT 103 | out (c), l 104 | out (c), h 105 | 106 | ; set sprite tile addresses 107 | ld hl, VDP_WRITE_REGISTER | (6 << 8) | 0xff 108 | 109 | ld c, VDP_CONTROL_PORT 110 | out (c), l 111 | out (c), h 112 | 113 | ; clear vram 114 | ld hl, VDP_WRITE_ADDRESS | 0x0000 115 | ld c, VDP_CONTROL_PORT 116 | out (c), l 117 | out (c), h 118 | 119 | ld c, VDP_DATA_PORT 120 | 121 | ld de, 0x4000 122 | clear_vram_loop: 123 | xor a, a 124 | out (c), a 125 | dec de 126 | ld a, d 127 | or a, e 128 | jr nz, clear_vram_loop 129 | 130 | ; check whether we're on a game gear in game gear mode 131 | ; and whether there's an fm unit installed 132 | call banjo_check_hardware 133 | 134 | ld a, (banjo_fm_unit_present) 135 | or a, a 136 | jr z, banjo_init_no_fm 137 | 138 | ; initialise channels and FM 139 | ld a, MODE_FM 140 | call banjo_init 141 | 142 | ; use fm song and sfx tables 143 | ld hl, song_table_fm 144 | call banjo_set_song_table 145 | 146 | ld hl, sfx_table_fm 147 | call banjo_set_sfx_table 148 | 149 | jr banjo_init_done 150 | 151 | banjo_init_no_fm: 152 | 153 | ; initialise channels 154 | ld a, MODE_SN 155 | call banjo_init 156 | 157 | ; use sn song and sfx tables 158 | ld hl, song_table_sn 159 | call banjo_set_song_table 160 | 161 | ld hl, sfx_table_sn 162 | call banjo_set_sfx_table 163 | 164 | banjo_init_done: 165 | 166 | ; queue up song 0 167 | ld a, 0 168 | call banjo_queue_song 169 | 170 | ld a, 0 171 | ld (tic), a 172 | 173 | ; enable vdp screen 1 and enable vblank interrupt 174 | ld hl, VDP_WRITE_REGISTER | (1 << 8) | 0b11100000 175 | 176 | ld c, VDP_CONTROL_PORT 177 | out (c), l 178 | out (c), h 179 | 180 | ei 181 | 182 | wait_vblank: 183 | 184 | halt 185 | 186 | ld a, (tic) 187 | inc a 188 | ld (tic), a 189 | 190 | ; get controller 1 input 191 | in a, (0xdc) 192 | ld c, a 193 | 194 | ; this will combine the new input and the last input 195 | ; so that only a bit which is 0 this frame and was 1 last frame 196 | ; will be 0 this frame (i.e. the button was just pressed) 197 | ld a, (input_last) 198 | xor a, c 199 | cpl 200 | or a, c 201 | ld (input_pressed), a 202 | 203 | ; update input_last 204 | ld a, c 205 | ld (input_last), a 206 | 207 | ; set background colour 208 | ld c, VDP_CONTROL_PORT 209 | 210 | ; set background colour for debugging 211 | ld hl, VDP_WRITE_REGISTER | (0x7 << 8) | 0x1 212 | out (c), l 213 | out (c), h 214 | 215 | ; button 1 stops or starts play 216 | ld a, (input_pressed) 217 | and a, 0x10 218 | jr nz, + 219 | 220 | ld a, (song_playing) 221 | or a, a 222 | call z, banjo_song_resume 223 | call nz, banjo_song_stop 224 | 225 | +; 226 | 227 | ; button 2 plays an sfx 228 | ld a, (input_pressed) 229 | and a, 0x20 230 | ld a, 0 231 | call z, banjo_queue_sfx 232 | 233 | ; run update every frame! 234 | call banjo_update 235 | 236 | ; reset background colour 237 | ld c, VDP_CONTROL_PORT 238 | 239 | ld hl, VDP_WRITE_REGISTER | (0x7 << 8) | 0x0 240 | out (c), l 241 | out (c), h 242 | 243 | jr wait_vblank 244 | 245 | .incdir "../../music_driver/" 246 | .include "music_driver.asm" 247 | 248 | ; song table 249 | ; SONG_DEF(SONG_LABEL) 250 | song_table_fm: 251 | SONG_DEF(cmajor) 252 | 253 | song_table_sn: 254 | SONG_DEF(cmajor_sn) 255 | 256 | ; sfx table 257 | ; SFX_DEF(SFX_LABEL, SFX_PRIORITY) 258 | sfx_table_fm: 259 | SFX_DEF(sfx_test, 10) 260 | 261 | sfx_table_sn: 262 | SFX_DEF(sfx_test_sn, 10) 263 | 264 | 265 | .BANK 2 266 | .SLOT 2 267 | .SECTION "Songs 2" 268 | .include "cmajor.asm" 269 | .include "sfx_test.asm" 270 | .ENDS 271 | 272 | .BANK 3 273 | .SLOT 2 274 | .SECTION "Songs 3" 275 | .include "cmajor_sn.asm" 276 | .include "sfx_test_sn.asm" 277 | .ENDS -------------------------------------------------------------------------------- /music_driver_sdas/update_pitch_registers.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; get fnum for note into de 6 | ; hl: tone lookup table address 7 | ; ix: channel 8 | ; iy: state 9 | music_calc_fnum: 10 | 11 | ; check if we have a pitch slide going as 12 | ; it takes precedence over arpeggios 13 | bit CHAN_FLAG_BIT_SLIDE, channel.flags(ix) 14 | jr nz, mcf_no_arp 15 | 16 | ; check if we have an arpeggio running 17 | bit CHAN_FLAG_BIT_ARPEGGIO, channel.flags(ix) 18 | jr z, mcf_no_arp 19 | 20 | ; check arpeggio pos 21 | ld a, channel.arpeggio_pos(ix) 22 | or a, a 23 | jr z, mcf_arp_pos_0 24 | 25 | ; isolate lower nibble 26 | ; the arpeggio nibbles will be swapped in music_process_channels_tic 27 | ld a, channel.arpeggio(ix) 28 | and a, #0xf 29 | 30 | mcf_arp_pos_0: 31 | 32 | ; add the channel's midi note to the arpeggio note 33 | ; double it to make it an offset into the fnum table 34 | add a, channel.midi_note(ix) 35 | add a, a 36 | 37 | ; add to tone lookup table address in hl 38 | add a, l 39 | ld l, a 40 | adc a, h 41 | sub a, l 42 | ld h, a 43 | 44 | ; get frequency from table into de 45 | ld e, (hl) 46 | inc hl 47 | ld d, (hl) 48 | 49 | ret 50 | 51 | mcf_no_arp: 52 | 53 | ; get tone fnum in de 54 | ld e, channel.freq(ix) 55 | ld d, channel.freq+1(ix) 56 | 57 | ret 58 | 59 | ; write to pitch registers for the specified channel 60 | ; ix: channel 61 | ; iy: state 62 | music_update_pitch_registers: 63 | 64 | push bc 65 | 66 | ; check which type of channel we're looking at 67 | ld a, channel.type(ix) 68 | or a, a 69 | jr nz, music_update_pitch_is_opll 70 | 71 | ; check if this is the noise channel (need to shift the 3 by 5 as subchannel is pre-shifted) 72 | ld a, channel.subchannel(ix) 73 | cp a, #3<<5 74 | jr nz, mup_sn_square_channel 75 | 76 | ; we're in a noise channel 77 | ; check whether it's pitched or fixed 78 | ld a, music_state.noise_mode(iy) 79 | and a, #0x3 80 | cp a, #0x3 81 | jr nz, mup_sn_fixed_noise_channel 82 | 83 | ; we're in a pitched noise channel 84 | ; we want to write to the last square channel's pitch instead 85 | ld c, #0x2<<5 86 | jr mup_sn_pitched_noise_channel 87 | 88 | ; for a regular square channel we update this channel's pitch 89 | mup_sn_square_channel: 90 | 91 | ; get channel number (pre-shifted into correct position) 92 | ld c, channel.subchannel(ix) 93 | 94 | ; for pitched noise it uses channel 3's pitch 95 | mup_sn_pitched_noise_channel: 96 | 97 | ; get note fnum in de 98 | ld hl, #sn_tone_lookup 99 | call music_calc_fnum 100 | 101 | ; store tone data in channel struct 102 | ld channel.freq(ix), e 103 | ld channel.freq+1(ix), d 104 | 105 | ; prepare note value for writing to chip 106 | ; isolate lower nibble 107 | ld a, e 108 | 109 | and a, #0xf 110 | ; set latch bit 111 | or a, #0x80 112 | ; combine with channel number 113 | or a, c 114 | 115 | ; output first byte of tone data 116 | ld c, channel.port(ix) 117 | out (c), a 118 | 119 | ; get upper four bits of lower byte of fnum 120 | ; or with lower two bits of upper byte of fnum 121 | ld a, e 122 | and a, #0xf0 123 | or a, d 124 | 125 | ; rotate them into place 126 | rlca 127 | rlca 128 | rlca 129 | rlca 130 | 131 | ; output second byte of tone data 132 | out (c), a 133 | 134 | ; done 135 | jr music_update_pitch_done 136 | 137 | ; pitch not based on channel 3 138 | mup_sn_fixed_noise_channel: 139 | 140 | ; get midi number 141 | ld a, channel.midi_note(ix) 142 | 143 | ; mod 12 144 | fixed_noise_mod_12: 145 | sub a, #12 146 | jr nc, fixed_noise_mod_12 147 | 148 | ; restrict to 0-2 149 | add a, #12 150 | and a, #0x3 151 | ld c, a 152 | ; actual value should be 2 minus this value 153 | ld a, #2 154 | sub a, c 155 | and a, #0x3 156 | ld c, a 157 | 158 | ; clear bottom 2 bits of current noise mode 159 | ; and or with new value 160 | ld a, music_state.noise_mode(iy) 161 | or a, c 162 | 163 | ; update chip 164 | ld c, channel.port(ix) 165 | out (c), a 166 | 167 | ; done 168 | jr music_update_pitch_done 169 | 170 | music_update_pitch_is_opll: 171 | 172 | ; don't update pitch for drum channels 173 | cp a, #CHAN_OPLL_DRUMS 174 | jr z, music_update_pitch_done 175 | 176 | ; get note fnum in de 177 | ld hl, #fm_tone_lookup 178 | call music_calc_fnum 179 | 180 | ; get f-number register to write to for this channel 181 | ld a, channel.subchannel(ix) 182 | add a, #0x10 183 | ld c, a 184 | out (#OPLL_REG_PORT), a 185 | 186 | ; lower 8 bits of f number 187 | ld a, e 188 | out (#OPLL_DATA_PORT), a 189 | 190 | ; delay after opll data write 191 | push hl 192 | pop hl 193 | push hl 194 | pop hl 195 | push hl 196 | pop hl 197 | 198 | ; store for later 199 | ld channel.freq(ix), a 200 | 201 | ; get octave + key on register to write to for this channel 202 | ; by adding to base_reg from earlier 203 | ld a, c 204 | add a, #0x10 205 | out (#OPLL_REG_PORT), a 206 | 207 | ; get octave and 9th bit of f number 208 | ld a, d 209 | 210 | ; store for later 211 | ld channel.freq+1(ix), a 212 | 213 | ; set key on bit and write 214 | and a, #0xf 215 | or a, #0x10 216 | out (#OPLL_DATA_PORT), a 217 | 218 | nop 219 | nop 220 | nop 221 | nop 222 | nop 223 | nop 224 | nop 225 | nop 226 | 227 | jr music_update_pitch_done 228 | 229 | 230 | music_update_pitch_done: 231 | 232 | ; reset pitch_changed flag 233 | res CHAN_FLAG_BIT_PITCH_CHANGED, channel.flags(ix) 234 | 235 | pop bc 236 | 237 | ret 238 | -------------------------------------------------------------------------------- /music_driver/update_pitch_registers.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; get fnum for note into de 6 | ; hl: tone lookup table address 7 | ; ix: channel 8 | ; iy: state 9 | music_calc_fnum: 10 | 11 | ; check if we have a pitch slide going as 12 | ; it takes precedence over arpeggios 13 | bit CHAN_FLAG_BIT_SLIDE, (ix + channel.flags) 14 | jr nz, mcf_no_arp 15 | 16 | ; check if we have an arpeggio running 17 | bit CHAN_FLAG_BIT_ARPEGGIO, (ix + channel.flags) 18 | jr z, mcf_no_arp 19 | 20 | ; check arpeggio pos 21 | ld a, (ix + channel.arpeggio_pos) 22 | or a, a 23 | jr z, mcf_arp_pos_0 24 | 25 | ; isolate lower nibble 26 | ; the arpeggio nibbles will be swapped in music_process_channels_tic 27 | ld a, (ix + channel.arpeggio) 28 | and a, 0xf 29 | 30 | mcf_arp_pos_0: 31 | 32 | ; add the channel's midi note to the arpeggio note 33 | ; double it to make it an offset into the fnum table 34 | add a, (ix + channel.midi_note) 35 | add a, a 36 | 37 | ; add to tone lookup table address in hl 38 | add a, l 39 | ld l, a 40 | adc a, h 41 | sub a, l 42 | ld h, a 43 | 44 | ; get frequency from table into de 45 | ld e, (hl) 46 | inc hl 47 | ld d, (hl) 48 | 49 | ret 50 | 51 | mcf_no_arp: 52 | 53 | ; get tone fnum in de 54 | ld e, (ix + channel.freq) 55 | ld d, (ix + channel.freq + 1) 56 | 57 | ret 58 | 59 | ; write to pitch registers for the specified channel 60 | ; ix: channel 61 | ; iy: state 62 | music_update_pitch_registers: 63 | 64 | push bc 65 | 66 | ; check which type of channel we're looking at 67 | ld a, (ix + channel.type) 68 | or a, a 69 | jr nz, music_update_pitch_is_opll 70 | 71 | ; check if this is the noise channel (need to shift the 3 by 5 as subchannel is pre-shifted) 72 | ld a, (ix + channel.subchannel) 73 | cp a, 3 << 5 74 | jr nz, mup_sn_square_channel 75 | 76 | ; we're in a noise channel 77 | ; check whether it's pitched or fixed 78 | ld a, (iy + music_state.noise_mode) 79 | and a, 0x3 80 | cp a, 0x3 81 | jr nz, mup_sn_fixed_noise_channel 82 | 83 | ; we're in a pitched noise channel 84 | ; we want to write to the last square channel's pitch instead 85 | ld c, 0x2 << 5 86 | jr mup_sn_pitched_noise_channel 87 | 88 | ; for a regular square channel we update this channel's pitch 89 | mup_sn_square_channel: 90 | 91 | ; get channel number (pre-shifted into correct position) 92 | ld c, (ix + channel.subchannel) 93 | 94 | ; for pitched noise it uses channel 3's pitch 95 | mup_sn_pitched_noise_channel: 96 | 97 | ; get note fnum in de 98 | ld hl, sn_tone_lookup 99 | call music_calc_fnum 100 | 101 | ; store tone data in channel struct 102 | ld (ix + channel.freq), e 103 | ld (ix + channel.freq + 1), d 104 | 105 | ; prepare note value for writing to chip 106 | ; isolate lower nibble 107 | ld a, e 108 | 109 | and a, 0xf 110 | ; set latch bit 111 | or a, 0x80 112 | ; combine with channel number 113 | or a, c 114 | 115 | ; output first byte of tone data 116 | ld c, (ix + channel.port) 117 | out (c), a 118 | 119 | ; get upper four bits of lower byte of fnum 120 | ; or with lower two bits of upper byte of fnum 121 | ld a, e 122 | and a, 0xf0 123 | or a, d 124 | 125 | ; rotate them into place 126 | rlca 127 | rlca 128 | rlca 129 | rlca 130 | 131 | ; output second byte of tone data 132 | out (c), a 133 | 134 | ; done 135 | jr music_update_pitch_done 136 | 137 | ; pitch not based on channel 3 138 | mup_sn_fixed_noise_channel: 139 | 140 | ; get midi number 141 | ld a, (ix + channel.midi_note) 142 | 143 | ; mod 12 144 | fixed_noise_mod_12: 145 | sub a, 12 146 | jr nc, fixed_noise_mod_12 147 | 148 | ; restrict to 0-2 149 | add a, 12 150 | and a, 0x3 151 | ld c, a 152 | ; actual value should be 2 minus this value 153 | ld a, 2 154 | sub a, c 155 | and a, 0x3 156 | ld c, a 157 | 158 | ; clear bottom 2 bits of current noise mode 159 | ; and or with new value 160 | ld a, (iy + music_state.noise_mode) 161 | or a, c 162 | 163 | ; update chip 164 | ld c, (ix + channel.port) 165 | out (c), a 166 | 167 | ; done 168 | jr music_update_pitch_done 169 | 170 | music_update_pitch_is_opll: 171 | 172 | ; don't update pitch for drum channels 173 | cp a, CHAN_OPLL_DRUMS 174 | jr z, music_update_pitch_done 175 | 176 | ; get note fnum in de 177 | ld hl, fm_tone_lookup 178 | call music_calc_fnum 179 | 180 | ; get f-number register to write to for this channel 181 | ld a, (ix + channel.subchannel) 182 | add a, 0x10 183 | ld c, a 184 | out (OPLL_REG_PORT), a 185 | 186 | ; lower 8 bits of f number 187 | ld a, e 188 | out (OPLL_DATA_PORT), a 189 | 190 | ; delay after opll data write 191 | push hl 192 | pop hl 193 | push hl 194 | pop hl 195 | push hl 196 | pop hl 197 | 198 | ; store for later 199 | ld (ix + channel.freq), a 200 | 201 | ; get octave + key on register to write to for this channel 202 | ; by adding to base_reg from earlier 203 | ld a, c 204 | add a, 0x10 205 | out (OPLL_REG_PORT), a 206 | 207 | ; get octave and 9th bit of f number 208 | ld a, d 209 | 210 | ; store for later 211 | ld (ix + channel.freq + 1), a 212 | 213 | ; set key on bit and write 214 | and a, 0xf 215 | or a, 0x10 216 | out (OPLL_DATA_PORT), a 217 | 218 | nop 219 | nop 220 | nop 221 | nop 222 | nop 223 | nop 224 | nop 225 | nop 226 | 227 | jr music_update_pitch_done 228 | 229 | 230 | music_update_pitch_done: 231 | 232 | ; reset pitch_changed flag 233 | res CHAN_FLAG_BIT_PITCH_CHANGED, (ix + channel.flags) 234 | 235 | pop bc 236 | 237 | ret 238 | -------------------------------------------------------------------------------- /music_driver_sdas/instrument_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; ix: channel 6 | ; iy: state 7 | ; a: new instrument number 8 | music_instrument_change: 9 | 10 | ; store the new instrument number in the channel 11 | ld channel.instrument_num(ix), a 12 | 13 | ; get pointer to instrument address table in hl 14 | ld l, music_state.instrument_ptrs(iy) 15 | ld h, music_state.instrument_ptrs+1(iy) 16 | 17 | ; offset to get instrument pointer by adding instrument_num * 2 to de 18 | add a, a 19 | add a, l 20 | ld l, a 21 | adc a, h 22 | sub a, l 23 | ld h, a 24 | 25 | ; get pointer to instrument into de 26 | ld a, (hl) 27 | inc hl 28 | ld h, (hl) 29 | ld l, a 30 | 31 | ; volume macro 32 | ; set initial macro position 33 | ld channel.volume_macro_pos(ix), #0 34 | 35 | ; copy over macro info 36 | ld a, (hl) 37 | ld channel.volume_macro_len(ix), a 38 | 39 | ; check if volume_macro_len > 0 40 | or a, a 41 | jr z, mic_no_volume_macro 42 | 43 | inc hl 44 | ld a, (hl) 45 | ld channel.volume_macro_loop(ix), a 46 | 47 | inc hl 48 | ld a, (hl) 49 | ld channel.volume_macro_ptr(ix), a 50 | 51 | inc hl 52 | ld a, (hl) 53 | ld channel.volume_macro_ptr+1(ix), a 54 | 55 | ; set volume macro flag 56 | set CHAN_FLAG_BIT_VOLUME_MACRO, channel.flags(ix) 57 | 58 | jr mic_volume_macro_done 59 | 60 | mic_no_volume_macro: 61 | 62 | inc hl 63 | inc hl 64 | inc hl 65 | 66 | ld channel.volume_macro_loop(ix), #0 67 | ld channel.volume_macro_ptr(ix), #0 68 | ld channel.volume_macro_ptr+1(ix), #0 69 | 70 | ; clear volume macro flag 71 | res CHAN_FLAG_BIT_VOLUME_MACRO, channel.flags(ix) 72 | 73 | mic_volume_macro_done: 74 | 75 | ; check if this is a normal fm channel, skip ahead if it's not 76 | ld a, channel.type(ix) 77 | cp a, #CHAN_OPLL 78 | jr nz, music_instrument_change_done 79 | 80 | .ifdef INCLUDE_OPLL 81 | 82 | ; it is an fm instrument, store the patch number shifted left by 4 83 | inc hl 84 | ld a, (hl) 85 | ld channel.fm_patch_shifted(ix), a 86 | 87 | ; if it's patch 0, update the fm registers with the custom patch data 88 | ; otherwise we're done 89 | or a, a 90 | jr nz, music_instrument_change_done 91 | 92 | ; load custom patch 93 | inc hl 94 | 95 | ; start register number 96 | ld c, #0x00 97 | 98 | music_instrument_change_fm_patch: 99 | 100 | ld a, c 101 | out (#OPLL_REG_PORT), a 102 | 103 | ld a, (hl) 104 | out (#OPLL_DATA_PORT), a 105 | 106 | ; delay after opll data write 107 | push hl 108 | pop hl 109 | push hl 110 | pop hl 111 | 112 | ; next register and next patch data 113 | inc hl 114 | inc c 115 | 116 | ; reached the last register? 117 | ld a, #0x08 118 | cp a, c 119 | jr nz, music_instrument_change_fm_patch 120 | 121 | .endif 122 | 123 | music_instrument_change_done: 124 | 125 | ret 126 | 127 | ; hl: music instruction pointer 128 | music_fm_drum_change: 129 | 130 | ; get instrument number in a 131 | inc hl 132 | ld a, (hl) 133 | ld channel.instrument_num(ix), a 134 | 135 | ; preserve hl in de 136 | ex de, hl 137 | 138 | ; get pointer to pointer to instrument in hl 139 | ld l, music_state.instrument_ptrs(iy) 140 | ld h, music_state.instrument_ptrs+1(iy) 141 | 142 | ; double it to get offset into instrument pointers 143 | add a, a 144 | 145 | ; apply offset 146 | add a, l 147 | ld l, a 148 | adc a, h 149 | sub a, l 150 | ld h, a 151 | 152 | ; get instrument pointer into hl 153 | ld a, (hl) 154 | inc hl 155 | ld h, (hl) 156 | 157 | ; move hl along to fm patch data 158 | add a, #4 159 | ld l, a 160 | adc a, h 161 | sub a, l 162 | ld h, a 163 | 164 | ; restore hl from de 165 | ; pointer to fm data now in de 166 | ex de, hl 167 | 168 | ; is this a fixed-frequency patch? 169 | ld a, (de) 170 | cp a, #0xff 171 | jr z, music_fm_drum_change_fixed_freq 172 | 173 | ; not fixed freq 174 | ld a, channel.fm_drum_trigger(ix) 175 | and a, #0x1f 176 | ld channel.fm_drum_trigger(ix), a 177 | jr music_fm_drum_change_done 178 | 179 | music_fm_drum_change_fixed_freq: 180 | 181 | ; enable fixed pitch mode on drum trigger 182 | ld a, channel.fm_drum_trigger(ix) 183 | or a, #0x20 184 | ld channel.fm_drum_trigger(ix), a 185 | 186 | ; fixed pitch fnums and blocks 187 | inc de 188 | 189 | ; kick? 190 | ld a, #0x16 191 | out (#OPLL_REG_PORT), a 192 | ld a, (de) 193 | out (#OPLL_DATA_PORT), a 194 | 195 | inc de 196 | 197 | ; delay after opll data write 198 | push hl 199 | pop hl 200 | push hl 201 | pop hl 202 | push hl 203 | pop hl 204 | 205 | 206 | ld a, #0x26 207 | out (#OPLL_REG_PORT), a 208 | ld a, (de) 209 | out (#OPLL_DATA_PORT), a 210 | 211 | inc de 212 | 213 | ; delay after opll data write 214 | push hl 215 | pop hl 216 | push hl 217 | pop hl 218 | push hl 219 | pop hl 220 | 221 | ; snare? 222 | ld a, #0x17 223 | out (#OPLL_REG_PORT), a 224 | ld a, (de) 225 | out (#OPLL_DATA_PORT), a 226 | 227 | inc de 228 | 229 | ; delay after opll data write 230 | push hl 231 | pop hl 232 | push hl 233 | pop hl 234 | push hl 235 | pop hl 236 | 237 | ld a, #0x27 238 | out (#OPLL_REG_PORT), a 239 | ld a, (de) 240 | out (#OPLL_DATA_PORT), a 241 | 242 | inc de 243 | 244 | ; delay after opll data write 245 | push hl 246 | pop hl 247 | push hl 248 | pop hl 249 | push hl 250 | pop hl 251 | 252 | ; tom? 253 | ld a, #0x18 254 | out (#OPLL_REG_PORT), a 255 | ld a, (de) 256 | out (#OPLL_DATA_PORT), a 257 | 258 | inc de 259 | 260 | ; delay after opll data write 261 | push hl 262 | pop hl 263 | push hl 264 | pop hl 265 | push hl 266 | pop hl 267 | 268 | ld a, #0x28 269 | out (#OPLL_REG_PORT), a 270 | ld a, (de) 271 | out (#OPLL_DATA_PORT), a 272 | 273 | 274 | music_fm_drum_change_done: 275 | inc hl 276 | jp music_process_new_line_subloop 277 | -------------------------------------------------------------------------------- /music_driver/instrument_change.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; ix: channel 6 | ; iy: state 7 | ; a: new instrument number 8 | music_instrument_change: 9 | 10 | ; store the new instrument number in the channel 11 | ld (ix + channel.instrument_num), a 12 | 13 | ; get pointer to instrument address table in hl 14 | ld l, (iy + music_state.instrument_ptrs) 15 | ld h, (iy + music_state.instrument_ptrs + 1) 16 | 17 | ; offset to get instrument pointer by adding instrument_num * 2 to de 18 | add a, a 19 | add a, l 20 | ld l, a 21 | adc a, h 22 | sub a, l 23 | ld h, a 24 | 25 | ; get pointer to instrument into de 26 | ld a, (hl) 27 | inc hl 28 | ld h, (hl) 29 | ld l, a 30 | 31 | ; volume macro 32 | ; set initial macro position 33 | ld (ix + channel.volume_macro_pos), 0 34 | 35 | ; copy over macro info 36 | ld a, (hl) 37 | ld (ix + channel.volume_macro_len), a 38 | 39 | ; check if volume_macro_len > 0 40 | or a, a 41 | jr z, mic_no_volume_macro 42 | 43 | inc hl 44 | ld a, (hl) 45 | ld (ix + channel.volume_macro_loop), a 46 | 47 | inc hl 48 | ld a, (hl) 49 | ld (ix + channel.volume_macro_ptr), a 50 | 51 | inc hl 52 | ld a, (hl) 53 | ld (ix + channel.volume_macro_ptr + 1), a 54 | 55 | ; set volume macro flag 56 | set CHAN_FLAG_BIT_VOLUME_MACRO, (ix + channel.flags) 57 | 58 | jr mic_volume_macro_done 59 | 60 | mic_no_volume_macro: 61 | 62 | inc hl 63 | inc hl 64 | inc hl 65 | 66 | ld (ix + channel.volume_macro_loop), 0 67 | ld (ix + channel.volume_macro_ptr), 0 68 | ld (ix + channel.volume_macro_ptr + 1), 0 69 | 70 | ; clear volume macro flag 71 | res CHAN_FLAG_BIT_VOLUME_MACRO, (ix + channel.flags) 72 | 73 | mic_volume_macro_done: 74 | 75 | ; check if this is a normal fm channel, skip ahead if it's not 76 | ld a, (ix + channel.type) 77 | cp a, CHAN_OPLL 78 | jr nz, music_instrument_change_done 79 | 80 | .ifdef INCLUDE_OPLL 81 | 82 | ; it is an fm instrument, store the patch number shifted left by 4 83 | inc hl 84 | ld a, (hl) 85 | ld (ix + channel.fm_patch_shifted), a 86 | 87 | ; if it's patch 0, update the fm registers with the custom patch data 88 | ; otherwise we're done 89 | or a, a 90 | jr nz, music_instrument_change_done 91 | 92 | ; load custom patch 93 | inc hl 94 | 95 | ; start register number 96 | ld c, 0x00 97 | 98 | music_instrument_change_fm_patch: 99 | 100 | ld a, c 101 | out (OPLL_REG_PORT), a 102 | 103 | ld a, (hl) 104 | out (OPLL_DATA_PORT), a 105 | 106 | ; delay after opll data write 107 | push hl 108 | pop hl 109 | push hl 110 | pop hl 111 | 112 | ; next register and next patch data 113 | inc hl 114 | inc c 115 | 116 | ; reached the last register? 117 | ld a, 0x08 118 | cp a, c 119 | jr nz, music_instrument_change_fm_patch 120 | 121 | .endif 122 | 123 | music_instrument_change_done: 124 | 125 | ret 126 | 127 | ; hl: music instruction pointer 128 | music_fm_drum_change: 129 | 130 | ; get instrument number in a 131 | inc hl 132 | ld a, (hl) 133 | ld (ix + channel.instrument_num), a 134 | 135 | ; preserve hl in de 136 | ex de, hl 137 | 138 | ; get pointer to pointer to instrument in hl 139 | ld l, (iy + music_state.instrument_ptrs) 140 | ld h, (iy + music_state.instrument_ptrs + 1) 141 | 142 | ; double it to get offset into instrument pointers 143 | add a, a 144 | 145 | ; apply offset 146 | add a, l 147 | ld l, a 148 | adc a, h 149 | sub a, l 150 | ld h, a 151 | 152 | ; get instrument pointer into hl 153 | ld a, (hl) 154 | inc hl 155 | ld h, (hl) 156 | 157 | ; move hl along to fm patch data 158 | add a, 4 159 | ld l, a 160 | adc a, h 161 | sub a, l 162 | ld h, a 163 | 164 | ; restore hl from de 165 | ; pointer to fm data now in de 166 | ex de, hl 167 | 168 | ; is this a fixed-frequency patch? 169 | ld a, (de) 170 | cp a, 0xff 171 | jr z, music_fm_drum_change_fixed_freq 172 | 173 | ; not fixed freq 174 | ld a, (ix + channel.fm_drum_trigger) 175 | and a, 0x1f 176 | ld (ix + channel.fm_drum_trigger), a 177 | jr music_fm_drum_change_done 178 | 179 | music_fm_drum_change_fixed_freq: 180 | 181 | ; enable fixed pitch mode on drum trigger 182 | ld a, (ix + channel.fm_drum_trigger) 183 | or a, 0x20 184 | ld (ix + channel.fm_drum_trigger), a 185 | 186 | ; fixed pitch fnums and blocks 187 | inc de 188 | 189 | ; kick? 190 | ld a, 0x16 191 | out (OPLL_REG_PORT), a 192 | ld a, (de) 193 | out (OPLL_DATA_PORT), a 194 | 195 | inc de 196 | 197 | ; delay after opll data write 198 | push hl 199 | pop hl 200 | push hl 201 | pop hl 202 | push hl 203 | pop hl 204 | 205 | 206 | ld a, 0x26 207 | out (OPLL_REG_PORT), a 208 | ld a, (de) 209 | out (OPLL_DATA_PORT), a 210 | 211 | inc de 212 | 213 | ; delay after opll data write 214 | push hl 215 | pop hl 216 | push hl 217 | pop hl 218 | push hl 219 | pop hl 220 | 221 | ; snare? 222 | ld a, 0x17 223 | out (OPLL_REG_PORT), a 224 | ld a, (de) 225 | out (OPLL_DATA_PORT), a 226 | 227 | inc de 228 | 229 | ; delay after opll data write 230 | push hl 231 | pop hl 232 | push hl 233 | pop hl 234 | push hl 235 | pop hl 236 | 237 | ld a, 0x27 238 | out (OPLL_REG_PORT), a 239 | ld a, (de) 240 | out (OPLL_DATA_PORT), a 241 | 242 | inc de 243 | 244 | ; delay after opll data write 245 | push hl 246 | pop hl 247 | push hl 248 | pop hl 249 | push hl 250 | pop hl 251 | 252 | ; tom? 253 | ld a, 0x18 254 | out (OPLL_REG_PORT), a 255 | ld a, (de) 256 | out (OPLL_DATA_PORT), a 257 | 258 | inc de 259 | 260 | ; delay after opll data write 261 | push hl 262 | pop hl 263 | push hl 264 | pop hl 265 | push hl 266 | pop hl 267 | 268 | ld a, 0x28 269 | out (OPLL_REG_PORT), a 270 | ld a, (de) 271 | out (OPLL_DATA_PORT), a 272 | 273 | 274 | music_fm_drum_change_done: 275 | inc hl 276 | jp music_process_new_line_subloop -------------------------------------------------------------------------------- /music_driver/opll/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; OPLL Pitch slides 9 | ; slide type == 1, upward slide 10 | music_update_fm_pitch_slide_upward: 11 | 12 | ; add slide_amount to lower byte 13 | ld a, e 14 | add a, (ix + channel.slide_amount) 15 | ld e, a 16 | 17 | ; add carry to upper byte 18 | adc a, d 19 | sub a, e 20 | ld d, a 21 | 22 | ; we want to check if fnum > 325 23 | 24 | ; isolate bit 8 of fnum 25 | and a, 0x1 26 | ; combine with low byte of fnum - we don't care about the lowest bit 27 | or a, e 28 | ; rotate so a now contains top 8 bits of the 9-bit fnum 29 | rrca 30 | 31 | ; a now contains the fnum / 2 32 | ; check if a > (325 / 2) 33 | cp a, 162 34 | jp c, music_update_pitch_slide_done 35 | 36 | ; a already contains half of the fnum so we can load that into e 37 | ld e, a 38 | 39 | ; clear upper bit of fnum (lowest bit of d) and add 1 to block 40 | ld a, d 41 | and a, 0xfe 42 | add a, 0x2 43 | ld d, a 44 | 45 | ; has block overflowed? 46 | and a, 0xf0 47 | jp z, music_update_pitch_slide_done 48 | 49 | ; cap pitch 50 | ld de, 3990 51 | jp music_update_pitch_slide_done 52 | 53 | 54 | ; slide type == 2, downward slide 55 | music_update_fm_pitch_slide_downward: 56 | 57 | ; sub slide_amount from lower byte 58 | ld a, e 59 | sub a, (ix + channel.slide_amount) 60 | ld e, a 61 | 62 | ; sub carry from upper byte 63 | ld a, d 64 | sbc a, e 65 | add a, e 66 | ld d, a 67 | 68 | ; isolate bit 8 of fnum 69 | and a, 0x1 70 | ; combine with low byte of fnum - we don't care about the lowest bit 71 | or a, e 72 | ; rotate so a now contains top 8 bits of the 9-bit fnum 73 | rrca 74 | 75 | ; a now contains the fnum / 2 76 | ; check if a < (120 / 2) 77 | cp a, 60 78 | jp nc, music_update_pitch_slide_done 79 | 80 | ; double the fnum 81 | sla e 82 | sla e 83 | 84 | ; subtract 1 from the block 85 | ld a, d 86 | and a, 0xfe 87 | sub a, 0x2 88 | ld d, a 89 | 90 | ; has block underflowed? 91 | jp nc, music_update_pitch_slide_done 92 | 93 | ; if it has, cap pitch 94 | ld de, 110 95 | jp music_update_pitch_slide_done 96 | 97 | ; handle OPLL portamento 98 | ; note that as the ranges of fnums we're using for the notes C to B span from 99 | ; C = 172 to B = 325 100 | ; we need to move to the next octave when we go too far above/below those fnums 101 | ; otherwise we'll never hit the target_freq's combination of octave and fnum 102 | ; ix: current channel 103 | ; iy: music state 104 | ; de: current channel.freq 105 | ; outputs updated freq in de 106 | music_update_fm_portamento: 107 | 108 | ; get target_freq in hl 109 | ld l, (ix + channel.target_freq) 110 | ld h, (ix + channel.target_freq + 1) 111 | 112 | ; subtract current freq from target_freq 113 | sbc hl, de 114 | jr z, mufm_porta_end_portamento 115 | jr c, mufm_porta_current_higher 116 | 117 | mufm_porta_current_lower: 118 | 119 | ; get target_freq back into hl 120 | add hl, de 121 | 122 | ; add slide amount to freq 123 | ld a, (ix + channel.slide_amount) 124 | add a, e 125 | ld e, a 126 | adc a, d 127 | sub a, e 128 | ld d, a 129 | 130 | ; check if fnum > (fnum for note B) + leeway 131 | ; if it is, we need to inc the octave and half the fnum 132 | 133 | ; if bit 0 of d isn't set, the fnum is < 256 134 | bit 0, d 135 | jr z, mufm_porta_lower_octave_ok 136 | 137 | ; check if the lower byte makes the full fnum > 352 138 | ld a, e 139 | cp a, 352 - 256 140 | jr nc, mufm_porta_lower_octave_ok 141 | 142 | ; halve the fnum 143 | ; or-ing in the 256 bit from the upper byte as a 128 bit 144 | srl a 145 | or a, 0x80 146 | ld e, a 147 | 148 | ; clear the 256 bit from the fnum in the upper byte 149 | ld a, d 150 | and a, 0xfe 151 | ; add 1 to the octave 152 | add a, 0x2 153 | ld d, a 154 | 155 | mufm_porta_lower_octave_ok: 156 | ; check if we've passed target_freq 157 | ; if z then freq == target and we're done 158 | ; if c then freq > target and we're done 159 | ; if nc then we carry on next loop 160 | sbc hl, de 161 | jr z, mufm_porta_end_portamento 162 | jr c, mufm_porta_end_portamento 163 | jr nc, mufm_porta_done 164 | 165 | mufm_porta_current_higher: 166 | 167 | ; get target_freq back into hl 168 | add hl, de 169 | 170 | ; sub slide amount from freq 171 | ld a, e 172 | sub a, (ix + channel.slide_amount) 173 | ld e, a 174 | ld a, d 175 | sbc a, 0 176 | ld d, a 177 | 178 | ; check if fnum < (fnum for note C) - some leeway 179 | ; if it is, we need to dec the octave and double the fnum 180 | ; if bit 0 of d is set, the fnum is > 256 181 | bit 0, d 182 | jr nz, mufm_porta_higher_octave_ok 183 | 184 | ; check the lower byte 185 | ld a, e 186 | cp a, 160 187 | jr nc, mufm_porta_higher_octave_ok 188 | 189 | ; double the fnum 190 | add a, a 191 | ld e, a 192 | adc a, d 193 | sub a, e 194 | ; subtract 1 from the octave 195 | sub a, 0x2 196 | ld d, a 197 | 198 | mufm_porta_higher_octave_ok: 199 | ; check if we've passed target_freq 200 | ; if z then freq == target and we're done 201 | ; if nc then freq < target and we're done 202 | ; if nc then we carry on next loop 203 | sbc hl, de 204 | jr z, mufm_porta_end_portamento 205 | jr nc, mufm_porta_end_portamento 206 | jr c, mufm_porta_done 207 | 208 | mufm_porta_end_portamento: 209 | 210 | ; restore target_freq into hl 211 | ; exhange target_freq into de 212 | add hl, de 213 | ex de, hl 214 | 215 | ; cancel the portamento 216 | ld a, 0 217 | ld (ix + channel.slide_type), a 218 | 219 | mufm_porta_done: 220 | 221 | jp music_update_pitch_slide_done 222 | 223 | ; don't include full opll code 224 | .else 225 | 226 | music_update_fm_pitch_slide_upward: 227 | music_update_fm_pitch_slide_downward: 228 | music_update_fm_portamento: 229 | jp music_update_pitch_slide_done 230 | 231 | .endif -------------------------------------------------------------------------------- /music_driver_sdas/opll/pitch_slide.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2024 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; OPLL Pitch slides 9 | ; slide type == 1, upward slide 10 | music_update_fm_pitch_slide_upward: 11 | 12 | ; add slide_amount to lower byte 13 | ld a, e 14 | add a, channel.slide_amount(ix) 15 | ld e, a 16 | 17 | ; add carry to upper byte 18 | adc a, d 19 | sub a, e 20 | ld d, a 21 | 22 | ; we want to check if fnum > 325 23 | 24 | ; isolate bit 8 of fnum 25 | and a, #0x1 26 | ; combine with low byte of fnum - we don't care about the lowest bit 27 | or a, e 28 | ; rotate so a now contains top 8 bits of the 9-bit fnum 29 | rrca 30 | 31 | ; a now contains the fnum / 2 32 | ; check if a > (325 / 2) 33 | cp a, #162 34 | jp c, music_update_pitch_slide_done 35 | 36 | ; a already contains half of the fnum so we can load that into e 37 | ld e, a 38 | 39 | ; clear upper bit of fnum (lowest bit of d) and add 1 to block 40 | ld a, d 41 | and a, #0xfe 42 | add a, #0x2 43 | ld d, a 44 | 45 | ; has block overflowed? 46 | and a, #0xf0 47 | jp z, music_update_pitch_slide_done 48 | 49 | ; cap pitch 50 | ld de, #3990 51 | jp music_update_pitch_slide_done 52 | 53 | 54 | ; slide type == 2, downward slide 55 | music_update_fm_pitch_slide_downward: 56 | 57 | ; sub slide_amount from lower byte 58 | ld a, e 59 | sub a, channel.slide_amount(ix) 60 | ld e, a 61 | 62 | ; sub carry from upper byte 63 | ld a, d 64 | sbc a, e 65 | add a, e 66 | ld d, a 67 | 68 | ; isolate bit 8 of fnum 69 | and a, #0x1 70 | ; combine with low byte of fnum - we don't care about the lowest bit 71 | or a, e 72 | ; rotate so a now contains top 8 bits of the 9-bit fnum 73 | rrca 74 | 75 | ; a now contains the fnum / 2 76 | ; check if a < (120 / 2) 77 | cp a, #60 78 | jp nc, music_update_pitch_slide_done 79 | 80 | ; double the fnum 81 | sla e 82 | sla e 83 | 84 | ; subtract 1 from the block 85 | ld a, d 86 | and a, #0xfe 87 | sub a, #0x2 88 | ld d, a 89 | 90 | ; has block underflowed? 91 | jp nc, music_update_pitch_slide_done 92 | 93 | ; if it has, cap pitch 94 | ld de, #110 95 | jp music_update_pitch_slide_done 96 | 97 | ; handle OPLL portamento 98 | ; note that as the ranges of fnums we're using for the notes C to B span from 99 | ; C = 172 to B = 325 100 | ; we need to move to the next octave when we go too far above/below those fnums 101 | ; otherwise we'll never hit the target_freq's combination of octave and fnum 102 | ; ix: current channel 103 | ; iy: music state 104 | ; de: current channel.freq 105 | ; outputs updated freq in de 106 | music_update_fm_portamento: 107 | 108 | ; get target_freq in hl 109 | ld l, channel.target_freq(ix) 110 | ld h, channel.target_freq+1(ix) 111 | 112 | ; subtract current freq from target_freq 113 | sbc hl, de 114 | jr z, mufm_porta_end_portamento 115 | jr c, mufm_porta_current_higher 116 | 117 | mufm_porta_current_lower: 118 | 119 | ; get target_freq back into hl 120 | add hl, de 121 | 122 | ; add slide amount to freq 123 | ld a, channel.slide_amount(ix) 124 | add a, e 125 | ld e, a 126 | adc a, d 127 | sub a, e 128 | ld d, a 129 | 130 | ; check if fnum > (fnum for note B) + leeway 131 | ; if it is, we need to inc the octave and half the fnum 132 | 133 | ; if bit 0 of d isn't set, the fnum is < 256 134 | bit 0, d 135 | jr z, mufm_porta_lower_octave_ok 136 | 137 | ; check if the lower byte makes the full fnum > 352 138 | ld a, e 139 | cp a, #352-256 140 | jr nc, mufm_porta_lower_octave_ok 141 | 142 | ; halve the fnum 143 | ; or-ing in the 256 bit from the upper byte as a 128 bit 144 | srl a 145 | or a, #0x80 146 | ld e, a 147 | 148 | ; clear the 256 bit from the fnum in the upper byte 149 | ld a, d 150 | and a, #0xfe 151 | ; add 1 to the octave 152 | add a, #0x2 153 | ld d, a 154 | 155 | mufm_porta_lower_octave_ok: 156 | ; check if we've passed target_freq 157 | ; if z then freq == target and we're done 158 | ; if c then freq > target and we're done 159 | ; if nc then we carry on next loop 160 | sbc hl, de 161 | jr z, mufm_porta_end_portamento 162 | jr c, mufm_porta_end_portamento 163 | jr nc, mufm_porta_done 164 | 165 | mufm_porta_current_higher: 166 | 167 | ; get target_freq back into hl 168 | add hl, de 169 | 170 | ; sub slide amount from freq 171 | ld a, e 172 | sub a, channel.slide_amount(ix) 173 | ld e, a 174 | ld a, d 175 | sbc a, #0 176 | ld d, a 177 | 178 | ; check if fnum < (fnum for note C) - some leeway 179 | ; if it is, we need to dec the octave and double the fnum 180 | ; if bit 0 of d is set, the fnum is > 256 181 | bit 0, d 182 | jr nz, mufm_porta_higher_octave_ok 183 | 184 | ; check the lower byte 185 | ld a, e 186 | cp a, #160 187 | jr nc, mufm_porta_higher_octave_ok 188 | 189 | ; double the fnum 190 | add a, a 191 | ld e, a 192 | adc a, d 193 | sub a, e 194 | ; subtract 1 from the octave 195 | sub a, #0x2 196 | ld d, a 197 | 198 | mufm_porta_higher_octave_ok: 199 | ; check if we've passed target_freq 200 | ; if z then freq == target and we're done 201 | ; if nc then freq < target and we're done 202 | ; if nc then we carry on next loop 203 | sbc hl, de 204 | jr z, mufm_porta_end_portamento 205 | jr nc, mufm_porta_end_portamento 206 | jr c, mufm_porta_done 207 | 208 | mufm_porta_end_portamento: 209 | 210 | ; restore target_freq into hl 211 | ; exhange target_freq into de 212 | add hl, de 213 | ex de, hl 214 | 215 | ; cancel the portamento 216 | ld a, #0 217 | ld channel.slide_type(ix), a 218 | 219 | mufm_porta_done: 220 | 221 | jp music_update_pitch_slide_done 222 | 223 | ; don't include full opll code 224 | .else 225 | 226 | music_update_fm_pitch_slide_upward: 227 | music_update_fm_pitch_slide_downward: 228 | music_update_fm_portamento: 229 | jp music_update_pitch_slide_done 230 | 231 | .endif 232 | -------------------------------------------------------------------------------- /music_driver_sdas/banjo.h: -------------------------------------------------------------------------------- 1 | 2 | // banjo sound driver 3 | // Joe Kennedy 2024 4 | 5 | #ifndef __BANJO_H 6 | #define __BANJO_H 7 | 8 | // instructions 9 | #define NOTE_ON 0x00 10 | #define NOTE_OFF 0x01 11 | #define INSTRUMENT_CHANGE 0x02 12 | #define VOLUME_CHANGE 0x03 13 | #define FM_DRUM 0x04 14 | #define SN_NOISE_MODE 0x05 15 | #define SLIDE_UP 0x06 16 | #define SLIDE_DOWN 0x07 17 | #define SLIDE_PORTA 0x08 18 | #define SLIDE_OFF 0x09 19 | #define ARPEGGIO 0x0a 20 | #define ARPEGGIO_OFF 0x0b 21 | #define VIBRATO 0x0c 22 | #define VIBRATO_OFF 0x0d 23 | #define LEGATO_ON 0x0e 24 | #define LEGATO_OFF 0x0f 25 | #define GAME_GEAR_PAN 0x10 26 | #define END_LINE 0x80 27 | 28 | #define MODE_FM 1 29 | #define MODE_SN 2 30 | #define MODE_SN_FM 3 31 | #define MODE_DUAL_SN 4 32 | #define MODE_FM_DRUMS 5 33 | #define MODE_SN_FM_DRUMS 7 34 | 35 | #define CHAN_FLAG_MUTED 0x01 36 | #define CHAN_FLAG_NOTE_ON 0x02 37 | #define CHAN_FLAG_LEGATO 0x04 38 | #define CHAN_FLAG_PITCH_CHANGE 0x08 39 | #define CHAN_FLAG_VOLUME_MACRO 0x10 40 | #define CHAN_FLAG_VIBRATO 0x20 41 | #define CHAN_FLAG_ARPEGGIO 0x40 42 | #define CHAN_FLAG_SLIDE 0x80 43 | 44 | // used in song/sfx table definitions 45 | #define SFX_DEF(SFX_LABEL, SFX_BANK, SFX_PRIORITY) { SFX_PRIORITY, SFX_BANK, &SFX_LABEL } 46 | #define SONG_DEF(SONG_LABEL, SONG_BANK) { 0, SONG_BANK, &SONG_LABEL } 47 | 48 | typedef struct instrument_s { 49 | 50 | unsigned char volume_macro_len; 51 | unsigned char volume_macro_loop; 52 | const unsigned char * volume_macro_ptr; 53 | unsigned char fm_preset; 54 | unsigned char fm_patch[8]; 55 | 56 | } instrument_t; 57 | 58 | typedef struct song_data_s { 59 | unsigned char magic_byte; 60 | unsigned char bank; 61 | unsigned char channel_count; 62 | unsigned char loop; 63 | unsigned char sfx_channel; 64 | unsigned char has_sn; 65 | unsigned char has_fm; 66 | unsigned char has_fm_drums; 67 | unsigned char time_base; 68 | unsigned char speed_1; 69 | unsigned char speed_2; 70 | unsigned char pattern_length; 71 | unsigned char orders_length; 72 | const instrument_t * const * instrument_pointers; 73 | const unsigned char * const * const * order_pointers; 74 | unsigned char subtic; 75 | unsigned char tic; 76 | unsigned char line; 77 | unsigned char order; 78 | unsigned char process_new_line; 79 | unsigned char noise_mode; 80 | unsigned char panning; 81 | const unsigned char channel_types[32]; 82 | } song_data_t; 83 | 84 | typedef struct channel_s { 85 | unsigned char flags; 86 | unsigned char type; 87 | unsigned char subchannel; 88 | unsigned char port; 89 | 90 | unsigned int freq; 91 | unsigned int target_freq; 92 | 93 | unsigned char volume; 94 | unsigned char midi_note; 95 | unsigned char instrument_num; 96 | 97 | unsigned char slide_amount; 98 | unsigned char slide_type; 99 | 100 | unsigned char vibrato_current; 101 | unsigned char vibrato_target; 102 | unsigned char vibrato_counter; 103 | unsigned char vibrato_counter_add; 104 | 105 | unsigned char arpeggio_pos; 106 | unsigned char arpeggio; 107 | 108 | unsigned int order_table_ptr; 109 | unsigned int pattern_ptr; 110 | unsigned char line_wait; 111 | 112 | unsigned char volume_macro_len; 113 | unsigned char volume_macro_pos; 114 | unsigned char volume_macro_loop; 115 | unsigned int volume_macro_ptr; 116 | 117 | unsigned char fm_patch_shifted; 118 | unsigned char fm_drum_trigger; 119 | unsigned char fm_drum_volume_mask; 120 | } channel_t; 121 | 122 | typedef struct song_s { 123 | 124 | unsigned char priority; 125 | unsigned char bank; 126 | const song_data_t * song; 127 | 128 | } song_t; 129 | 130 | // stores the mode after it's set in banjo_init 131 | extern unsigned char banjo_mode; 132 | 133 | // flags whether the FM unit is installed 134 | extern unsigned char banjo_fm_unit_present; 135 | 136 | // flags whether we're running on a Game Gear 137 | extern unsigned char banjo_game_gear_mode; 138 | 139 | // flags whether we're running on Sega System E 140 | extern unsigned char banjo_system_e; 141 | 142 | extern song_data_t song_state; 143 | extern channel_t song_channels[]; 144 | 145 | extern song_data_t sfx_state; 146 | extern channel_t sfx_channel; 147 | 148 | // sets banjo_fm_unit_present to 1 if an fm unit is installed, 0 otherwise 149 | // sets banjo_game_gear_mode to 1 if it's detected that wer're running on a game gear in game gear mode, 0 otherwise 150 | void banjo_check_hardware(void); 151 | 152 | // initialise banjo for the given mode 153 | void banjo_init(unsigned char mode); 154 | 155 | // Queues ON: 156 | 157 | // handle song/sfx queues and update playing song/sfx 158 | // this will change the bank for slot 2 (i.e. 0x8000 to 0xbfff (writes to mapper at 0xffff)) 159 | void banjo_update(void); 160 | 161 | // queue song/sfx to be played back starting on next banjo_update 162 | void banjo_queue_song(unsigned char song); 163 | void banjo_queue_sfx(unsigned char sfx); 164 | 165 | // set the loop mode for the song/sfx 166 | // by default songs loop, but sfx don't 167 | void banjo_queue_song_loop_mode(unsigned char loop); 168 | void banjo_queue_sfx_loop_mode(unsigned char loop); 169 | 170 | // set up pointers to the song and sfx tables 171 | void banjo_set_song_table(const song_t *song_table_ptr); 172 | void banjo_set_sfx_table(const song_t *sfx_table_ptr); 173 | 174 | // Queues OFF: 175 | 176 | // start playing song/sfx 177 | // change to the song/sfx's bank before calling this 178 | void banjo_play_song(const song_data_t *song_ptr, unsigned char loop_mode); 179 | void banjo_play_sfx(const song_data_t *song_ptr, unsigned char loop_mode); 180 | 181 | // update song/sfx if one is playing 182 | // change to the song/sfx's bank before calling this 183 | void banjo_update_song(void); 184 | void banjo_update_sfx(void); 185 | 186 | // Queues both ON and OFF: 187 | 188 | // stop the currently playing song/sfx 189 | void banjo_song_stop(void); 190 | void banjo_sfx_stop(void); 191 | 192 | // resume playback of a stopped song 193 | void banjo_song_resume(void); 194 | 195 | // mute the given song channel 196 | void banjo_mute_song_channel(unsigned char chan); 197 | 198 | // unmute the given song channel 199 | // when handling your own banking and using opll fm with custom instruments 200 | // change to the song's bank before calling this to properly restore the custom instrument patch 201 | void banjo_unmute_song_channel(unsigned char chan); 202 | 203 | #endif -------------------------------------------------------------------------------- /convert_wladx_sdas.js: -------------------------------------------------------------------------------- 1 | // 2 | // Converts instructions from wla-dx to sdas format 3 | // e.g. ld a, 4 => ld a, #4 4 | // ld a, (ix + 4) => ld a, 4(ix) 5 | // 6 | // Joe Kennedy 2024 7 | // 8 | 9 | const fs = require('fs'); 10 | const readline = require('readline'); 11 | 12 | const register_names = ["a", "b", "c", "d", "e", "h", "l", "af", "bc", "de", "hl", "ix", "iy", "sp"]; 13 | 14 | function rearrange(tokens) 15 | { 16 | if (tokens.length == 0) 17 | { 18 | return tokens; 19 | } 20 | 21 | if (tokens.length == 1) 22 | { 23 | // just return tokens if it's a register on its own 24 | if (register_names.indexOf(tokens[0]) != -1) 25 | { 26 | return tokens; 27 | } 28 | } 29 | 30 | // if the first character is an upper/lower byte selector, make the following an immediate value 31 | if (tokens[0] == "<" || tokens[0] == ">") 32 | { 33 | tokens.splice(0, 0, "#"); 34 | return tokens; 35 | } 36 | 37 | // either immediate addressing or using index register 38 | if (tokens[0] == "(") 39 | { 40 | // index registers 41 | if ((tokens[1] == "ix" || tokens[1] == "iy") && tokens[2] == "+") 42 | { 43 | return reorganise_ix_iy(tokens); 44 | } 45 | 46 | return tokens; 47 | } 48 | else 49 | { 50 | tokens.splice(0, 0, "#"); 51 | return tokens; 52 | } 53 | } 54 | 55 | // transforms 56 | // (ix + 5) -> 5(ix) 57 | // (ix + n) -> n(ix) 58 | function reorganise_ix_iy(tokens) 59 | { 60 | let out = tokens.slice(3, tokens.length - 1); 61 | out.push("(") 62 | out.push(tokens[1]); 63 | out.push(")"); 64 | return out; 65 | } 66 | 67 | // split asm line into tokens 68 | async function tokenize_lines() 69 | { 70 | let full_output = []; 71 | 72 | let fileStream = fs.createReadStream(process.argv[2]); 73 | 74 | const rl = readline.createInterface({ 75 | input: fileStream, 76 | crlfDelay: Infinity, 77 | }); 78 | 79 | for await (const line of rl) 80 | { 81 | let output = []; 82 | let accumulator = ""; 83 | 84 | let inside_instruction = false; 85 | 86 | for (var i = 0; i < line.length; i++) 87 | { 88 | // start of comment 89 | if (line[i] == ";") 90 | { 91 | if (accumulator.length > 0) 92 | { 93 | output.push(accumulator); 94 | accumulator = ""; 95 | } 96 | 97 | output.push(line[i]); 98 | output.push(line.substring(i + 1, line.length)); 99 | break; 100 | } 101 | 102 | // whitespace 103 | else if (" \t".indexOf(line[i]) != -1) 104 | { 105 | if (accumulator.length > 0) 106 | { 107 | output.push(accumulator); 108 | accumulator = ""; 109 | } 110 | 111 | // if this whitespace came before the instruction 112 | // then keep it to preserve indentation, otherwise ignore it 113 | if (inside_instruction == false) 114 | { 115 | output.push(line[i]); 116 | } 117 | } 118 | 119 | // these chars usually delimit in some way 120 | else if (",()+-|&<>".indexOf(line[i]) != -1) 121 | { 122 | inside_instruction = true; 123 | 124 | if (accumulator.length > 0) 125 | { 126 | output.push(accumulator); 127 | accumulator = ""; 128 | } 129 | 130 | output.push(line[i]); 131 | } 132 | 133 | // append this character to the accumulator 134 | else 135 | { 136 | inside_instruction = true; 137 | 138 | accumulator = accumulator + line[i]; 139 | } 140 | 141 | // write the accumulator if this is the last character 142 | if (i == line.length - 1 && accumulator.length > 0) 143 | { 144 | output.push(accumulator); 145 | accumulator = ""; 146 | } 147 | } 148 | 149 | full_output.push(output); 150 | } 151 | 152 | return full_output; 153 | } 154 | 155 | // process the tokens 156 | async function process_lines(lines) 157 | { 158 | // we're expecting certain labels to be declared in C or used in C 159 | // add an underscore in front of these symbols so they match the C declared ones 160 | let underscore_prefix = [ 161 | "song_channels", "song_state", "song_table", 162 | "sfx_table", "sfx_channel", "sfx_state", 163 | "banjo_init", "banjo_init:", 164 | "banjo_update", "banjo_update:", 165 | "banjo_queue_song", "banjo_queue_song:", 166 | "banjo_queue_sfx", "banjo_queue_sfx:", 167 | "banjo_play_song", "banjo_play_song:", 168 | "banjo_play_sfx", "banjo_play_sfx:", 169 | "banjo_update_song", "banjo_update_song:", 170 | "banjo_update_sfx", "banjo_update_sfx:", 171 | "banjo_mute_song_channel", "banjo_mute_song_channel:", 172 | "banjo_unmute_song_channel", "banjo_unmute_song_channel:", 173 | "banjo_set_song_table:", "banjo_set_sfx_table:", 174 | "banjo_check_hardware:", "banjo_song_stop:", "banjo_sfx_stop:", 175 | "banjo_queue_song_loop_mode:", "banjo_queue_sfx_loop_mode:", 176 | "banjo_fm_unit_present", "banjo_game_gear_mode", "banjo_system_e", "banjo_mode", 177 | ]; 178 | 179 | var outfile = fs.createWriteStream(process.argv[3], {flags: "w+"}); 180 | 181 | console.log(lines); 182 | 183 | for (let i = 0; i < lines.length; i++) 184 | { 185 | let line = lines[i]; 186 | 187 | let opcode = null; 188 | let dest = []; 189 | let source = []; 190 | 191 | let whitespace = ""; 192 | let comment = ""; 193 | 194 | let search = "opcode"; 195 | 196 | for (let j = 0; j < line.length; j++) 197 | { 198 | let token = line[j]; 199 | 200 | // handle comments 201 | if (token == ";") 202 | { 203 | comment = ";" + line[j + 1]; 204 | j++; 205 | } 206 | 207 | // handle whitespace 208 | else if (token == " " || token == "\t") 209 | { 210 | whitespace += token; 211 | } 212 | 213 | // found the opcode 214 | else if (search == "opcode") 215 | { 216 | if (underscore_prefix.indexOf(token) != -1) 217 | { 218 | token = "_" + token; 219 | } 220 | 221 | opcode = token; 222 | search = "dest"; 223 | } 224 | 225 | // found a destination token 226 | else if (search == "dest") 227 | { 228 | if (token == ",") 229 | { 230 | search = "source"; 231 | } 232 | else 233 | { 234 | dest.push(token); 235 | } 236 | } 237 | 238 | // found a source token 239 | else if (search == "source") 240 | { 241 | source.push(token); 242 | } 243 | } 244 | 245 | // add underscore to front of selected symbols 246 | for (let k = 0; k < dest.length; k++) 247 | { 248 | if (underscore_prefix.indexOf(dest[k]) != -1) 249 | { 250 | dest[k] = "_" + dest[k]; 251 | } 252 | } 253 | 254 | for (let k = 0; k < source.length; k++) 255 | { 256 | if (underscore_prefix.indexOf(source[k]) != -1) 257 | { 258 | source[k] = "_" + source[k]; 259 | } 260 | } 261 | 262 | // 263 | // update opcode parameters to sdas formatting 264 | // 265 | 266 | // make port for out immediate if it's not register c 267 | if (opcode == "out") 268 | { 269 | if (dest[0] == "(" && dest[1] != "c") 270 | { 271 | dest.splice(1, 0, "#"); 272 | } 273 | } 274 | 275 | else if (opcode == "in") 276 | { 277 | if (source[0] == "(" && source[1] != "c") 278 | { 279 | source.splice(1, 0, "#"); 280 | } 281 | } 282 | 283 | else if (opcode == "ld") 284 | { 285 | source = rearrange(source); 286 | dest = rearrange(dest); 287 | } 288 | 289 | else if (opcode == "cp" || opcode == "add" || opcode == "adc" || opcode == "sub" || opcode == "sbc" || opcode == "and" || opcode == "or" || opcode == "xor") 290 | { 291 | source = rearrange(source); 292 | } 293 | 294 | else if (opcode == "inc" || opcode == "dec") 295 | { 296 | dest = rearrange(dest); 297 | } 298 | 299 | else if (opcode == "bit" || opcode == "set" || opcode == "res") 300 | { 301 | source = rearrange(source); 302 | } 303 | 304 | outfile.write( 305 | whitespace + 306 | (opcode ? (opcode + " ") : "") + 307 | (dest.length ? dest.join("") : "") + 308 | (source.length ? (", " + source.join("")) : "") + 309 | comment + 310 | "\n" 311 | ); 312 | } 313 | 314 | //console.log(output); 315 | } 316 | 317 | async function main() 318 | { 319 | let lines = await tokenize_lines(); 320 | process_lines(lines); 321 | } 322 | 323 | main(); -------------------------------------------------------------------------------- /music_driver_sdas/opll/note_on_off.inc: -------------------------------------------------------------------------------- 1 | 2 | ; banjo sound driver 3 | ; Joe Kennedy 2023 4 | 5 | ; include full opll code 6 | .ifdef INCLUDE_OPLL 7 | 8 | ; b : current channel number 9 | ; hl : pointer to current instruction in pattern 10 | ; ix : pointer to current channel 11 | ; iy : pointer to music state 12 | music_fm_note_on: 13 | 14 | ; restore original hl 15 | ex de, hl 16 | 17 | ; send note off if there's a note currently playing and we're not playing legato 18 | bit CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 19 | jr z, mufm_no_legato_done 20 | 21 | bit CHAN_FLAG_BIT_LEGATO, channel.flags(ix) 22 | jr nz, mufm_no_legato_done 23 | 24 | ; send note off 25 | ld a, channel.subchannel(ix) 26 | add a, #0x20 27 | out (#OPLL_REG_PORT), a 28 | 29 | ld a, channel.freq+1(ix) 30 | and a, #0xf 31 | out (#OPLL_DATA_PORT), a 32 | 33 | ; delay after opll data write 34 | push hl 35 | pop hl 36 | push hl 37 | pop hl 38 | push hl 39 | pop hl 40 | 41 | mufm_no_legato_done: 42 | 43 | ; select volume and instrument register to write to 44 | ld a, channel.subchannel(ix) 45 | add a, #0x30 46 | out (#OPLL_REG_PORT), a 47 | 48 | ; volume 49 | ld a, channel.volume(ix) 50 | and a, #0xf 51 | 52 | ; combine with shifted patch number 53 | or a, channel.fm_patch_shifted(ix) 54 | 55 | ; write data 56 | out (#OPLL_DATA_PORT), a 57 | 58 | ; update note on status 59 | set CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 60 | 61 | ; update midi number field 62 | inc hl 63 | ld a, (hl) 64 | ld channel.midi_note(ix), a 65 | 66 | ; look up tone data by note number 67 | ld de, #fm_tone_lookup 68 | add a, a 69 | add a, e 70 | ld e, a 71 | adc a, d 72 | sub a, e 73 | ld d, a 74 | 75 | ; check whether portamento is enabled so 76 | ; we need to store the fnum in target frequency instead 77 | ld a, channel.slide_type(ix) 78 | cp a, #SLIDE_TYPE_PORTA 79 | jr z, mfmno_portamento 80 | 81 | mfmno_no_portamento: 82 | 83 | ; reset current vibrato level 84 | ld channel.vibrato_current(ix), #VIBRATO_CENTRE 85 | 86 | ; store lower byte of fnum 87 | ld a, (de) 88 | ld channel.freq(ix), a 89 | 90 | ; store octave and 9th bit of f number 91 | inc de 92 | ld a, (de) 93 | ld channel.freq+1(ix), a 94 | 95 | jr mfmno_done 96 | 97 | mfmno_portamento: 98 | 99 | ; store lower byte of fnum 100 | ld a, (de) 101 | ld channel.target_freq(ix), a 102 | 103 | ; store octave and 9th bit of f number 104 | inc de 105 | ld a, (de) 106 | ld channel.target_freq+1(ix), a 107 | 108 | ; check high byte of freq 109 | ; if it's 0xff that implies no notes have played so far 110 | ; so we write to freq too 111 | ld a, channel.freq+1(ix) 112 | cp a, #0xff 113 | dec de 114 | jr z, mfmno_no_portamento 115 | 116 | mfmno_done: 117 | inc hl 118 | jp music_process_new_line_subloop 119 | 120 | ; b : current channel number 121 | ; hl : pointer to current instruction in pattern 122 | ; ix : pointer to current channel 123 | ; iy : pointer to music state 124 | music_fm_note_off: 125 | 126 | ; restore original hl 127 | ex de, hl 128 | 129 | ; clear note on flag 130 | res CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 131 | 132 | ; select octave + key on register 133 | ld a, channel.subchannel(ix) 134 | add a, #0x20 135 | out (#OPLL_REG_PORT), a 136 | 137 | ; write current octave + key off 138 | ld a, channel.freq+1(ix) 139 | and a, #0xf 140 | out (#OPLL_DATA_PORT), a 141 | 142 | ; delay after opll data write 143 | push hl 144 | pop hl 145 | push hl 146 | pop hl 147 | push hl 148 | pop hl 149 | 150 | inc hl 151 | jp music_process_new_line_subloop 152 | 153 | ; b : current channel number 154 | ; hl : pointer to current instruction in pattern 155 | ; ix : pointer to current channel 156 | ; iy : pointer to music state 157 | music_fm_drums_note_on: 158 | 159 | ; restore original hl 160 | ex de, hl 161 | 162 | ; send note off if we're not playing legato and there's a note currently playing 163 | bit CHAN_FLAG_BIT_NOTE_ON, channel.flags(ix) 164 | jr z, mfmdno_legato_done 165 | 166 | bit CHAN_FLAG_BIT_LEGATO, channel.flags(ix) 167 | jr nz, mfmdno_legato_done 168 | 169 | ; select rhythm trigger register 170 | ld a, #0xe 171 | out (#OPLL_REG_PORT), a 172 | 173 | ; mask out current bit for this drum's trigger 174 | ld a, channel.fm_drum_trigger(ix) 175 | and a, #0x1f 176 | cpl 177 | ld c, a 178 | 179 | ; get current drum state 180 | ld a, (fm_drum_note_ons) 181 | ; mask out this drum's bit and update the triggers to note-off 182 | and a, c 183 | out (#OPLL_DATA_PORT), a 184 | 185 | ; delay after opll data write 186 | push hl 187 | pop hl 188 | push hl 189 | pop hl 190 | nop 191 | nop 192 | nop 193 | 194 | mfmdno_legato_done: 195 | 196 | ; volume 197 | ; select volume register for this drum 198 | ld a, channel.port(ix) 199 | add a, #0x20 200 | out (#OPLL_REG_PORT), a 201 | 202 | ; get volume and store in c 203 | ld c, channel.volume(ix) 204 | 205 | ; point de at current volume status for this register 206 | ; and store in e, keep de around for later 207 | ld d, #>fm_drum_volumes 208 | ld a, #