Package: asfv1
asfv1Alternate Assembler for Spin Semi FV-1Copyright (C) 2017-2019 Nathan FraserAn alternate assembler for the Spin Semiconductor FV-1 DSP. This
assembler aims to replicate some of the behaviour of the Spin FV-1
assembler in standard Python, for developers who are unable or
unwilling to use the Spin provided IDE.RequirementsPython >= 2.6InstallationMake sure your system has a python interpreter
(preferably python3), then install from thePython Package Indexusing thepipcommand:$ pip3 install asfv1or$ pip install asfv1For system-specific installation instructions seeSystem Specific Installationbelow.Descriptionasfv1 reads a single FV-1 DSP program then parses and assembles it.
If no errors are encountered, machine code is written to an output
file. If the output filename ends with 'hex', an Intel hex
encoded output is produced, otherwise raw binary data is written.usage: asfv1 [-h] [-q] [-v] [-c] [-s] [-p {0,1,2,3,4,5,6,7}] [-b]
         	infile outfileinfile: Filename for an ASCII, utf-8 or utf-16 encoded text file
containing FV-1 assembly (seeAssembly Program Syntaxbelow)outfile: Filename for assembled output. If filename ends with 'hex',
an Intel hex file is written.-h,--help: Show a help message and exit-q,--quiet: Suppress warning messages-v,--version: Print program version and exit-c,--clamp: Clamp out of range instruction operand values without error.
A warning message is printed for each clamped operand.-s,--spinreals: Interpret integer literals1and2as 1.0 and 2.0
respectively. This option should be used with SpinASM assembly.-p {0,1,2,3,4,5,6,7}: Nominate one of the eight available program
slots on an FV-1 eeprom as the target. When this option is used with
binary output, machine code is offset appropriately in the target
file, allowing for assembly into an existing binary bank file. When
Intel HEX output is requested, the output file will include a single
program and relevant offset information for the target program.-b,--binary: Force output in binary format, even ifoutfileends with 'hex'.Assembly Program SyntaxAn FV-1 assembly program recognised by asfv1 closely resembles
theSpinIDE(.spn) format.
Input is an ASCII, utf-8 or utf-16 encoded
text file containing zero to 128 FV-1instructionswith optionaltargets,labels,commentsandassembly directives.
All text is matched case-insensitively and runs of whitespace characters
(newline, tab, space) are condensed.
Each of the input instructions is assembled into a single 32 bit
machine code. If less than 128 assembly instructions are input,
the unallocated program space is padded with 'NOP' instructions
(0x00000011).Forexample:; A complete, but useless FV-1 assembly program
MEM	delay	int(32767*3/5)	; ~0.6 sec delay
EQU	input	ADCL		; use ADCL for input
EQU	output	DACL		; use DACL for output
EQU	vol	REG0		; use REG0 for volume
start:	skp	RUN,main	; skip to main after first sample
	ldax	POT0		; read from POT0
	wrax	vol,0.0		; write volume to register
main:	ldax	input		; read from input
	mulx	vol		; scale by volume
	wra	delay,0.0	; write to delay
	rda	delay^,0.5	; read from delay midpoint
	rda	delay#,0.5	; read from delay end
	wrax	output,0.0	; write to outputWhen assembled with asfv1, the resulting machine code contains
9 instructions and padding with NOP instructions:$ asfv1 -q example.asm example.bin
$ hd example.bin 
00000000  80 40 00 11 00 00 02 05  00 00 04 06 00 00 02 85  |.@..............|
00000010  00 00 04 0a 00 00 00 02  20 04 cc c0 20 09 99 80  |........ ... ...|
00000020  00 00 02 c6 00 00 00 11  00 00 00 11 00 00 00 11  |................|
00000030  00 00 00 11 00 00 00 11  00 00 00 11 00 00 00 11  |................|
*
00000200CommentsA semicolon character ';' starts comment text. The assembler will
ignore all text including the ';' up to the end of a line.
Examples:; Comment out a whole line
target:	or	0xffffff	; comment to end of line
trget2:		; comment between target and instruction
	and	0x000000	; comment follows instruction
	; xor 0xa5a5a5		; instruction commented out
; excessive commenting:
addr02: cho			; op=0x14 interpolated memory access
		rdal,		; type=0x3 read offset(LFO) into ACC
		SIN1,		; lfo=0x1 use SIN1 LFO
		COS|REG		; flags=0xb register LFO and use COS outputLabel AssignmentEQU	LABEL	EXPRESSIONDirective 'EQU' assigns the constant value resulting from the
