;
; 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