;
; 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
External Labels :
addr
noaddr
get_bit