evaluation of 'EXPRESSION' (seeOperand Expressionsbelow) to the text label 'LABEL'.
LABEL must begin with one alphabetic character in the set [A-Z,a-z]
followed by any number of alphanumeric characters or underscores:
[A-Z,a-z,0-9,_].
EXPRESSION can contain any previously assigned labels, including
those pre-defined by the assembler (seePre-defined Labelsbelow). For
compatibility with SpinASM, the order of 'EQU' and 'LABEL'
may be swapped. Examples:EQU	input	ADCL		; assign value of ADCL (0x14) to 'input'
EQU	r3_7	3/7		; assign the value 3/7 to 'r3_7'
inve	EQU	1/r3_7		; assign the inverse of 'r3_7' to 'inve'EQU does not generate any code in the program, it merely reserves
the name for subsequent use. The parser evaluates all expressions
in-place so a label must be declared before it is used:or	missing		; error
EQU	missing	123		; missing is used before definition

parse error: Undefined label missing on line ...Re-defining an already assigned label is allowed, but will generate
a warning message:EQU	POT0	POT1		; point POT0 to POT1

warning: Label POT0 re-defined on line ...Labels, mnemonics and operators are matched case insensitively:EQU	Label_One	-1.0	; assign 1.0 to 'LABEL_ONE'
eQu	lABEL_oNE	-1.0	; assign 1.0 to 'LABEL_ONE' again
	Or	label_one	; or -1.0
	oR	LABEL_ONE	; or -1.0
	OR	LABEL_ONE	; or -1.0
	or	lAbEl_OnE	; or -1.0Memory AllocationMEM	LABEL	EXPRESSIONAddresses in the FV-1's 32768 sample circular buffer can be assigned
by the assembler using the 'MEM' directive. MEM reserves a portion
of memory that represents a delay of 'EXPRESSION' samples between
the start point and end point, and assigns three labels:LABEL	start of delay segment
LABEL^	midpoint of delay segment
LABEL#	end of delay segmentEXPRESSION must define an integer number of samples
or a parse error will be generated:MEM	invalid	123.4556	; invalid memory definition

Memory INVALID length 123.4556 not integer on line 42

MEM	third	32767//3	; valid due to integer divide
MEM	d0_13	int(0.13*32767)	; valid due to explicit type castLABEL has the same requirements as forEQU, and
the assigned labels can be used in any expression. Eg:MEM	Del_A	375		; declare a 375 sample delay called 'DEL_A'
	wra	DEL_A,0.0	; write to start of delay, DEL_A=0
	rda	del_a^,0.5	; read 0.5*midpoint of delay, DEL_A^=187
	rda	DeL_A#,0.5	; add to 0.5*end of delay, DEL_A#=375The assembler keeps track of allocated memory, placing each new
segment immediately after those previously defined. Each segment
with a delay of LENGTH, will consume LENGTH+1 samples of memory.
An attempt to use more than the available space will trigger
a parse error:MEM	long	0x7f00		; long:0 long#:0x7f00
MEM	short	0x00ff		; short:0x7f01 short#:0x8000 (error)

parse error: Delay exhausted: requested 255 exceeds 254 available on line ...The caret character '^' is also used in expressions as
the bitwise XOR operator, so expressions which reference
a delay may need to be explicitly parenthesised if used with '^':or	delay^0xffff	; parse error - delay label takes caret

parse error: Unexpected INTEGER 0xffff on line ...

	or	(delay)^0xffff	; OK - parentheses enforce ordering
	or	delay^^0xffff	; OKJump TargetsJump targets label a particular address in the program output
and can be placed between instructions anywhere in a source file.
A jump target is a text label followed by a colon ':' character:skp	1,TARGET1	; skip offset is 3
		skp	2,TARGET2	; skip offset is 2
		skp	4,TARGET3	; skip offset is 1
		or	0xff	Target1:	; target after instr
TARGET2:			; target on its own line
tarGET3:	and	0x12	; all three targets point to this instructionUse of an already defined label for a target will result in a parse error:EQU	error	-1
error:	or	0x800000

parse error: Target ERROR already assigned on line ...Target labels are not assigned values until parsing is complete
and they can only be used as a destination for askip instruction. For example,
the following attempt to offset from a target generates a
parse error:skp	NEG,target	; skip to target if negative
	skp	0,target+1	; error - invalid expression
target:	clr			; clear ACC
	wrax	DACL,0.0	; output only positive

parse error: Unexpected OPERATOR + on line ...To achieve the desired if/else behaviour, use a second target:skp	NEG,ifpart	; skip to target if negative
	skp	0,elsept	; else, skip ahead
ifpart:	clr			; clear ACC
elsept:	wrax	DACL,0.0	; output >= 0InstructionsAn instruction is represented by a mnemonic text followed by zero
or moreoperand expressionsseparated by commas:MnemonicOperandsDescriptionrdaADDRESS,MULTIPLIERmultiply delay[ADDRESS] and accumulatermpaMULTIPLERmultiply delay[(*ADDR_PTR)] and accumulatewraADDRESS,MULTIPLIERwrite delay[ADDRESS] and multiplywrapADDRESS,MULTIPLIERwrite delay[ADDRESS], multiply and add LRrdaxREGISTER,MULTIPLIERmultiply (*REGISTER) and accumulaterdfxREGISTER,MULTIPLIERsubtract (*REGISTER), multiply and add (*REGISTER)ldaxREGISTERload (*REGISTER)wraxREGISTER,MULTIPLIERwrite (*REGISTER) and multiplywrhxREGISTER,MULTIPLIERwrite (*REGISTER) and highpass shelfwrlxREGISTER,MULTIPLIERwrite (*REGISTER) and lowpass shelfmaxxREGISTER,MULTIPLIERload maximum of absolute valuesabsaload absolute value of ACCmulxREGISTERmultiply by (*REGISTER)logMULTIPLIER,OFFSETlog2(ACC), multiply and offsetexpMULTIPLIER,OFFSET2**(ACC), multiply and offsetsofMULTIPLIER,OFFSETmultiply and offsetandVALUEbitwise ANDclrclear ACCorVALUEbitwise ORxorVALUEbitwise XORnotbitwise negationskpCONDITIONS,OFFSETskip offset instructions if all conditions metjmpOFFSETjump offset instructionsnopno operationwldsLFO,FREQUENCY,AMPLITUDEajdust SIN LFOwldrLFO,FREQUENCY,AMPLITUDEadjust RMP LFOjamLFOreset LFOchoTYPE,LFO,FLAGS,ADDRESSinterpolated memory accessrawU32insert U32 opcodeEach operand must evaluate to a single constant numeric
value. The sizes and types are specific to each instruction
(seeInstruction Referencebelow).Operand ExpressionsOperand expressions are any valid combination
of labels, numbers, parentheses and the following
operators, listed from highest to lowest precedence. Operators
on the same line have the same precedence, and are evaluated
left to right - except for '**' (power) which worksas in the python intepreter.OperatorFunctionNote|bitwise orvalid for integers only^bitwise xorvalid for integers only&bitwise andvalid for integers only<< >>shift left, shift rightvalid for integers only+ -add, subtract* // /multiply, divide//forces integer divide+ - ~ intunary plus, minus, invert bits, integer cast!is an alias for~**powerBinds right:-10**-2=-0.01The following numeric entry formats are recognised:LiteralValueType123123Decimal integer0x123291Hexadecimal integer$123291Hexadecimal integer0b1010_1111175Binary integer%0101_1111175Binary integer ('_' is ignored)1.1241.124Floating point number1.124e-30.001124Floating point number with exponentThe final value of an expression will be either an
integer, which is used for the instruction operand
unchanged or a floating point value which is later converted
to the closest fixed-point integer of the required size
(seeFixed Point Conversionbelow).
The unaryintoperator will force a floating-point value
to be rounded and converted to the nearest integer:MEM	d0_23	int(0.23*0x8000) ; ~0.23 second delay = 7537 samplesIf the result of the expression is a complex number, or if the
expression cannot be evaluated, a parse error is generated:EQU	j	(-1)**(1/2)	; j=sqrt(-1)

parse error: Expression result (6.123233995736766e-17+1j) invalid type on line ...

EQU	ns	1024<<(-1)	; impossible negative shift

parse error: negative shift count on line ...

EQU	tms	(1024/13)&0x123	; type mismatch

