; ; xoxmacros.h: generic macros ; ; author: Marko Kanala, raato ]a t[ mulletronic.com ; ; LICENSE ; Creative Commons Attribution-NonCommercial-ShareALike 2.5 ; ; You are free: ; ; * to copy, distribute, display, and perform the work ; * to make derivative works ; ; Under the following conditions: ; ; 1. Attribution. You must attribute the work in the manner specified by the ; author or licensor. ; 2. Noncommercial. You may not use this work for commercial purposes. ; 3. Share Alike. If you alter, transform, or build upon this work, you may ; distribute the resulting work only under a license identical to this one. ; ; * For any reuse or distribution, you must make clear to others the license ; terms of this work. ; * Any of these conditions can be waived if you get permission from the ; copyright holder. ; ; Your fair use and other rights are in no way affected by the above. ; ; This is a human-readable summary of the Legal Code. ; See full license at http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode ; ; Copyright: Marko Kanala (raato@mulletronic.com). ; ; ; moves the address to to table address pointer ; ; thrashes W TABLE_ADDR MACRO addr movlw ((addr) & 0xff) ; store lo byte movwf TBLPTRL movlw ((addr) >> 8) ; store hi byte movwf TBLPTRH ENDM ; ; branches to addr if the key is pressed ; ; thrashes W KEYEVENT MACRO key, addr movlw key xorwf keypress, W btfsc STATUS, Z bra addr ENDM ; ; branches to addr if a step key is pressed ; STEPKEYEVENT MACRO addr LOCAL ske_no movf keypress, W bz ske_no ; none pressed, not step key sublw KEY_S16 ; key over 16 pressed ? bn ske_no bra addr ske_no: ENDM ; ; calls subroutine at addr if the key is pressed ; ; thrashes W KEYEVENTCALL MACRO key, addr movlw key xorwf keypress, W btfsc STATUS, Z call addr ENDM ; ; checks if the downkey is held down and ; branches to addr if so ; ; note: only clear, laststep, instrsel, ; shuffleflam and shiftfunc keys ; can be held down. ; IFKEYDOWN MACRO downkeybit, addr btfsc downkeys, downkeybit bra addr ENDM ; selfexplanatory IFKEYNOTDOWN MACRO downkeybit, addr btfss downkeys, downkeybit bra addr ENDM ; ; checks if both specified downkeys are ; held down and branches to addr if so ; ; note: only clear, laststep, instrsel, ; shuffleflam and shiftfunc keys ; can be held down. ; IFKEYSDOWN MACRO downkeybit1, downkeybit2, addr btfss downkeys, downkeybit1 bra $+2+2+2+2 ; nope, skip over btfss downkeys, downkeybit2 bra $+2+2 ; nope, skip over bra addr ENDM ; ; checks if the downkey is held down and ; makes a call to addr if so ; ; note: only clear, laststep, instrsel, ; shuffleflam and shiftfunc keys ; can be held down. ; IFKEYDOWNCALL MACRO downkeybit, addr btfsc downkeys, downkeybit call addr ENDM ; ; branches to addr if display ; has not been changed ; DISPLAYNOTCHANGED MACRO addr tstfsz lcdwait ; if lcdwait != 0 not changed! bra addr movf currentdisplay, W subwf lastdisplay, W btfsc STATUS, Z bra addr ENDM ; ; branches to addr if the current display ; matches display ; DISPLAY MACRO display, addr movlw display subwf currentdisplay, W btfsc STATUS, Z bra addr ENDM ; ; sets the current display as changed ; DISPLAYREFRESHED MACRO movff currentdisplay, lastdisplay ENDM ; ; sets the display as next display ; to be refreshed ; SETDISPLAY MACRO display movlw display movwf currentdisplay ENDM ; ; forces lcd display refresh ; FORCEDISPLAYREFRESH MACRO clrf lastdisplay ENDM ; ; forces the current screen to be shown for ; a amount of timer 2 interrupts. note that ; the display must be changed before this ; macro is used. ; DISPLAYDELAY MACRO delay clrf lcdwaitcounter movlw delay movwf lcdwait ENDM ; ; sets mode bit ; SETMODE MACRO mode_bit bsf mode, mode_bit ENDM ; ; clears mode bit ; CLEARMODE MACRO mode_bit bcf mode, mode_bit ENDM ; ; toggles mode bit ; TOGGLEMODE MACRO mode_bit btg mode, mode_bit ENDM ; branches to addr if in mode ; IFMODE MACRO mode_bit, addr btfsc mode, mode_bit bra addr ENDM ; ; branches to addr if not in mode ; IFNOTINMODE MACRO mode_bit, addr btfss mode, mode_bit bra addr ENDM ; calls addr if in mode ; IFMODECALL MACRO mode_bit, addr btfsc mode, mode_bit call addr ENDM ; ; calls addr if not in mode ; IFNOTINMODECALL MACRO mode_bit, addr btfss mode, mode_bit call addr ENDM ; ; checks if the sequencer is running and ; branches to addr if so ; IFRUNNING MACRO addr btfsc mode, MODE_RUN bra addr ENDM ; ; checks if the sequencer is not running and ; branches to addr if so ; IFNOTRUNNING MACRO addr btfss mode, MODE_RUN bra addr ENDM ; ; checks for a event and branches to noaddr ; if no event of the type has occurred. ; IFNOEVENT MACRO ev, noaddr btfss event, ev bra noaddr ENDM ; ; checks for a event and calls noaddr ; if no event of the type has occurred ; IFNOEVENTCALL MACRO ev, noaddr btfss event, ev call noaddr ENDM ; ; checks for a event and branches to noaddr ; if event of the type has occurred. ; IFEVENT MACRO ev, noaddr btfsc event, ev bra noaddr ENDM ; ; checks for a event and calls noaddr ; if event of the type has occurred ; IFEVENTCALL MACRO ev, noaddr btfsc event, ev call noaddr ENDM ; ; sets a specific event ; SETEVENT MACRO ev bsf event, ev ENDM ; ; clears a specific event ; CLEAREVENT MACRO ev bcf event, ev ENDM ; ; loads current pattern address with offset to ; to FSRfsr (pattern or fillpattern). ; ; thrashes W LOADPATTOFSR MACRO fsr, offset btfss mode, MODE_FILL bra $+6+2 ; not fill, skip lfsr+bra == 3 words ; mode == fill IF fsr==0 lfsr FSR0, fillpattern ENDIF IF fsr==1 lfsr FSR1, fillpattern ENDIF IF fsr==2 lfsr FSR2, fillpattern ENDIF bra $+4+2 ; was fill, skip lfsr == 2 words ; mode != fill IF fsr==0 lfsr FSR0, pattern ENDIF IF fsr==1 lfsr FSR1, pattern ENDIF IF fsr==2 lfsr FSR2, pattern ENDIF movlw offset IF fsr==0 addwf FSR0L ENDIF IF fsr==1 addwf FSR1L ENDIF IF fsr==2 addwf FSR2L ENDIF ENDM ; ; loads the trigger row address of the ; selected instrument to FSR1 ; ; thrashes W LOADFSR1_CURRENTTRIGGERROW MACRO LOADPATTOFSR 1, PAT_TRIGS movf selectedinst, W addwf WREG ; 2 bytes / instrument addwf FSR1L ; advance to correct trigger ; row ENDM ; ; loads the trigger row address of the ; selected instrument to FSR2 ; ; thrashes W LOADFSR2_CURRENTTRIGGERROW MACRO LOADPATTOFSR 2, PAT_TRIGS movf selectedinst, W addwf WREG ; 2 bytes / instrument addwf FSR2L ; advance to correct trigger ; row ENDM ; ; loads lsb nybble of a pattern offset to W ; using FSR indirect addressing ; PATLSBTOW MACRO offset, fsr LOADPATTOFSR fsr, offset IF fsr==0 movf INDF0, W ENDIF IF fsr==1 movf INDF1, W ENDIF IF fsr==2 movf INDF2, W ENDIF andlw 0x0f ENDM ; ; loads msb nybble of a pattern offset to W ; using indirect addressing ; PATMSBTOW MACRO offset, fsr LOADPATTOFSR fsr, offset IF fsr==0 swapf INDF0, W ENDIF IF fsr==1 swapf INDF1, W ENDIF IF fsr==2 swapf INDF2, W ENDIF andlw 0x0f ENDM ; ; stores W to lsb nybble of pattern offset ; ; thrashes W and tempreg WTOPATLSB MACRO offset, tempreg, fsr movwf tempreg ; 0000FFFF LOADPATTOFSR fsr, offset IF fsr==0 movf INDF0, W ; ssssffff andlw 0xf0 ; ssss0000 iorwf tempreg, W ; ssssFFFF movwf INDF0 ; store ENDIF IF fsr==1 movf INDF1, W ; ssssffff andlw 0xf0 ; ssss0000 iorwf tempreg, W ; ssssFFFF movwf INDF1 ; store ENDIF IF fsr==0 movf INDF2, W ; ssssffff andlw 0xf0 ; ssss0000 iorwf tempreg, W ; ssssFFFF movwf INDF2 ; store ENDIF ENDM ; ; stores W to a msb nybble of pattern offset ; WTOPATMSB MACRO offset, tempreg, fsr swapf WREG movwf tempreg ; SSSS0000 LOADPATTOFSR fsr, offset IF fsr==0 movf INDF0, W ; ssssffff andlw 0x0f ; 0000ffff iorwf tempreg, W ; SSSSffff movwf INDF0 ; store ENDIF IF fsr==1 movf INDF1, W ; ssssffff andlw 0x0f ; 0000ffff iorwf tempreg, W ; SSSSffff movwf INDF1 ; store ENDIF IF fsr==2 movf INDF2, W ; ssssffff andlw 0x0f ; 0000ffff iorwf tempreg, W ; SSSSffff movwf INDF2 ; store ENDIF ENDM ; ; loads shuffle amount to WREG ; SHUFFLETOW MACRO fsr PATMSBTOW PAT_SHUFFLEFLAM, fsr ENDM ; ; loads flam amount to WREG ; FLAMTOW MACRO fsr PATLSBTOW PAT_SHUFFLEFLAM, fsr ENDM ; ; stores shuffle value from W to shuffle reg ; ; thrashes W and tempreg WTOSHUFFLE MACRO tempreg, fsr WTOPATMSB PAT_SHUFFLEFLAM, tempreg, fsr ENDM ; ; stores flam value from W to flam reg ; ; thrashes W and tempreg WTOFLAM MACRO tempreg, fsr WTOPATLSB PAT_SHUFFLEFLAM, tempreg, fsr ENDM ; ; loads scale to WREG ; ; note: allows only values 0 - 3 for scale SCALETOW MACRO fsr LOADPATTOFSR fsr, PAT_SCALELENGTH movlw b'00110000' IF fsr==0 andwf INDF0, W ENDIF IF fsr==1 andwf INDF1, W ENDIF IF fsr==2 andwf INDF2, W ENDIF swapf WREG ENDM ; ; loads pattern length to WREG ; LENGTHTOW MACRO fsr PATLSBTOW PAT_SCALELENGTH, fsr incf WREG ENDM ; ; stores scale value from W to scale reg ; ; thrashes W and tempreg WTOSCALE MACRO tempreg, fsr WTOPATMSB PAT_SCALELENGTH, tempreg, fsr ENDM ; ; stores length value from W to length reg ; ; thrashes W and tempreg WTOLENGTH MACRO tempreg, fsr decf WREG WTOPATLSB PAT_SCALELENGTH, tempreg, fsr ENDM ; ; fetches the instrument specific ; last step value to W ; ; instrument is selected with WREG ; (bass drum = 0) ; ; NOTE: fsrreg should be loaded with ; correct pattern (pattern or fillpattern)! ; ; fsrreg, W and tempreg are trashed INSTRLENGTHTOW MACRO fsrreg, tempreg movwf tempreg ; store instrument index LOADPATTOFSR fsrreg, PAT_LENGTHS ; FSR<fsrreg> points to pattern lenghts bcf STATUS, C rrcf tempreg, W ; w = instrument / 2 IF fsrreg==0 addwf FSR0L ENDIF IF fsrreg==1 addwf FSR1L ENDIF IF fsrreg==2 addwf FSR2L ENDIF ; FSR points to correct byte btfss tempreg, 0 bra $+6 ; even, skip bra+movf+bra == 6 ; odd instrument in W => get lsb nybble IF fsrreg==0 movf INDF0, W ENDIF IF fsrreg==1 movf INDF1, W ENDIF IF fsrreg==2 movf INDF2, W ENDIF bra $+4 ; odd ready, skip bra, swapf == 4 ; even instrument in W => get msb nybble IF fsrreg==0 swapf INDF0, W ENDIF IF fsrreg==1 swapf INDF1, W ENDIF IF fsrreg==2 swapf INDF2, W ENDIF movwf tempreg movlw 0x0f andwf tempreg, W incf WREG ; done, W == instrument last step ENDM ; ; fetches the instrument specific ; current step position to W ; ; instrument is selected with WREG ; (bass drum = 0) ; ; W and fsrreg are trashed INSTRCURRENTSTEPTOW MACRO fsrreg IF fsrreg==0 lfsr FSR0, currentstep_instr addwf FSR0L movf INDF0, W ENDIF IF fsrreg==1 lfsr FSR1, currentstep_instr addwf FSR1L movf INDF1, W ENDIF IF fsrreg==2 lfsr FSR2, currentstep_instr addwf FSR2L movf INDF2, W ENDIF ; done, W == current step of instr ENDM ; ; checks if a step trigger is on ; and calls addr if so. ; ; The current byte column from FSR2 ; is selected before the call to addr is made ; according to currentstepreg. ; ; FSR2 should point to [fill]pattern+PAT_TRIGS. ; ; Note: this macro automatically advances to ; the next instrument (FSR2) and should ; be called in correct instrument order. ; Additionally, the bit of the current step ; is stored in temptrig. ; ; flamreg and flambit define the address and bit ; to set if flam is on. This information is used ; later when the flams are triggered. ; IFTRIGONCALL MACRO currentstepreg, addr, flamreg, flambit LOCAL tr_firsthalf, tr_go, tr_not_on, tr_noflam movlw 8 cpfsgt currentstepreg bra tr_firsthalf subwf currentstepreg, W ; > 8 decf WREG call get_bit incf FSR2L ; to correct byte bra tr_go tr_firsthalf: decf currentstepreg, W ; < 9 call get_bit tr_go: ; now FSR2 points to correct step byte ; and WREG has a mask to correct bit movwf temptrig andwf INDF2, W bz tr_not_on ; trigger is on call addr ; check flams movlw PAT_FLAMS - PAT_TRIGS ; if voice was on addwf FSR2L ; avoid calling movf temptrig, W andwf INDF2, W ; addr twice! bz tr_noflam bsf flamreg, flambit bra tr_noflam tr_not_on: ; check flams movlw PAT_FLAMS - PAT_TRIGS addwf FSR2L movf temptrig, W andwf INDF2, W bz tr_noflam call addr ; voice was off, call addr bsf flamreg, flambit tr_noflam: movlw PAT_FLAMS - PAT_TRIGS subwf FSR2L ; advance to next trigger btfss FSR2L, 0 incf FSR2L ; only one incf if incf FSR2L ; needed ENDM ; ; checks if a step trigger is on ; and calls addr if so. ; ; The current byte column from FSR2 ; is selected before the call to addr is made ; according to currentstepreg. ; ; FSR2 should point to [fill]pattern+PAT_TRIGS. ; ; Note: this macro automatically advances to ; the next instrument (FSR2) and should ; be called in correct instrument order. ; Additionally, the bit of the current step ; is stored in temptrig. ; IFTRIGONNOFLAMCALL MACRO currentstepreg, addr LOCAL tr_firsthalf, tr_go, tr_not_on movlw 8 cpfsgt currentstepreg bra tr_firsthalf subwf currentstepreg, W ; > 8 decf WREG call get_bit incf FSR2L ; to correct byte bra tr_go tr_firsthalf: decf currentstepreg, W ; < 9 call get_bit tr_go: ; not FSR2 points to correct step byte ; and WREG has a mask to correct bit movwf temptrig andwf INDF2, W bz tr_not_on ; trigger is on call addr tr_not_on: ; advance to next trigger btfss FSR2L, 0 incf FSR2L ; only one incf if incf FSR2L ; needed ENDM ; ; sets the trigger volume dac ; and multiplexers for an instrument. ; ; additionally calculates the value for midi note ; to be sent if necessary ; TRIGSETVOL MACRO mpxvalue, basevol, accadd, midivolreg LOCAL tr_acc_not_on ; inhibit muxes bsf TRIGMUXINH_PORT, TRIGMUXINH_PIN ; set base volume ; movlw basevol ; movwf temptrigvol ; movlw accadd ; tstfsz totalaccent ; addwf temptrigvol ; nop ; nop movlw basevol tstfsz totalaccent addlw accadd movwf temptrigvol ; advance to pattern specific accents movlw PAT_ACCENTS - PAT_TRIGS addwf FSR2L ; temptrig == current bit movf temptrig, W andwf INDF2, W bz tr_acc_not_on ; accent is on, increment volume movlw accadd addwf temptrigvol movlw vol_acc_midi ; accent midi ... movwf midivolreg ; ... note too tr_acc_not_on: ; go back to pattern steps movlw -(PAT_ACCENTS - PAT_TRIGS) addwf FSR2L ; set clock low for dac latch bcf TRIGDAC_CLK_PORT, TRIGDAC_CLK_PIN ; write dac and preserve port bits movlw b'11000000' andwf PORT_TRIG, W iorwf temptrigvol, W ; temptrigvol 6 LSB bits = volume movwf PORT_TRIG ; set dac latch nop nop bsf TRIGDAC_CLK_PORT, TRIGDAC_CLK_PIN ; ... dac reads the port b nop nop nop ; dac now set to correct volume, ; use mux to select the correct instrument movlw b'11000000' andwf PORT_TRIG, W iorlw mpxvalue movwf PORT_TRIG ; select instrument nop nop nop ; enable muxes for dac bcf TRIGMUXINH_PORT, TRIGMUXINH_PIN ; 4051 takes 0.5us - 1us to react on enable ; nop ; TODO: 9 cycles, are these needed ? ; nop ; nop ; nop ; nop ; nop ; nop ; nop ; nop movlw vol_base_midi ; 1 tstfsz totalaccent ; 1 (2 or 3) addlw vol_acc_midi ; 1 addwf midivolreg ; 1 ; 4 - 5 cycles ; now dac output fed through the mux ENDM ; ; checks and resets instrument specific ; currentstep-counters. ; ; FSR2 should be loaded to the current ; [fill]pattern+PAT_LENGHTS. ; player_temp should be loaded with ; pattern last step. ; INSTRCHECKPOS MACRO msbcounter, lsbcounter LOCAL skip_msb, skip_lsb, msb_pat, lsb_pat, msb_do, lsb_do ; check msb counter movf msbcounter, W cpfsgt player_temp ; if pattern last step < instr last step bra msb_pat ; use it for comparison ; compare against instr specific last step swapf INDF2, W andlw 0x0f ; msb len incf WREG bra msb_do msb_pat: ; compare against pattern last step movf player_temp, W msb_do: cpfsgt msbcounter bra skip_msb movlw 1 movwf msbcounter skip_msb: ; check lsb counter movf lsbcounter, W cpfsgt player_temp ; if pattern last step < instr last step bra lsb_pat ; use it for comparison ; compare against instr specific last step movlw 0x0f andwf INDF2, W ; lsb len incf WREG bra lsb_do lsb_pat: ; compare against pattern last step movf player_temp, W lsb_do: cpfsgt lsbcounter bra skip_lsb movlw 1 movwf lsbcounter skip_lsb: incf FSR2L ENDM ; ; waits for midi transmit to end ; WAITMIDI MACRO LOCAL loop loop: btfss PIR1, TXIF bra loop ENDM ; ; increases a 16-bit value in addr ; INCF16 MACRO addr infsnz addr + 1 incf addr ENDM ; ; decreases a 16-bit value in addr ; DECF16 MACRO addr decf addr + 1 btfss STATUS, C decf addr ENDM ; ; clears a 16-bit memory value ; CLRF16 MACRO addr clrf addr clrf addr+1 ENDM ; ; Macros to decode and encode stored measure ; data follow ; ; note: measures are encoded as b'BBPPPPRR' ; ; decodes bank value in W ; DECODE_BANK_FROM_MEASURE MACRO andlw b'11000000' rlncf WREG rlncf WREG ENDM ; ; decodes pattern value in W ; DECODE_PATTERN_FROM_MEASURE MACRO andlw b'00111100' rrncf WREG rrncf WREG ENDM ; ; decodes repeat value in W ; DECODE_REPEAT_FROM_MEASURE MACRO andlw b'00000011' ENDM ; ; sets settings mode ; SETSETTINGSMODE MACRO movff mode, mode_before_settings clrf mode ENDM ; ; clears settings mode ; CLEARSETTINGSMODE MACRO movff mode_before_settings, mode ; note: a proper enter-call should be made after ENDM ; ; branches to addr if setting set ; IFSETTINGON MACRO setting_bit, addr btfsc settings, setting_bit bra addr ENDM ; ; branches to addr if setting not set ; IFSETTINGOFF MACRO setting_bit, addr btfss settings, setting_bit bra addr ENDM ; ; calls addr if setting set ; IFSETTINGONCALL MACRO setting_bit, addr btfsc settings, setting_bit call addr ENDM ; ; calls addr if setting not set ; IFSETTINGOFFCALL MACRO setting_bit, addr btfss settings, setting_bit call addr ENDM
addr noaddr get_bit