Pastebin
API
tools
faq
paste
Login
Sign up
Please fix the following errors:
New Paste
Syntax Highlighting
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;"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 } }
Optional Paste Settings
Category:
None
Cryptocurrency
Cybersecurity
Fixit
Food
Gaming
Haiku
Help
History
Housing
Jokes
Legal
Money
Movies
Music
Pets
Photo
Science
Software
Source Code
Spirit
Sports
Travel
TV
Writing
Tags:
Syntax Highlighting:
None
Bash
C
C#
C++
CSS
HTML
JSON
Java
JavaScript
Lua
Markdown (PRO members only)
Objective C
PHP
Perl
Python
Ruby
Swift
4CS
6502 ACME Cross Assembler
6502 Kick Assembler
6502 TASM/64TASS
ABAP
AIMMS
ALGOL 68
APT Sources
ARM
ASM (NASM)
ASP
ActionScript
ActionScript 3
Ada
Apache Log
AppleScript
Arduino
Asymptote
AutoIt
Autohotkey
Avisynth
Awk
BASCOM AVR
BNF
BOO
Bash
Basic4GL
Batch
BibTeX
Blitz Basic
Blitz3D
BlitzMax
BrainFuck
C
C (WinAPI)
C Intermediate Language
C for Macs
C#
C++
C++ (WinAPI)
C++ (with Qt extensions)
C: Loadrunner
CAD DCL
CAD Lisp
CFDG
CMake
COBOL
CSS
Ceylon
ChaiScript
Chapel
Clojure
Clone C
Clone C++
CoffeeScript
ColdFusion
Cuesheet
D
DCL
DCPU-16
DCS
DIV
DOT
Dart
Delphi
Delphi Prism (Oxygene)
Diff
E
ECMAScript
EPC
Easytrieve
Eiffel
Email
Erlang
Euphoria
F#
FO Language
Falcon
Filemaker
Formula One
Fortran
FreeBasic
FreeSWITCH
GAMBAS
GDB
GDScript
Game Maker
Genero
Genie
GetText
Go
Godot GLSL
Groovy
GwBasic
HQ9 Plus
HTML
HTML 5
Haskell
Haxe
HicEst
IDL
INI file
INTERCAL
IO
ISPF Panel Definition
Icon
Inno Script
J
JCL
JSON
Java
Java 5
JavaScript
Julia
KSP (Kontakt Script)
KiXtart
Kotlin
LDIF
LLVM
LOL Code
LScript
Latex
Liberty BASIC
Linden Scripting
Lisp
Loco Basic
Logtalk
Lotus Formulas
Lotus Script
Lua
M68000 Assembler
MIX Assembler
MK-61/52
MPASM
MXML
MagikSF
Make
MapBasic
Markdown (PRO members only)
MatLab
Mercury
MetaPost
Modula 2
Modula 3
Motorola 68000 HiSoft Dev
MySQL
Nagios
NetRexx
Nginx
Nim
NullSoft Installer
OCaml
OCaml Brief
Oberon 2
Objeck Programming Langua
Objective C
Octave
Open Object Rexx
OpenBSD PACKET FILTER
OpenGL Shading
Openoffice BASIC
Oracle 11
Oracle 8
Oz
PARI/GP
PCRE
PHP
PHP Brief
PL/I
PL/SQL
POV-Ray
ParaSail
Pascal
Pawn
Per
Perl
Perl 6
Phix
Pic 16
Pike
Pixel Bender
PostScript
PostgreSQL
PowerBuilder
PowerShell
ProFTPd
Progress
Prolog
Properties
ProvideX
Puppet
PureBasic
PyCon
Python
Python for S60
QBasic
QML
R
RBScript
REBOL
REG
RPM Spec
Racket
Rails
Rexx
Robots
Roff Manpage
Ruby
Ruby Gnuplot
Rust
SAS
SCL
SPARK
SPARQL
SQF
SQL
SSH Config
Scala
Scheme
Scilab
SdlBasic
Smalltalk
Smarty
StandardML
StoneScript
SuperCollider
Swift
SystemVerilog
T-SQL
TCL
TeXgraph
Tera Term
TypeScript
TypoScript
UPC
Unicon
UnrealScript
Urbi
VB.NET
VBScript
VHDL
VIM
Vala
Vedit
VeriLog
Visual Pro Log
VisualBasic
VisualFoxPro
WHOIS
WhiteSpace
Winbatch
XBasic
XML
XPP
Xojo
Xorg Config
YAML
YARA
Z80 Assembler
ZXBasic
autoconf
jQuery
mIRC
newLISP
q/kdb+
thinBasic
Paste Expiration:
Never
Burn after read
10 Minutes
1 Hour
1 Day
1 Week
2 Weeks
1 Month
6 Months
1 Year
Paste Exposure:
Public
Unlisted
Private
Folder:
(members only)
Password
NEW
Enabled
Disabled
Burn after read
NEW
Paste Name / Title:
Create New Paste
Hello
Guest
Sign Up
or
Login
Sign in with Facebook
Sign in with Twitter
Sign in with Google
You are currently not logged in, this means you can not edit or delete anything you paste.
Sign Up
or
Login
Public Pastes
FnafHuntedAdapter
Lua | 8 min ago | 18.52 KB
MTG/RiffleShuffle Simulation
Python | 25 min ago | 4.58 KB
300$ GIFTCARDS? P8
Java | 28 min ago | 0.07 KB
FREE 500$ GIFTCARDS???? VF
Java | 29 min ago | 0.07 KB
Make 3500$ in 1 day [Method]???? XH
Java | 30 min ago | 0.06 KB
?FREE Method Leaked? 1Y
Java | 32 min ago | 0.07 KB
?Make 1500$ in 1 day [Method]? AZ
Java | 33 min ago | 0.07 KB
200 INSTANTLY [Works Worldwide]? SD
Java | 34 min ago | 0.07 KB
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the
Cookies Policy
.
OK, I Understand
Not a member of Pastebin yet?
Sign Up
, it unlocks many cool features!