parse error: unsupported operand type(s) for &: 'float' and 'int' on line ...More formally, a valid operand expression matches the
following grammar:expression ::= or_expr
or_expr ::= xor_expr | or_expr "|" xor_expr
xor_expr ::= and_expr | xor_expr "^" and_expr
and_expr ::= shift_expr | and_expr "&" shift_expr
shift_expr ::= a_expr | shift_expr "<<" a_expr | shift_expr ">>" a_expr
a_expr ::=  m_expr | a_expr "+" m_expr | a_expr "-" m_expr
m_expr ::=  u_expr | m_expr "*" u_expr | m_expr "//" u_expr | m_expr "/" u_expr
u_expr ::=  power | "-" u_expr | "+" u_expr | "~" u_expr | "int" u_expr
power ::= atom ["**" u_expr]
atom ::= label | literal | "(" expression ")"Where label is a text label, and literal is a number. Expressions
are parsed and evaluated in-place by asfv1. All labels must be defined
before they are referenced in an expression.Fixed Point ConversionFor instructions that require fixed-point real values as
input, asfv1 automatically converts real expression results
from an intermediate floating-point value to the nearest
equivalent signed fixed-point integer. This value is then
masked to the correct number of bits and placed in machine
code. The conversion is performed for all types by computing
the multiplication:fixed = int(round(floating * REFERENCE)) & MASKWhere REFERENCE is the equivalent integer value of +1.0 in the
desired number format and floating is the saturated intermediate
floating-point value. The following table lists the sizes and range
of each of the FV-1 number formats.Name	Bits	Refval	Minval	Maxval
S4_6	11	64	-16.0	15.984375
S1_9	11	512	-2.0	1.998046875
S_10	11	1024	-1.0	0.9990234375
S1_14	16	16384	-2.0	1.99993896484375
S_15	16	32768	-1.0	0.999969482421875
S_23	24	8388608	-1.0	0.9999998807907104For example, the following entries all generate the same
code:or	-0.4335784912109375		; S_23 real value
	or	-0x377f80&0xffffff		; signed 23bit int to unsigned 24 bit int
	or	0xc88080			; unsigned 24bit int in hexadecimal
	or	13140096			; unsigned 24bit int in decimal
	or	1<<23|2**22|1<<19|2**15|1<<7	; unsigned 24bit int by bitwise or
	or	0b110010001000000010000000	; unsigned 24bit int in binary
	or	int(-0.4335784912109375*2**23)&0xffffff	; S_23 to unsigned 24bit conversionPre-defined LabelsThe following text labels are pre-defined by asfv1.
Refer to the FV-1 datasheet for information on the
function of registers.LabelValueDescriptionSIN0_RATE0x00SIN0 rate control registerSIN0_RANGE0x01SIN0 range control registerSIN1_RATE0x02SIN1 rate control registerSIN1_RANGE0x03SIN1 range control registerRMP0_RATE0x04RMP0 rate control registerRMP0_RANGE0x05RMP0 range control registerRMP1_RATE0x06RMP1 rate control registerRMP1_RANGE0x07RMP1 range control registerPOT00x10POT0 input registerPOT10x11POT1 input registerPOT20x12POT2 input registerADCL0x14Left AD input registerADCR0x15Right AD input registerDACL0x16Left DA output registerDACR0x17Right DA output registerADDR_PTR0x18Delay address pointerREG0-REG310x20-0x3fGeneral purpose registersSIN00x00SIN0 LFO selectorSIN10x01SIN1 LFO selectorRMP00x02RMP0 LFO selectorRMP10x03RMP1 LFO selectorRDA0x00CHO type selectorSOF0x02CHO type selectorRDAL0x03CHO type selectorSIN0x00CHO flagCOS0x01CHO flagREG0x02CHO flagCOMPC0x04CHO flagCOMPA0x08CHO flagRPTR20x10CHO flagNA0x20CHO flagRUN0x10SKP condition flagZRC0x08SKP condition flagZRO0x04SKP condition flagGEZ0x02SKP condition flagNEG0x01SKP condition flagPre-defined labels may be re-defined within a source file,
however, the re-defined value only applies to label referencesfollowingthe assignment. Any re-definition will issue a
warning message:ldax	POT0	; load POT0 (0x10)
EQU	POT0	ADCL	; re-define POT0 to be 0x14
	ldax	POT0	; load from ADCL (0x14)

warning: Label POT0 re-defined on line ...Instruction Referencerda ADDRESS, MULTIPLIERMultiply and accumulate a sample from delay memory.ADDRESS:	Real S_15 or Unsigned 15bit integer delay address
MULTIPLIER:	Real S1_9 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<21 | ADDRESS<<5 | 0b00000Action:ACC <- ACC + MULTIPLIER * delay[ADDRESS]
PACC <- ACC
LR <- delay[ADDRESS]Example:rda	pdel^+324,0.1	; add 0.1 * delay[pdel^+324] to ACC
	rda	pdel#,0.3	; add 0.3 * delay[pdel#] to ACC
	rda	0.3,0.5		; add 0.5 * delay[0x2666] to ACCrmpa MULTIPLIERMultiply and accumulate a sample from the delay memory, using
the contents of ADDR_PTR as the delay address.MULTIPLIER:	Real S1_9 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<21 | 0b00001Action:ACC <- ACC + MULTIPLIER * delay[(*ADDR_PTR)>>8]
PACC <- ACC
LR <- delay[(*ADDR_PTR)>>8]Notes:15 bit delay addresses in ADDR_PTR are left shifted 8 bits,
so they can be accessed using the real S_23 value 0->0.9999 or
directly by multiplying the desired integer delay address by 256.Example:or	1234<<8		; load 1234*256 into ACC
	wrax	ADDR_PTR,0.0	; save to ADDR_PTR and clear ACC
	rmpa	0.25		; add 0.25 * delay[1234] to ACC
	clr
	or	0.5		; load 0.5 into ACC
	wrax	ADDR_PTR,0.0	; save the address pointer
	rmpa	0.7		; add 0.7 * delay[0x4000]wra ADDRESS, MULTIPLIERWrite ACC to delay memory and scale by multiplier.ADDRESS:	Real S_15 or Unsigned 15bit integer delay address
MULTIPLIER:	Real S1_9 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<21 | ADDRESS<<5 | 0b00010Action:delay[ADDRESS] <- ACC
PACC <- ACC
ACC <- MULTIPLIER * ACCExample:wra	pdel^+324,0.25	; write ACC to delay[pdel^+324] scale ACC by 0.25
	wra	pdel#,0.0	; write ACC to delay[pdel#] clear ACCwrap ADDRESS, MULTIPLIERWrite ACC to delay memory, multiply ACC, add to LR and save to ACC.ADDRESS:	Real S_15 or Unsigned 15bit integer delay address
MULTIPLIER:	Real S1_9 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<21 | ADDRESS<<5 | 0b00011Action:delay[ADDRESS] <- ACC
ACC <- LR + MULTIPLIER * ACC
PACC <- ACCExample:ldax	ADCL		; read from left input
	wrap	0x1000,0.3	; write ACC to delay[0x1000] scale ACC by 0.3 add to LRrdax REGISTER, MULTIPLIERMultiply and accumulate contents of register.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b00100Action:ACC <- ACC + MULTIPLIER * (*REGISTER)
PACC <- ACCExample:rdax	POT0,0.11	; add 0.11*POT0 to ACC
	rdax	REG8,-0.66	; subtract 0.66*REG8 from ACCrdfx REGISTER, MULTIPLIERSubtract register content from ACC, multiply and add to register content.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b00101Action:ACC <- (*REGISTER) + MULTIPLIER * (ACC - (*REGISTER))
PACC <- ACCExample:rdfx	ADCL,0.0	; transfer ADCL content to ACC
	rdfx	REG0,0.3	; average using temp reg
	wrlx	REG0,0.0	; infinite shelf LPFldax REGISTERCopy register content to ACC. Assembles to rdax with a multiplier
of 0.0.REGISTER:	Unsigned 6bit integer register address
Assembly:	REGISTER<<5 | 0b00101Action:ACC <- (*REGISTER)
PACC <- ACCExample:ldax	ADCL		; load ADCL content into ACC
	wrax	DACL,0.0	; write ACC to DACLwrax REGISTER, MULTIPLIERCopy ACC to REGISTER, and multiply ACC.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b00110Action:(*REGISTER) <- ACC
ACC <- MULTIPLIER * ACC
PACC <- ACCExample:wrax	REG0,-1.0	; copy ACC into REG0 and invert ACC
	wrax	DACL,0.0	; copy ACC into DAC and clear ACCwrhx REGISTER, MULTIPLIERCopy ACC to REGISTER, multiply ACC and add to PACC.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b00111Action:(*REGISTER) <- ACC
ACC <- PACC + MULTIPLIER * ACC
PACC <- ACCExample:rdfx	REG0,0.3	; average using temp reg
	wrhx	REG0,-0.5	; -6dB shelf highpass filter
	wrhx	REG1,0.0	; swap PACC and ACCwrlx REGISTER, MULTIPLIERCopy ACC to REGISTER, subtract ACC from PACC, multiply and add to PACC.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b01000Action:(*REGISTER) <- ACC
ACC <- PACC + MULTIPLIER * (PACC - ACC)
PACC <- ACCExample:rdfx	REG0,0.3	; average using temp reg
	wrlx	REG0,-0.5	; -6dB shelf lowpass filter
	wrlx	REG1,0.0	; swap PACC and ACCmaxx REGISTER, MULTIPLIERCopy the maximum of the absolute value of ACC and the
absolute value of REGISTER content times MULTIPLIER into ACC.REGISTER:	Unsigned 6bit integer register address
MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
Assembly:	MULTIPLIER<<16 | REGISTER<<5 | 0b01001Action:ACC <- maximum (abs (ACC), abs (MULTIPLIER * (*REGISTER)))
PACC <- ACCExample:ldax	ADCL		; copy ADCL to ACC
	maxx	ADCR,1.0	; copy max of left and right to ACC
	maxx	0,0		; absolute value of ACCabsaCopy the absolute value of ACC back into ACC. Assembles to maxx
with null register and zero multiplier.Assembly:	0b01001Action:ACC <- abs (ACC)
PACC <- ACCExample:ldax	ADCL		; copy ADCL to ACC
	absa			; absolute value of ACCmulx REGISTERMultiply ACC by the content of REGISTER.REGISTER:	Unsigned 6bit integer register address
Assembly:	REGISTER<<5 | 0b01010Action:ACC <- ACC * (*REGISTER)
PACC <- ACCExample:ldax	ADCL		; copy ADCL to ACC
	mulx	POT0		; scale input by POT0log MULTIPLIER, OFFSETCompute the base 2 log of the absolute value of ACC,
multiply and then add offset. Input ACC is S_23,
result ACC is S4_19.MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
OFFSET:		Real S_10 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<16 | OFFSET<<5 | 0b01011Action:ACC <- OFFSET + MULTIPLIER * log2 (abs (ACC))
PACC <- ACCNotes:OFFSET is input as a real S_10 value, however it represents
an S4_6 offset of the accumulator, which is in S4_19 after the log.Example:log	0.5,0.0		; 2*log2(a) = log2(a**2)
	exp	1.0,0.0		; a = 2**(0.5 * log2(a**2))	[square root]exp MULTIPLIER, OFFSETRaise 2 to the power of ACC, multiply and add OFFSET.
Input ACC is S4_16, result ACC is S_23.MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
OFFSET:		Real S_10 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<16 | OFFSET<<5 | 0b01100Action:ACC <- OFFSET + MULTIPLIER * 2**ACC
PACC <- ACCExample:log	1.0,4.0		; log2(16*a) = log2(16) + log2(a)
	exp	1.0,0.0		; 16*a = 2**(4+log2(a))	[x16 gain]sof MULTIPLIER, OFFSETScale ACC and then add an offset.MULTIPLIER:	Real S1_14 or Unsigned 16bit integer
OFFSET:		Real S_10 or Unsigned 11bit integer
Assembly:	MULTIPLIER<<16 | OFFSET<<5 | 0b01101Action:ACC <- OFFSET + MULTIPLIER * ACC
PACC <- ACCExample:sof	1.5,-0.4	; multiply acc by 1.5 and subtract 0.4and VALUEPerform a bitwise AND of ACC and VALUEVALUE:		Real S_23 or Unsigned 24bit integer
Assembly:	VALUE<<8 | 0b01110Action:ACC <- ACC & VALUE
PACC <- ACCExample:ldax	POT0		; load POT0 into ACC
	and	0x700000	; mask pot to 8 steps
	and	0		; clear ACCclrPerform a bitwise AND of ACC with zero - clearing ACCAssembly:	0b01110Action:ACC <- 0
PACC <- ACCExample:clr			; clear ACC
	rda	1234,1.0	; load delay[1234]or VALUEPerform a bitwise OR of ACC and VALUEVALUE:		Real S_23 or Unsigned 24bit integer
Assembly:	VALUE<<8 | 0b01111Action:ACC <- ACC | VALUE
PACC <- ACCExample:clr			; clear ACC
	or	-2.3427e-4	; load an immediate value into ACC
	or	0x0a40f1	; set specific bits in ACCxor VALUEPerform a bitwise XOR of ACC and VALUEVALUE:		Real S_23 or Unsigned 24bit integer
Assembly:	VALUE<<8 | 0b10000Action:ACC <- ACC ^ VALUE
PACC <- ACCExample:ldax	POT0		; load POT0
	and	0x7f0000	; mask off lower bits
	xor	0x150000	; compare with 0x150000
	skp	ZRO,equal	; if same, skip to equal
	xor	0x150000	; else restore original valuenotPerform a bitwise negation of ACC by XOR with 0xffffff.Assembly:	0xffffff<<8 | 0b10000Action:ACC <- ~ACC
PACC <- ACCExample:ldax	POT0		; load POT0
	not			; invert all bitsskp CONDITIONS, OFFSETSkip over OFFSET instructions if all flagged CONDITIONS are met.CONDITIONS:	Unsigned 5bit flags
OFFSET:		Unsigned 6bit integer or target label
Assembly:	CONDITIONS<<27 | OFFSET<<21 | 0b10001Condition Flags:NEG	0x01	ACC is less than zero
GEZ	0x02	ACC is greater than or equal to zero
ZRO	0x04	ACC is zero
ZRC	0x08	sign of ACC and PACC differ
RUN	0x10	Program has completed at least one iterationNotes:if no condition flags are set, the skip is always performed.if OFFSET starts with a label, it is assumed to be a jump target,
which should be present later in the program. An attempt to skip
backward will raise an error:start:	clr
 	skp	0,start		; try to skip backward

 parse error: Target START does not follow SKP on line ...the maximum possible skip offset is 63, an error will be generated if
the named target is out of range:skp	0,target
 	[>63 instructions]
 target: clr

 parse error: Offset from SKP to TARGET (0x40) too large on line ...To force computation of an offset, wrap an expression in parentheses:EQU	three	3
 	skp	0,three+3	; error - three is not a target

 parse error: Unexpected OPERATOR + on line ...
 parse error: Undefined target THREE for SKP on line ...

 	skp	0,(three+3)	; ok, offset is evaluated as expressionif mutually exclusive conditions are specified, the skip is
assembled but never performed, turning the instruction into NOP:skp	NEG|ZRO,target	; ACC cannot be negative AND zeroExample:skp	0,target	; unconditionally skip to target
	ldax	ADCL		; read in ADCL
	ldax	REG0		; load a previous value
	skp	ZRC|NEG,target	; skip to target on positive zero crossing
	skp	RUN,1		; skip 1 instruction except on first runjmp OFFSETPerform an unconditional skip over OFFSET instructions.OFFSET:		Unsigned 6bit integer or target label
Assembly:	OFFSET<<21 | 0b10001Notes:jmp is assembled as skp 0,OFFSET. See skp above for details on
specifying jmp targets.Example:jmp	target		; unconditionally skip to target
	jmp	3		; always skip over 3 instructionsnopNo operation, equivalent to skp 0,0. Use for padding, or blocking.Assembly:	0b10001Example:nop nop nop nop		; reserve 4 instruction slotswlds LFO, FREQUENCY, AMPLITUDEAdjust SIN LFO with coefficients FREQUENCY and AMPLITUDE.LFO:		1bit integer (0 = SIN0 or 1 = SIN1)
FREQUENCY:	Unsigned 9bit integer
AMPLITUDE:	Real S_15 or Unsigned 15bit integer
Assembly:	LFO<<29 | FREQUENCY<<20 | AMPLITUDE<<5 | 0b10010Notes:FREQUENCY coefficient is related to LFO rate (f) in Hz
by the following:FREQUENCY = int (2**18 * pi * f / Fs)
 f = FREQUENCY * Fs / (2**18 * pi)Where Fs is the sample rate. For a 32768Hz crystal, the SIN
LFO ranges from 0Hz up to about 20Hz.AMPLITUDE coefficient specifies the peak-to-peak amplitude
of the LFO in delay samples, and may be entered using a real
value. Negative amplitudes work as with SINx_RANGE register.The frequency and amplitude of SIN LFOs can also be set
by writing to registers: SIN0_RATE, SIN0_RANGE, SIN1_RATE,
and SIN1_RANGE.Example:wlds	SIN0,511,1	; Set SIN0 to 20Hz, amplitude 1 sample
	wlds	SIN1,1,0x7fff	; Set SIN1 to 0.04Hz and full delay length
	or	0.5
	wrax	SIN0_RATE,0.0	; Set SIN0 to ~10Hz
	ldax	POT0
	wrax	SIN0_RANGE,0.0	; Set SIN0 range from POT0wldr LFO, FREQUENCY, AMPLITUDEAdjust RMP LFO with coefficients FREQUENCY and AMPLITUDE.LFO:		1bit integer (0 = RMP0 or 1 = RMP1)
FREQUENCY:	Real S_15 or Signed 16bit integer
AMPLITUDE:	2bit integer (0=4096, 1=2048, 2=1024, 3=512)
Assembly:	(LFO|0x2)<<29 | FREQUENCY<<13 | AMPLITUDE<<5 | 0b10010Notes:LFO may also be set using pre-defined labels RMP0 and RMP1.AMPLITUDE may also be set by entering one of the specific
integer values: 4096, 2048, 1024 or 512.FREQUENCY may be entered using a real value, which has the same
interpretation as the RMPx_RATE register.The frequency and amplitude of RMP LFOs can also be set
by writing to registers: RMP0_RATE, RMP0_RANGE, RMP1_RATE,
and RMP1_RANGE.Example:wldr	RMP0,32767,0	; Set RMP0 to max rate, 4096 amplitude
	wldr	RMP1,-1923,512	; Set RMP1 to 512 and a negative frequencyjam LFOReset specified ramp LFO to start.LFO:		1bit integer (0 = RMP0 or 1 = RMP1)
Assembly:	(LFO|0x2)<<6 | 0b10011Note:LFO may also be set using pre-defined labels RMP0 and RMP1.Example:jam	RMP0		; reset RMP0 lfocho rda, LFO, FLAGS, ADDRESSRead from delay memory at ADDRESS + offset (LFO) according to
FLAGS, multiply the result by coeff (LFO) and accumulate.LFO:		2bit integer (SIN0, SIN1, RMP0 or RMP1)
FLAGS:		6bit integer flags
ADDRESS:	Real S_15 or Unsigned 16bit integer
Assembly:	FLAGS<<24 | LFO<<21 | ADDRESS<<5 | 0x10100Action:ACC <- ACC + coeff (LFO) * delay[ADDRESS + offset (LFO)]
PACC <- ACCFlags:COS	0x01	use cosine output of SIN LFO 
REG	0x02	'register' LFO state (see note below)
COMPC	0x04	complement coefficient: 1 - coeff (LFO) 
COMPA	0x08	complement address offset: 1 - offset (LFO)
RPTR2	0x10	use second, half-off ramp 
NA	0x20	offset (LFO) = 0.0, coeff (LFO) = crossfade coefficientNotes:offset (LFO) is the coarse LFO delay offset, based on flag settings
to a whole samplecoeff (LFO) is an interpolation coefficient, based on flag settings
and the LFO fine position between whole samplesthe first use of cho in a program with any LFO must include the
REG flag in order to 'register' the LFO state and get valid dataflags that are not relevant for the chosen LFO or cho mode are ignoredExample:cho	rda,SIN0,REG|COMPC,20	; load first half of interpolation
	cho	rda,SIN0,0,21		; add second half of interpolationcho sof, LFO, FLAGS, OFFSETMultiply ACC by coeff (LFO), and add OFFSET.LFO:		2bit integer (SIN0, SIN1, RMP0 or RMP1)
FLAGS:		6bit integer flags (see cho rda)
OFFSET:		Real S_15 or Unsigned 16bit integer
Assembly:	0x2<<30 | FLAGS<<24 | LFO<<21 | OFFSET<<5 | 0x10100Action:ACC <- coeff (LFO) * ACC + OFFSET
PACC <- ACCExample:ldax	ADCL			; load AD
	cho	sof,RMP0,REG|COMPC|NA,0	; tremolo - scale ACC by RMP0 xfadecho rdal, LFO [, FLAGS]Read the specified LFO address offset value into ACC
according to optional FLAGS. If FLAGS are omitted, a default
value of REG (0x2) is assembled.LFO:		2bit integer (SIN0, SIN1, RMP0 or RMP1)
FLAGS:		6bit integer flags (see cho rda and notes)
Assembly:	0x3<<30 | FLAGS<<24 | LFO<<21 | 0x10100Action:ACC <- ACC + offset (LFO)
PACC <- ACCNotes:REG flag must be set in order to get a meaningful value in ACC
value (this is the default if FLAGS are omitted)Only COS flag has any affect on the instruction, and only when used with
SIN LFO.Example:cho	rdal,SIN0,REG	; load the SIN value and 'register' LFO
	wrax	DACL,0.0	; output to left channel
	cho	rdal,SIN0,COS	; load the COS value
	wrax	DACR,0.0	; output to right channelraw U32Copy the unsigned 32 bit value in U32 directly to the output program.U32:		Unsigned 32bit integer
Assembly:	U32Example:raw	0x4000000f	; manually assemble "or 0.5"
	skp	0,1		; skip over the next instruction
	raw	0xa899fbda	; place a signature in the binarySystem Specific InstallationThe preferred method for installation is to use your system's
packaged pip3 command to fetch and install asfv1 fromPyPiand set it up to work with a python3
interpreter.Linux with apt (Debian, Ubuntu)$ sudo apt install python3-venv python3-pip
$ pip3 install asfv1Linux with yum (Fedora 21)$ sudo yum install python3 python3-wheel
$ pip3 install asfv1Linux with dnf (Fedora 22)$ sudo dnf install python3 python3-wheel
$ pip3 install asfv1Arch Linux$ sudo pacman -S python-pip
$ pip install asfv1MacOSDownload a copy of the "Latest Python 3 Release"
for Mac OS frompython.org.
Install the package, then open a terminal and run:$ pip3 install asfv1WindowsDownload a copy of the "Latest Python 3 Release"
for Windows frompython.org.
Install the package, then open a command prompt and run:C:\> pip3 install asfv1For more detailed information, please refer to thePython package installation documentationandinstalling pip with packaging managersatpackaging.python.org.Install from SourceIf you would prefer to not use pip, or if your system is provided with
and older version of Python (eg MacOS), asfv1 can be installed using
the included setup.py script. Fetch a copy of the latest source package,
unpack it and then run the installer as root:$ sudo python ./setup.py installAlternatively, the main source file can be run directly with a python
interpreter without the need to install any files:$ python ./asfv1.py infile.asm outfile.binLinksFV-1 disassembler:https://github.com/ndf-zz/disfv1FV-1 test suite:https://github.com/ndf-zz/fv1testingDervish eurorack FV-1 module:http://gbiswell.myzen.co.uk/dervish/Readme_First.htmlSpin FV-1 website:http://spinsemi.com/products.htmlDatasheet:http://spinsemi.com/Products/datasheets/spn1001/FV-1.pdfAN0001:http://spinsemi.com/Products/appnotes/spn1001/AN-0001.pdf