Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;"2nd_music_driver_2025-06-20\main.p8":
- %zeropage basicsafe
- %import music
- song_data {
- %option ignore_unused
- const ubyte PULSE = %00000000
- const ubyte SAWTOOTH = %01000000
- const ubyte TRIANGLE = %10000000
- const ubyte NOISE = %11000000
- const ubyte SILENT = %00000000
- const ubyte LEFT = %01000000
- const ubyte RIGHT = %10000000
- const ubyte STEREO = %11000000
- ; 6 bytes per instrument
- const ubyte INSTRUMENTS_LEN = sizeof(instruments)/6
- ; +1 for tick speed
- const ubyte ORDERS_LEN = sizeof(orders)/(CHANNELS+1)
- ; 2 bytes per pattern pointer
- const ubyte PATTERNS_LEN = sizeof(patterns)/2
- ; This is what you pass to music.play()
- ; Note: mkword's first arg is the high byte, not the low byte!
- uword[] @nosplit info = [mkword(INSTRUMENTS_LEN, CHANNELS),
- mkword(PATTERNS_LEN, ORDERS_LEN),
- mkword(0, 0), ; (Reserved)
- &instruments, &orders, &patterns]
- const uword INS_NULL = 0
- const uword INS_SAWLONG = 1
- const uword INS_PLSSHRT = 2
- const uword INS_PLSLONG = 3
- const uword INS_NOISHRT_L = 4
- const uword INS_NOISHRT_R = 5
- const uword INS_NOILONG_L = 6
- const uword INS_NOILONG_R = 7
- const uword INS_NOISHRT = 8
- const uword INS_NOILONG = 9
- const uword INS_CHIRP = 10
- ; WAVEFORM|WIDTH, EAR_CHANLS|VOL, MAXVOL_6b, ATTACK_8b, SUSTAIN_8b, RELEASE_8b
- ;
- ; From psg.envelope()'s remarks:
- ; maxvolume = 0-63
- ; attack, sustain, release = 0-255 that determine the speed of the A/D/R:
- ; attack time: MAXVOL/15/attack sec. higher value = faster attack.
- ; sustain time: sustain/60 sec. higher value = longer sustain.
- ; release time: MAXVOL/15/release sec. higher vaule = faster release.
- ;
- ; (A max volume of 0 is interpreted as 'no note', which can be used for
- ; envelopes longer than the row it resides in, as that current envelope
- ; will continue until the next row with an instrument without a maxvol of 0.)
- ubyte[] instruments = [ 0, 0, 0, 255, 0, 255, ; 0 NULL
- SAWTOOTH, STEREO|0, 30, 208, 0, 5, ; 1 SAWLONG
- PULSE|48, STEREO|0, 33, 170, 5, 44, ; 2 PLSSHRT
- PULSE|38, STEREO|0, 33, 170, 13, 44, ; 3 PLSLONG
- NOISE , LEFT|0, 19, 58, 0, 16, ; 4 NOISHRT_L
- NOISE , RIGHT|0, 19, 58, 0, 16, ; 5 NOISHRT_R
- NOISE , LEFT|0, 19, 58, 0, 8, ; 6 NOILONG_L
- NOISE , RIGHT|0, 19, 58, 0, 8, ; 7 NOILONG_R
- NOISE , STEREO|0, 14, 58, 0, 16, ; 8 NOISHRT
- NOISE , STEREO|0, 14, 58, 0, 8, ; 9 NOILONG
- PULSE| 9, STEREO|0, 21, 208, 0, 6] ; 10 CHIRP
- ; Accepts values 1-6 (ORDERS_LEN = sizeof(orders)/CHANNELS)
- const ubyte CHANNELS = 5
- ; Contains indices into the patterns array, where the actual
- ; index equals i-1, and a value of 0 means no pattern at all
- ;
- ; The first byte of an order entry however, is the tick speed (frames per row)
- ubyte[] orders = [
- 8, 1, 2, 3, 4, 5,
- ]
- ; This is an array of pointers
- ;
- ; In each pattern, the first byte correlates to its length mask.
- ; For example, a pattern with a length of 64 would have a mask of 63
- ; (Pattern length must be a power of 2!)
- ;
- ; If the 2nd byte is nonzero, that pattern's array lacks the @nosplit tag.
- ;
- ; The words after are the pattern's rows:
- ; Bits 0-6 are: The note index; piano key starting from C0 (0-119)
- ; (A note index of 0 indicates a silent note)
- ; Bits 7-F are: The instrument index (0-511)
- uword[] @nosplit patterns = [pattern_hatLR,
- pattern_hatST,
- pattern_saw,
- pattern_pulse,
- pattern_chirp]
- ; 1
- uword[] @nosplit pattern_hatLR = [mkword(0,7), ; Mask of 7, for 8 notes total
- INS_NOISHRT_L<<7 | piano.KEY_C9,
- INS_NOISHRT_R<<7 | piano.KEY_C9,
- INS_NOILONG_L<<7 | piano.KEY_D9S,
- INS_NULL <<7 | 0,
- INS_NOISHRT_R<<7 | piano.KEY_C9,
- INS_NOISHRT_L<<7 | piano.KEY_C9,
- INS_NOILONG_R<<7 | piano.KEY_D9S,
- INS_NULL <<7 | 0]
- ; 2
- uword[] @nosplit pattern_hatST = [mkword(0,7), ; Mask of 7, for 8 notes total
- INS_NOISHRT<<7 | piano.KEY_C9,
- INS_NOISHRT<<7 | piano.KEY_C9,
- INS_NOILONG<<7 | piano.KEY_D9S,
- INS_NULL <<7 | 0,
- INS_NOISHRT<<7 | piano.KEY_C9,
- INS_NOISHRT<<7 | piano.KEY_C9,
- INS_NOILONG<<7 | piano.KEY_D9S,
- INS_NULL <<7 | 0]
- ; 3
- uword[] @nosplit pattern_saw = [mkword(0,63), ; 64 notes total
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2S,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2S,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2S,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_C3,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_A2S,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_F2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_F2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_F2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_D2S,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_SAWLONG<<7 | piano.KEY_G2S,
- INS_NULL <<7 | 0]
- uword[] @nosplit pattern_pulse = [mkword(0,63), ; 64 notes total
- ; 0 -> 7:
- INS_PLSSHRT<<7 | piano.KEY_F3,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSLONG<<7 | piano.KEY_G4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSLONG<<7 | piano.KEY_D4S,
- INS_NULL <<7 | 0,
- ; 8 -> 15:
- INS_PLSSHRT<<7 | piano.KEY_F3,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSLONG<<7 | piano.KEY_G4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSLONG<<7 | piano.KEY_D4S,
- INS_NULL <<7 | 0,
- ; 16 -> 23:
- INS_PLSSHRT<<7 | piano.KEY_D3S,
- INS_PLSSHRT<<7 | piano.KEY_G3,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSLONG<<7 | piano.KEY_G4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSLONG<<7 | piano.KEY_D4S,
- INS_NULL <<7 | 0,
- ; 24 -> 31:
- INS_PLSSHRT<<7 | piano.KEY_D3S,
- INS_PLSSHRT<<7 | piano.KEY_G3,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSLONG<<7 | piano.KEY_G4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_C4,
- INS_PLSSHRT<<7 | piano.KEY_D4S,
- INS_PLSSHRT<<7 | piano.KEY_C4S,
- ; 32 -> 39:
- INS_PLSSHRT<<7 | piano.KEY_C3S,
- INS_PLSSHRT<<7 | piano.KEY_F3,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSLONG<<7 | piano.KEY_D4S,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSLONG<<7 | piano.KEY_C4,
- INS_NULL <<7 | 0,
- ; 40 -> 47:
- INS_PLSSHRT<<7 | piano.KEY_C3S,
- INS_PLSSHRT<<7 | piano.KEY_F3,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSLONG<<7 | piano.KEY_D4S,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_G3S,
- INS_PLSLONG<<7 | piano.KEY_C4,
- INS_NULL <<7 | 0,
- ; 48 -> 55:
- INS_PLSSHRT<<7 | piano.KEY_D3S,
- INS_PLSSHRT<<7 | piano.KEY_G3,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSLONG<<7 | piano.KEY_F4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSLONG<<7 | piano.KEY_C4S,
- INS_NULL <<7 | 0,
- ; 56 -> 63:
- INS_PLSSHRT<<7 | piano.KEY_D3S,
- INS_PLSSHRT<<7 | piano.KEY_G3,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSLONG<<7 | piano.KEY_F4,
- INS_NULL <<7 | 0,
- INS_PLSSHRT<<7 | piano.KEY_A3S,
- INS_PLSSHRT<<7 | piano.KEY_C4S,
- INS_PLSSHRT<<7 | piano.KEY_C4]
- const byte OCTAVE = 12
- const byte CHIRPMOD = OCTAVE * 0
- uword[] @nosplit pattern_chirp = [mkword(0,15), ; 16 notes total
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_NULL <<7 | 0,
- INS_CHIRP<<7 | (piano.KEY_G4 + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_G4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_A4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_C5 + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_G4 + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_G4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_A4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_C5 + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_G4 + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_G4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_A4S + CHIRPMOD),
- INS_CHIRP<<7 | (piano.KEY_C5 + CHIRPMOD)]
- }
- main {
- sub start() {
- cx16.screen_set_charset(3+2, 0) ; +2 for upper/lower charset's thin variant
- txt.clear_screen()
- music.init()
- music.play(song_data.info)
- music_debug.print_info()
- uword frame = 0
- repeat {
- if (frame%music.STATE_ticksPerRow) == 0 {
- ;txt.print("whichOrder = ")
- ;printn.uw_dec(music.STATE_whichOrder)
- ;txt.print(", whichRow = ")
- txt.print("whichRow = ")
- printn.ub_dec(music.STATE_whichRow)
- txt.nl()
- }
- sys.waitvsync()
- frame++
- }
- }
- }
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;"2nd_music_driver_2025-06-20\music.p8":
- ; - PUBLIC ROUTINES -
- ; music.init() (only needs to be called once!)
- ; music.play(ptr_info)
- ; music.stop()
- ; music.stop_forced()
- %import piano
- ; - EXAMPLE SONG -
- ; CAN BE PLAYED WITH THESE 2 LINES:
- ;music.init()
- ;music.play(song_example.info)
- song_example {
- %option ignore_unused
- const ubyte PULSE = %00000000
- const ubyte SAWTOOTH = %01000000
- const ubyte TRIANGLE = %10000000
- const ubyte NOISE = %11000000
- const ubyte SILENT = %00000000
- const ubyte LEFT = %01000000
- const ubyte RIGHT = %10000000
- const ubyte STEREO = %11000000
- ; 6 bytes per instrument
- const ubyte INSTRUMENTS_LEN = sizeof(instruments)/6
- ; +1 for tick speed
- const ubyte ORDERS_LEN = sizeof(orders)/(CHANNELS+1)
- const ubyte PATTERNS_LEN = sizeof(patterns)/2
- ; This is what you pass to music.play()
- ; Note: mkword's first arg is the high byte, not the low byte!
- uword[] @nosplit info = [mkword(INSTRUMENTS_LEN, CHANNELS),
- mkword(PATTERNS_LEN, ORDERS_LEN),
- mkword(0, 0), ; (Reserved)
- &instruments, &orders, &patterns]
- ; WAVEFORM|WIDTH, EAR_CHANLS|VOL, MAXVOL_6b, ATTACK_8b, SUSTAIN_8b, RELEASE_8b
- ;
- ; From psg.envelope()'s remarks:
- ; maxvolume = 0-63
- ; attack, sustain, release = 0-255 that determine the speed of the A/D/R:
- ; attack time: MAXVOL/15/attack sec. higher value = faster attack.
- ; sustain time: sustain/60 sec. higher value = longer sustain.
- ; release time: MAXVOL/15/release sec. higher vaule = faster release.
- ;
- ; (A max volume of 0 is interpreted as 'no note', which can be used for
- ; envelopes longer than the row it resides in, as that current envelope
- ; will continue until the next row with an instrument without a maxvol of 0.)
- ubyte[] instruments = [PULSE|63, STEREO|63, 63, 255, 10, 128,
- TRIANGLE, STEREO|48, 63, 4, 60, 4]
- ; Accepts values 1-6 (ORDERS_LEN = sizeof(orders)/CHANNELS)
- const ubyte CHANNELS = 6
- ; Contains indices into the patterns array, where the actual
- ; index equals i-1, and a value of 0 means no pattern at all
- ;
- ; The first byte of an order entry however, is the tick speed (frames per row)
- ubyte[] orders = [12, 1, 2, 0, 0, 0, 0]
- ; This is an array of pointers
- ;
- ; In each pattern, the first byte correlates to its length mask.
- ; For example, a pattern with a length of 64 would have a mask of 63
- ; (Pattern length must be a power of 2!)
- ;
- ; If the 2nd byte is nonzero, that pattern's array lacks the @nosplit tag.
- ;
- ; The words after are the pattern's rows:
- ; Bits 0-6 are: The note index; piano key starting from C0# (1-119)
- ; (A note index of 0 indicates a silent note)
- ; Bits 7-F are: The instrument index (0-511)
- uword[] @nosplit patterns = [pattern_1, pattern_2]
- uword[] @nosplit pattern_1 = [mkword(0,3), ; Mask of 3, for a total of 4 notes
- 0<<7 | piano.KEY_C4,
- 0<<7 | piano.KEY_G4,
- 0<<7 | piano.KEY_C5,
- 0<<7 | piano.KEY_G4]
- uword[] @nosplit pattern_2 = [mkword(0,0), ; Mask of 0, for a total of 1 note
- 1<<7 | piano.KEY_C4]
- }
- ; Uses (at most) the upper 12 PSG channels
- music {
- %option ignore_unused
- const uword NULL = 0 ; To make null pointer comparisons a bit more readable
- ; Byte indexes within song header
- const ubyte INDEX_CHANNELS = 0 ; LSB of uword 0
- const ubyte INDEX_INSTRUMENTS_LEN = 1 ; MSB of uword 0
- const ubyte INDEX_ORDERS_LEN = 2 ; LSB of uword 1
- const ubyte INDEX_PATTERNS_LEN = 3 ; MSB of uword 1
- ;const ubyte INDEX_? = 4 ; LSB of uword 2
- ;const ubyte INDEX_? = 5 ; MSB of uword 2
- const ubyte INDEX_INSTRUMENTS = 6
- const ubyte INDEX_ORDERS = 8
- const ubyte INDEX_PATTERNS = 10
- ; - SONG DATA (AKA CONTENTS OF SONG INFO) -
- ubyte SONG_channels
- ubyte SONG_instruments_len
- ubyte SONG_orders_len
- ubyte SONG_patterns_len
- ubyte SONG_order_size ; = channels+1 (used for alignment, mostly)
- uword SONG_ptr_info
- uword SONG_ptr_instruments
- uword SONG_ptr_orders
- uword SONG_ptr_patterns
- ; - STATE VARIABLES -
- bool STATE_isInit
- bool STATE_isPlaying
- uword STATE_whichOrder ; Remember, this is a uword, not a ubyte!
- ubyte STATE_whichRow
- ubyte STATE_whichTick ; Actually decrements, loading a new row at 0
- ubyte STATE_ticksPerRow
- ubyte STATE_highestMask
- ; For every note, switch to an alternate channel
- ; so that envelopes aren't cut off prematurely
- ; (This is never reinitialized on purpose, as it's redundant!)
- ;
- ; (Also, this is of type ubyte[] instead of bool[],
- ; so I'm able to apply an XOR to easily flip the boolean)
- ubyte[6] STATE_altChannel
- ; Determines the length of each pattern
- ubyte[6] STATE_rowMasks
- ; Pointers to the currently loaded patterns
- uword[6] STATE_ptrs_curOrder
- sub init() {
- if STATE_isInit return
- psg.init()
- cx16.enable_irq_handlers(false)
- cx16.set_vsync_irq_handler(&music.irq_routine)
- STATE_isInit = true
- }
- asmsub get_voice_num(ubyte channel @Y) clobbers(A) -> ubyte @Y {
- ; Calculates the raw voice index, based on a channel index of 0 -> 5
- ; returns: (4 + Y<<1 + alt_channel[Y])
- ; (I might've been able to save some bytes by storing the boolean in X,
- ; but that would clobber another register, which is probably worse)
- %asm {{
- lda p8v_STATE_altChannel, y
- beq _dont_increment ; If boolean is false, don't increment result
- ;_increment:
- tya
- asl ; Y<<1
- clc
- adc #4 ; +4
- tay
- iny ; +alt_channel[Y]
- rts
- _dont_increment:
- tya
- asl ; Y<<1
- clc
- adc #4 ; +4
- tay
- rts
- }}
- }
- ; Like peekw, but for split arrays
- ; (I don't have time to make this an asmsub right now unfortunately)
- ; TODO: This doesn't work in the context in which it's used; why?
- sub get_split_uword(uword arr, ubyte arr_len, ubyte index) -> uword {
- uword result
- setlsb(result, arr[index])
- arr += (arr_len as uword)
- setmsb(result, arr[index])
- return result
- }
- sub apply_key(ubyte whichVoice, ubyte whichKey) {
- void piano.key(whichVoice, whichKey&%01111111)
- }
- ; Applies both voice and envelope
- sub apply_instrument(ubyte whichVoice, uword whichInstrument) {
- whichInstrument *= 6 ; Instrument index to byte offset (each are 6 bytes)
- whichInstrument += SONG_ptr_instruments ; Byte offset to pointer
- ubyte wf_pw = whichInstrument[0] ; waveform | pulse_width
- ubyte ec_sv = whichInstrument[1] ; ear_channels | start_vol
- ubyte maxvol = whichInstrument[2]
- ubyte attack = whichInstrument[3]
- ubyte sustain = whichInstrument[4]
- ubyte release = whichInstrument[5]
- if maxvol == 0 { return }
- psg.voice(whichVoice, ec_sv&%11000000, ec_sv&%00111111,
- wf_pw&%11000000, wf_pw&%00111111)
- psg.envelope(whichVoice, maxvol, attack, sustain, release)
- }
- sub load_order(uword whichOrder) {
- ;STATE_whichOrder = whichOrder ; (This line should be redundant)
- whichOrder *= SONG_order_size ; Order index to byte offset
- whichOrder += SONG_ptr_orders ; Byte offset to pointer
- STATE_whichRow = 0
- STATE_ticksPerRow = whichOrder[0]
- STATE_highestMask = %00000001 ; A mask of 1 bit by default
- whichOrder++ ; whichOrder = &whichOrder[1] (skips the order's tick rate)
- &ubyte loop_i = &cx16.r4L ; Effectively an alias for cx16.r4L
- ubyte loop_max = SONG_channels-1
- ; For each pattern ptr in current order
- for loop_i in 0 to loop_max {
- ubyte patternID = whichOrder[loop_i]
- &uword ptr_pattern = &cx16.r5 ; Alias for r5
- if patternID != 0 {
- ptr_pattern = peekw( SONG_ptr_patterns + (((patternID-1) as uword)<<1) )
- } else { ; A pattern ID of 0 indicates 'no pattern used'
- ptr_pattern = NULL
- }
- STATE_ptrs_curOrder[loop_i] = ptr_pattern
- if patternID != 0 {
- ; First byte of pattern is the mask
- ubyte rowMask = ptr_pattern[0]
- STATE_rowMasks[loop_i] = rowMask
- ; For finding the largest mask in all of the currently loaded patterns,
- ; which is used to determine when an order is supposed to end
- if STATE_highestMask < rowMask { STATE_highestMask = rowMask }
- }
- }
- }
- ; (Assumes order is already loaded)
- sub load_row(ubyte whichRow) {
- STATE_whichTick = STATE_ticksPerRow ; Decrements instead of incrementing
- &ubyte loop_i = &cx16.r4L ; Effectively an alias for cx16.r4L
- ubyte loop_max = SONG_channels-1
- ; For each pattern ptr in current order
- for loop_i in 0 to loop_max {
- uword ptr_curPattern = STATE_ptrs_curOrder[loop_i]
- if ptr_curPattern == NULL { continue }
- ubyte patternIsSplit = ptr_curPattern[1]
- ptr_curPattern += 2 ; Skip the pattern's mask and split value
- ubyte patternMask = STATE_rowMasks[loop_i]
- ubyte whichRowMasked = whichRow & patternMask
- uword rowValue
- if patternIsSplit != 0 {
- ; TODO: Figure out why this doesn't work
- ;rowValue = get_split_uword(ptr_curPattern, patternMask, whichRowMasked)
- } else {
- rowValue = peekw( ptr_curPattern + ((whichRowMasked as uword)<<1) )
- }
- ubyte whichKey = lsb(rowValue) ;&%01111111 (this AND is redundant)
- ubyte whichInstrument = (rowValue>>7) as ubyte
- ubyte whichVoice = get_voice_num(loop_i)
- if whichKey == 0 { continue }
- apply_key(whichVoice, whichKey) ; (Automatically unsets MSb of whichKey)
- apply_instrument(whichVoice, whichInstrument)
- STATE_altChannel[loop_i] ^= 1 ; Flip the relevant bit
- }
- }
- ; Just 1 instruction (a 65C02-specific instruction :D)
- ; Assuming that STATE_isPlaying is zeropage, 1 byte per call is actually saved
- ; by inlining this lol (otherwise, the byte count is the same; 3 per call)
- inline asmsub stop() clobbers() { %asm {{ stz p8v_STATE_isPlaying }} }
- sub stop_forced() { stop() } ; TODO: Set all of the channels to mute here
- sub play(uword ptr_info) {
- if ptr_info == NULL { return }
- SONG_channels = ptr_info[INDEX_CHANNELS]
- SONG_instruments_len = ptr_info[INDEX_INSTRUMENTS_LEN]
- SONG_orders_len = ptr_info[INDEX_ORDERS_LEN]
- SONG_patterns_len = ptr_info[INDEX_PATTERNS_LEN]
- SONG_order_size = SONG_channels+1
- SONG_ptr_info = ptr_info
- SONG_ptr_instruments = peekw(ptr_info + INDEX_INSTRUMENTS)
- SONG_ptr_orders = peekw(ptr_info + INDEX_ORDERS)
- SONG_ptr_patterns = peekw(ptr_info + INDEX_PATTERNS)
- STATE_whichOrder = 0
- STATE_whichRow = 0 ; So that order 0 is immediately loaded next irq
- STATE_whichTick = 0
- STATE_highestMask = 255 ; Full mask by default, until order 0 is loaded
- STATE_isPlaying = true
- }
- sub irq_routine() -> bool {
- if not STATE_isPlaying { goto lbl_songStopped }
- ubyte whichRowMasked = STATE_whichRow & STATE_highestMask
- if whichRowMasked == 0 {
- ; TODO: Looping is broken. Fix it.
- load_order(STATE_whichOrder)
- STATE_whichOrder++
- ; Should be faster than a normal modulo
- if STATE_whichOrder >= SONG_orders_len {
- STATE_whichOrder -= SONG_orders_len
- }
- }
- if STATE_whichTick == 0 {
- load_row(whichRowMasked)
- STATE_whichRow++
- }
- STATE_whichTick--
- lbl_songStopped:
- return psg.envelopes_irq()
- }
- }
- ;------------------------------------------------------------------------------;
- %import textio
- %import conv
- ; txt.clear_screen()
- ; txt.nl()
- printn {
- %option ignore_unused
- sub ub_dec(ubyte v) { txt.print(conv.str_ub(v)) }
- sub b_dec(byte v) { txt.print(conv.str_b(v)) }
- sub uw_dec(uword v) { txt.print(conv.str_uw(v)) }
- sub w_dec(word v) { txt.print(conv.str_w(v)) }
- sub ub_bin(ubyte v) { txt.print(conv.str_ubbin(v)) }
- sub ub_hex(ubyte v) { txt.print(conv.str_ubhex(v)) }
- sub uw_hex(uword v) { txt.print(conv.str_uwhex(v)) }
- }
- music_debug {
- %option ignore_unused
- sub print_info() {
- txt.print("channels = ")
- printn.ub_dec(music.SONG_channels)
- txt.print("\ninstruments_len = ")
- printn.ub_dec(music.SONG_instruments_len)
- txt.print("\norders_len = ")
- printn.ub_dec(music.SONG_orders_len)
- txt.print("\npatterns_len = ")
- printn.ub_dec(music.SONG_patterns_len)
- txt.print("\norder_size = ")
- printn.ub_dec(music.SONG_order_size)
- txt.print("\nptr_instruments = $")
- printn.uw_hex(music.SONG_ptr_instruments)
- txt.print("\nptr_orders = $")
- printn.uw_hex(music.SONG_ptr_orders)
- txt.print("\nptr_patterns = $")
- printn.uw_hex(music.SONG_ptr_patterns)
- txt.nl()
- }
- sub print_state() {
- txt.print("isInit = ")
- printn.ub_dec(music.STATE_isInit as ubyte)
- txt.print("\nisPlaying = ")
- printn.ub_dec(music.STATE_isPlaying as ubyte)
- txt.print("\nwhichOrder = ")
- printn.uw_dec(music.STATE_whichOrder) ; uword, not ubyte
- txt.print("\nwhichRow = ")
- printn.ub_dec(music.STATE_whichRow)
- txt.print("\nwhichTick = ")
- printn.ub_dec(music.STATE_whichTick)
- txt.print("\nticksPerRow = ")
- printn.ub_dec(music.STATE_ticksPerRow)
- txt.print("\nhighestMask = ")
- printn.ub_dec(music.STATE_highestMask)
- &ubyte loop_i = &cx16.r6L
- txt.print("\naltChannel = [")
- for loop_i in 0 to 5 {
- printn.ub_dec(music.STATE_altChannel[cx16.r4L])
- txt.print(", ")
- }
- txt.print("]\n")
- txt.print("rowMasks = [")
- for loop_i in 0 to 5 {
- printn.ub_dec(music.STATE_rowMasks[cx16.r4L])
- txt.print(", ")
- }
- txt.print("]\n")
- txt.print("curOrder = [")
- for loop_i in 0 to 5 {
- txt.chrout('$')
- printn.uw_hex(music.STATE_ptrs_curOrder[cx16.r4L])
- txt.print(", ")
- }
- txt.print("]\n")
- }
- sub print_arr_ub(str prefix, uword ptr, ubyte index) {
- txt.print(prefix)
- txt.print(" = $")
- printn.ub_hex(ptr[index])
- txt.nl()
- }
- sub print_arr_uw(str prefix, uword ptr, ubyte index) {
- txt.print(prefix)
- txt.print(" = $")
- printn.uw_hex(peekw( ptr + ((index as uword)<<1) ))
- txt.nl()
- }
- }
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;"2nd_music_driver_2025-06-20\piano.p8":
- %import psg
- piano {
- ;%option no_symbol_prefixing, ignore_unused
- %option ignore_unused
- ; Referring to the index in the lookup table,
- ; not the actual frequency value, of course.
- const ubyte KEY_C0 = 0 ; 16.35Hz
- const ubyte KEY_C0S = 1 ; 17.32Hz
- const ubyte KEY_D0 = 2 ; 18.35Hz
- const ubyte KEY_D0S = 3 ; 19.44Hz
- const ubyte KEY_E0 = 4 ; 20.60Hz
- const ubyte KEY_F0 = 5 ; 21.82Hz
- const ubyte KEY_F0S = 6 ; 23.12Hz
- const ubyte KEY_G0 = 7 ; 24.49Hz
- const ubyte KEY_G0S = 8 ; 25.95Hz
- const ubyte KEY_A0 = 9 ; 27.50Hz
- const ubyte KEY_A0S = 10 ; 29.13Hz
- const ubyte KEY_B0 = 11 ; 30.86Hz
- const ubyte KEY_C1 = 12 ; 32.70Hz
- const ubyte KEY_C1S = 13 ; 34.64Hz
- const ubyte KEY_D1 = 14 ; 36.70Hz
- const ubyte KEY_D1S = 15 ; 38.89Hz
- const ubyte KEY_E1 = 16 ; 41.20Hz
- const ubyte KEY_F1 = 17 ; 43.65Hz
- const ubyte KEY_F1S = 18 ; 46.24Hz
- const ubyte KEY_G1 = 19 ; 48.99Hz
- const ubyte KEY_G1S = 20 ; 51.91Hz
- const ubyte KEY_A1 = 21 ; 55.00Hz
- const ubyte KEY_A1S = 22 ; 58.27Hz
- const ubyte KEY_B1 = 23 ; 61.73Hz
- const ubyte KEY_C2 = 24 ; 65.40Hz
- const ubyte KEY_C2S = 25 ; 69.29Hz
- const ubyte KEY_D2 = 26 ; 73.41Hz
- const ubyte KEY_D2S = 27 ; 77.78Hz
- const ubyte KEY_E2 = 28 ; 82.40Hz
- const ubyte KEY_F2 = 29 ; 87.30Hz
- const ubyte KEY_F2S = 30 ; 92.49Hz
- const ubyte KEY_G2 = 31 ; 97.99Hz
- const ubyte KEY_G2S = 32 ; 103.82Hz
- const ubyte KEY_A2 = 33 ; 110.00Hz
- const ubyte KEY_A2S = 34 ; 116.54Hz
- const ubyte KEY_B2 = 35 ; 123.47Hz
- const ubyte KEY_C3 = 36 ; 130.81Hz
- const ubyte KEY_C3S = 37 ; 138.59Hz
- const ubyte KEY_D3 = 38 ; 146.83Hz
- const ubyte KEY_D3S = 39 ; 155.56Hz
- const ubyte KEY_E3 = 40 ; 164.81Hz
- const ubyte KEY_F3 = 41 ; 174.61Hz
- const ubyte KEY_F3S = 42 ; 184.99Hz
- const ubyte KEY_G3 = 43 ; 195.99Hz
- const ubyte KEY_G3S = 44 ; 207.65Hz
- const ubyte KEY_A3 = 45 ; 220.00Hz
- const ubyte KEY_A3S = 46 ; 233.08Hz
- const ubyte KEY_B3 = 47 ; 246.94Hz
- const ubyte KEY_C4 = 48 ; 261.62Hz
- const ubyte KEY_C4S = 49 ; 277.18Hz
- const ubyte KEY_D4 = 50 ; 293.66Hz
- const ubyte KEY_D4S = 51 ; 311.12Hz
- const ubyte KEY_E4 = 52 ; 329.62Hz
- const ubyte KEY_F4 = 53 ; 349.22Hz
- const ubyte KEY_F4S = 54 ; 369.99Hz
- const ubyte KEY_G4 = 55 ; 391.99Hz
- const ubyte KEY_G4S = 56 ; 415.30Hz
- const ubyte KEY_A4 = 57 ; 440.00Hz
- const ubyte KEY_A4S = 58 ; 466.16Hz
- const ubyte KEY_B4 = 59 ; 493.88Hz
- const ubyte KEY_C5 = 60 ; 523.25Hz
- const ubyte KEY_C5S = 61 ; 554.36Hz
- const ubyte KEY_D5 = 62 ; 587.32Hz
- const ubyte KEY_D5S = 63 ; 622.25Hz
- const ubyte KEY_E5 = 64 ; 659.25Hz
- const ubyte KEY_F5 = 65 ; 698.45Hz
- const ubyte KEY_F5S = 66 ; 739.98Hz
- const ubyte KEY_G5 = 67 ; 783.99Hz
- const ubyte KEY_G5S = 68 ; 830.60Hz
- const ubyte KEY_A5 = 69 ; 880.00Hz
- const ubyte KEY_A5S = 70 ; 932.32Hz
- const ubyte KEY_B5 = 71 ; 987.76Hz
- const ubyte KEY_C6 = 72 ; 1046.50Hz
- const ubyte KEY_C6S = 73 ; 1108.73Hz
- const ubyte KEY_D6 = 74 ; 1174.65Hz
- const ubyte KEY_D6S = 75 ; 1244.50Hz
- const ubyte KEY_E6 = 76 ; 1318.51Hz
- const ubyte KEY_F6 = 77 ; 1396.91Hz
- const ubyte KEY_F6S = 78 ; 1479.97Hz
- const ubyte KEY_G6 = 79 ; 1567.98Hz
- const ubyte KEY_G6S = 80 ; 1661.21Hz
- const ubyte KEY_A6 = 81 ; 1760.00Hz
- const ubyte KEY_A6S = 82 ; 1864.65Hz
- const ubyte KEY_B6 = 83 ; 1975.53Hz
- const ubyte KEY_C7 = 84 ; 2093.00Hz
- const ubyte KEY_C7S = 85 ; 2217.46Hz
- const ubyte KEY_D7 = 86 ; 2349.31Hz
- const ubyte KEY_D7S = 87 ; 2489.01Hz
- const ubyte KEY_E7 = 88 ; 2637.02Hz
- const ubyte KEY_F7 = 89 ; 2793.82Hz
- const ubyte KEY_F7S = 90 ; 2959.95Hz
- const ubyte KEY_G7 = 91 ; 3135.96Hz
- const ubyte KEY_G7S = 92 ; 3322.43Hz
- const ubyte KEY_A7 = 93 ; 3520.00Hz
- const ubyte KEY_A7S = 94 ; 3729.31Hz
- const ubyte KEY_B7 = 95 ; 3951.06Hz
- const ubyte KEY_C8 = 96 ; 4186.00Hz
- const ubyte KEY_C8S = 97 ; 4434.92Hz
- const ubyte KEY_D8 = 98 ; 4698.63Hz
- const ubyte KEY_D8S = 99 ; 4978.03Hz
- const ubyte KEY_E8 = 100 ; 5274.04Hz
- const ubyte KEY_F8 = 101 ; 5587.65Hz
- const ubyte KEY_F8S = 102 ; 5919.91Hz
- const ubyte KEY_G8 = 103 ; 6271.92Hz
- const ubyte KEY_G8S = 104 ; 6644.87Hz
- const ubyte KEY_A8 = 105 ; 7040.00Hz
- const ubyte KEY_A8S = 106 ; 7458.62Hz
- const ubyte KEY_B8 = 107 ; 7902.13Hz
- const ubyte KEY_C9 = 108 ; 8372.01Hz
- const ubyte KEY_C9S = 109 ; 8869.84Hz
- const ubyte KEY_D9 = 110 ; 9397.27Hz
- const ubyte KEY_D9S = 111 ; 9956.06Hz
- const ubyte KEY_E9 = 112 ;10548.08Hz
- const ubyte KEY_F9 = 113 ;11175.30Hz
- const ubyte KEY_F9S = 114 ;11839.82Hz
- const ubyte KEY_G9 = 115 ;12543.85Hz
- const ubyte KEY_G9S = 116 ;13289.75Hz
- const ubyte KEY_A9 = 117 ;14080.00Hz
- const ubyte KEY_A9S = 118 ;14917.24Hz
- const ubyte KEY_B9 = 119 ;15804.26Hz
- ; PSG frequency lookup table (used by key(); private)
- ubyte[120] priv_freqs_lo = [44, 47, 49, 52, 55, 59, 62, 66, 70, 74, 78, 83, 88, 93, 99,104,111,117,124,132,139,148,156,166,176,186,197,209,221,234,248, 7, 23, 39, 57, 75, 95,116,138,162,186,213,241, 14, 45, 79,114,151,190,232, 20, 67,117,169,225, 28, 91,157,227, 46,125,208, 41,134,234, 83,194, 57,182, 58,199, 92,249,160, 81, 13,211,166,133,113,107,116,141,183,242, 64,162, 25,167, 76, 10,226,215,233, 27,110,229,129, 69, 51, 77,151, 19,196,173,210, 54,220,201, 2,138,102,155, 46, 38,136, 90,164,107,184]
- ubyte[120] priv_freqs_hi = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 26, 27, 29, 31, 32, 34, 36, 39, 41, 43, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 93, 98,104,110,117,124,131,139,147,156,165]
- ; voice_num: 0-15, which: 0-119
- ; returns: the resulting raw frequency value
- sub key(ubyte voice_num, ubyte which) -> uword {
- uword freq_full = mkword(priv_freqs_hi[which], priv_freqs_lo[which])
- psg.freq(voice_num, freq_full)
- return freq_full
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement