From e472bb908846dd130a26323dc0f166318250f805 Mon Sep 17 00:00:00 2001 From: Aaron Zuspan <50475791+aazuspan@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:45:32 -0700 Subject: [PATCH] Generate markdown documentation on the fly (#17) --- src/spinasm_lsp/docs/__init__.py | 24 + src/spinasm_lsp/docs/assemblers.py | 140 ++++ src/spinasm_lsp/docs/assemblers/equ.md | 42 - src/spinasm_lsp/docs/assemblers/mem.md | 64 -- src/spinasm_lsp/docs/instructions.py | 732 ++++++++++++++++++ src/spinasm_lsp/docs/instructions/absa.md | 23 - src/spinasm_lsp/docs/instructions/and.md | 31 - src/spinasm_lsp/docs/instructions/cho rda.md | 66 -- src/spinasm_lsp/docs/instructions/cho rdal.md | 25 - src/spinasm_lsp/docs/instructions/cho sof.md | 49 -- src/spinasm_lsp/docs/instructions/clr.md | 28 - src/spinasm_lsp/docs/instructions/exp.md | 33 - src/spinasm_lsp/docs/instructions/jam.md | 24 - src/spinasm_lsp/docs/instructions/ldax.md | 24 - src/spinasm_lsp/docs/instructions/log.md | 32 - src/spinasm_lsp/docs/instructions/maxx.md | 37 - src/spinasm_lsp/docs/instructions/mulx.md | 37 - src/spinasm_lsp/docs/instructions/not.md | 23 - src/spinasm_lsp/docs/instructions/or.md | 31 - src/spinasm_lsp/docs/instructions/rda.md | 33 - src/spinasm_lsp/docs/instructions/rdax.md | 34 - src/spinasm_lsp/docs/instructions/rdfx.md | 33 - src/spinasm_lsp/docs/instructions/rmpa.md | 32 - src/spinasm_lsp/docs/instructions/skp.md | 47 -- src/spinasm_lsp/docs/instructions/sof.md | 33 - .../docs/instructions/template.txt | 35 - src/spinasm_lsp/docs/instructions/wldr.md | 37 - src/spinasm_lsp/docs/instructions/wlds.md | 37 - src/spinasm_lsp/docs/instructions/wra.md | 33 - src/spinasm_lsp/docs/instructions/wrap.md | 27 - src/spinasm_lsp/docs/instructions/wrax.md | 38 - src/spinasm_lsp/docs/instructions/wrhx.md | 36 - src/spinasm_lsp/docs/instructions/wrlx.md | 37 - src/spinasm_lsp/docs/instructions/xor.md | 30 - src/spinasm_lsp/docs/markdown.py | 78 ++ src/spinasm_lsp/documentation.py | 54 -- src/spinasm_lsp/server.py | 12 +- tests/conftest.py | 2 +- tests/test_documentation.py | 7 +- 39 files changed, 985 insertions(+), 1155 deletions(-) create mode 100644 src/spinasm_lsp/docs/assemblers.py delete mode 100644 src/spinasm_lsp/docs/assemblers/equ.md delete mode 100644 src/spinasm_lsp/docs/assemblers/mem.md create mode 100644 src/spinasm_lsp/docs/instructions.py delete mode 100644 src/spinasm_lsp/docs/instructions/absa.md delete mode 100644 src/spinasm_lsp/docs/instructions/and.md delete mode 100644 src/spinasm_lsp/docs/instructions/cho rda.md delete mode 100644 src/spinasm_lsp/docs/instructions/cho rdal.md delete mode 100644 src/spinasm_lsp/docs/instructions/cho sof.md delete mode 100644 src/spinasm_lsp/docs/instructions/clr.md delete mode 100644 src/spinasm_lsp/docs/instructions/exp.md delete mode 100644 src/spinasm_lsp/docs/instructions/jam.md delete mode 100644 src/spinasm_lsp/docs/instructions/ldax.md delete mode 100644 src/spinasm_lsp/docs/instructions/log.md delete mode 100644 src/spinasm_lsp/docs/instructions/maxx.md delete mode 100644 src/spinasm_lsp/docs/instructions/mulx.md delete mode 100644 src/spinasm_lsp/docs/instructions/not.md delete mode 100644 src/spinasm_lsp/docs/instructions/or.md delete mode 100644 src/spinasm_lsp/docs/instructions/rda.md delete mode 100644 src/spinasm_lsp/docs/instructions/rdax.md delete mode 100644 src/spinasm_lsp/docs/instructions/rdfx.md delete mode 100644 src/spinasm_lsp/docs/instructions/rmpa.md delete mode 100644 src/spinasm_lsp/docs/instructions/skp.md delete mode 100644 src/spinasm_lsp/docs/instructions/sof.md delete mode 100644 src/spinasm_lsp/docs/instructions/template.txt delete mode 100644 src/spinasm_lsp/docs/instructions/wldr.md delete mode 100644 src/spinasm_lsp/docs/instructions/wlds.md delete mode 100644 src/spinasm_lsp/docs/instructions/wra.md delete mode 100644 src/spinasm_lsp/docs/instructions/wrap.md delete mode 100644 src/spinasm_lsp/docs/instructions/wrax.md delete mode 100644 src/spinasm_lsp/docs/instructions/wrhx.md delete mode 100644 src/spinasm_lsp/docs/instructions/wrlx.md delete mode 100644 src/spinasm_lsp/docs/instructions/xor.md create mode 100644 src/spinasm_lsp/docs/markdown.py delete mode 100644 src/spinasm_lsp/documentation.py diff --git a/src/spinasm_lsp/docs/__init__.py b/src/spinasm_lsp/docs/__init__.py index e69de29..9353ccb 100644 --- a/src/spinasm_lsp/docs/__init__.py +++ b/src/spinasm_lsp/docs/__init__.py @@ -0,0 +1,24 @@ +from spinasm_lsp.docs.assemblers import ASSEMBLERS +from spinasm_lsp.docs.instructions import INSTRUCTIONS +from spinasm_lsp.docs.markdown import MarkdownDocumentationGenerator + + +class DocumentationManager: + """A manager for case-insensitive documentation lookups.""" + + data: dict[str, MarkdownDocumentationGenerator] = {**INSTRUCTIONS, **ASSEMBLERS} + + def __getitem__(self, key: str) -> str: + return str(self.data[key.upper()]) + + def get(self, key: str, default: str = "") -> str: + return str(self.data.get(key.upper(), default)) + + def __contains__(self, key: str) -> bool: + return self.data.__contains__(key.upper()) + + def __iter__(self): + return iter(self.data) + + +__all__ = ["DocumentationManager"] diff --git a/src/spinasm_lsp/docs/assemblers.py b/src/spinasm_lsp/docs/assemblers.py new file mode 100644 index 0000000..18f3c54 --- /dev/null +++ b/src/spinasm_lsp/docs/assemblers.py @@ -0,0 +1,140 @@ +"""Markdown documentation generation for SpinASM assemblers.""" + +# ruff: noqa: E501 + +from dataclasses import dataclass +from functools import cached_property + +from spinasm_lsp.docs.markdown import MarkdownDocumentationGenerator, MarkdownString + + +@dataclass +class AssemblerDocumentation(MarkdownDocumentationGenerator): + """Generatable Markdown documentation for a SPINAsm assembler.""" + + name: str + description: str + example: str + example_remarks: str = "" + + @cached_property + def markdown(self) -> str: + """A markdown documentation string.""" + md = MarkdownString() + + md.add_heading(f"`{self.name}`", level=2) + md.add_horizontal_rule() + + md.add_paragraph(self.description.strip()) + + md.add_heading("Example", level=3) + md.add_codeblock(self.example.strip(), language="assembly") + + if self.example_remarks: + md.add_paragraph(self.example_remarks.strip()) + + md.add_horizontal_rule() + md.add_paragraph( + "*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference " + "manual. Copyright 2008 by Spin Semiconductor.*" + ) + + return str(md) + + +ASSEMBLERS = { + "EQU": AssemblerDocumentation( + name="EQU", + description="""The `EQU` statement allows one to define symbolic operands in order to increase the readability of the source code. Technically an `EQU` statement such as: + +```assembly +Name EQU Value [;Comment] +``` + +will cause SPINAsm to replace any occurrence of the literal "Name" by the literal "Value" within each instruction line during the assembly process excluding the comment portion of an instruction line. + +With the exception of blanks, any printable character is allowed within the literal "Name". However there are restrictions: "Name" must be an unique string, is limited to 32 characters and the first character must be a letter excluding the "+" and "­" signs and the "!" character. + +The reason for not allowing these characters being the first character of "Name" is that any symbolic operand may be prefixed with a sign or the "!" negation operator within the instruction line. The assembler will then perform the required conversion of the operand while processing the individual instruction lines. + +There is another, not syntax related, restriction when using symbolic operands defined by an `EQU` statement: Predefined symbols. As given in the end of the manual there is a set of predefined symbolic operands which should be omitted as "Name" literals within an `EQU` statement. It is not that these predefined symbols are prohibited, it is just that using them within an `EQU` statement will overwrite their predefined value. + +With the literal "Value" things are slightly more complicated since its format has to comply with the syntactical rules defined for the operand type it is to represent. Although it is suggested to place `EQU` statements at the beginning of the source code file, this is not mandatory. However, the `EQU` statement has to be defined before the literal "Name" can be used as a symbolical operand within an instruction line. + +### Remark +SPINAsm has no way of performing range checking while processing the EQU statement. This is because the operand type of value is not known to SPINAsm at the time the EQU statement is processed. As a result, range checking is performed when assembling the instruction line in which "Name" is to be replaced by "Value". +""", + example=""" +Attn EQU 0.5 ; 0.5 = -6dB attenuation +Tmp_Reg EQU 63 ; Temporary register within register file +Tmp_Del EQU $2000 ; Temporary memory location within delay ram +; +;------------------------------ +sof 0,0 ; Clear ACC +rda Tmp_Del,Attn ; Load sample from delay ram $2000, + ; multiply it by 0.5 and add ACC content +wrax Tmp_Reg,1.0 ; Save result to Tmp_Reg but keep it in ACC +wrax DACL,0 ; Move ACC to DAC left (predefined symbol) + ; and then clear ACC +""", + example_remarks="""If `Tmp_Del` was accidentally replaced by `Tmp_Reg` within the `rda` instruction line, SPINAsm would not detect this semantic error – simply because using `Tmp_Reg` would be syntactically correct.""", + ), + "MEM": AssemblerDocumentation( + name="MEM", + description="""The `MEM` Statement allows the user to partition the delay ram memory into individual blocks. A memory block declared by the statement + +```assembly +Name `MEM` Size [;Comment] +``` + +can be referenced by `Name` from within an instruction line. `Name` has to comply with the same syntactical rules previously defined with the EQU statement, "Size" is an unsigned integer in the range of 1 to 32768 which might be entered either in decimal or in hexadecimal. + +Besides the explicit identifier `Name` the assembler defines two additional implicit identifiers, `Name#` and `Name^`. `Name` refers to the first memory location within the memory block, whereas `Name#` refers to the last memory location. The identifier `Name^` references the middle of the memory block, or in other words its center. If a memory block of size 1 is defined, all three identifiers will address the same memory location. In case the memory block is of size 2, `Name` and `Name^` will address the same memory location, if the size is an even number the memory block cannot exactly be halved – the midpoint `Name^` will be calculated as: `size MOD 2`. + +Optionally all three identifiers can be offset by a positive or negative integer which is entered in decimal. Although range checking is performed when using offsets, there is no error generated if the result of the address calculation exceeds the address range of the memory block. This is also true for those cases in which the result will "wrap around" the physical 32k boundary of the delay memory. However, a warning will be issued in order to alert the user regarding the out of range condition. + +Mapping the memory blocks to their physical delay ram addresses is solely handled by SPINAsm. The user has no possibility to explicitly force SPINAsm to place a certain memory block to a specific physical address range. This of course does not mean that the user has no control over the layout of the delay ram at all: Knowing that SPINAsm will map memory blocks in the order they become defined within the source file, the user can implicitly control the memory map of the delay ram. +""", + example="""DelR MEM 1024 ; Right channel delay line +DelL MEM 1024 ; Left channel delay line + ; +;------------------------------ +sof 0,0 ; Clear ACC +rdax ADCL,1.0 ; Read in left ADC +wra DelL,0.25 ; Save it to the start of the left delay + ; line and keep a -12dB replica in ACC +rdax DelL^+20,0.25; Add sample from "center of the left delay + ; line + 20 samples" times 0.25 to ACC +rdax DelL#,0.25 ; Add sample from "end of the left delay + ; line" times 0.25 to ACC +rdax DelL-512,0.25; Add sample from "start of the left delay + ; line - 512 samples" times 0.25 to ACC +""", + example_remarks="""### Remark +At this point the result of the address calculation will reference a sample from outside the `DelL` memory block. While being syntactically correct, the instruction might not result in what the user intended. In order to make the user aware of that potential semantic error, a warning will be issued. + +```assembly +wrax DACL,0 ; Result to DACL, clear ACC + ; +rdax ADCR,1.0 ; Read in right ADC +wra DelR,0.25 ; Save it to the start of the right delay + ; line and keep a -12dB replica in ACC +rdax DelR^-20,0.25; Add sample from center of the right delay + ; line - 20 samples times 0.25 to ACC +rdax DelR#,0.25 ; Add sample from end of the right delay line + ; times 0.25 to ACC +rdax DelR-512,0.25; Add sample from start of the right delay + ; line - 512 samples times 0.25 to ACC +``` + +### Remark +At this point the result of the address calculation will reference a sample from outside the `DelR` memory block. And even worse than the previous case: This time the sample be fetched from delay ram address 32256 which will contain a sample that is apx. 1 second old! + +Again, syntactically correct but most likely a semantic error – warnings will be issued. + +```assembly +wrax DACR,0 ; Result to DACR, clear ACC +``` +""", + ), +} diff --git a/src/spinasm_lsp/docs/assemblers/equ.md b/src/spinasm_lsp/docs/assemblers/equ.md deleted file mode 100644 index 8be6eba..0000000 --- a/src/spinasm_lsp/docs/assemblers/equ.md +++ /dev/null @@ -1,42 +0,0 @@ -## `EQU` - ------------------- - -The `EQU` statement allows one to define symbolic operands in order to increase the readability of the source code. Technically an `EQU` statement such as: - -```assembly -Name EQU Value [;Comment] -``` - -will cause SPINAsm to replace any occurrence of the literal "Name" by the literal "Value" within each instruction line during the assembly process excluding the comment portion of an instruction line. - -With the exception of blanks, any printable character is allowed within the literal "Name". However there are restrictions: "Name" must be an unique string, is limited to 32 characters and the first character must be a letter excluding the "+" and "­" signs and the "!" character. - -The reason for not allowing these characters being the first character of "Name" is that any symbolic operand may be prefixed with a sign or the "!" negation operator within the instruction line. The assembler will then perform the required conversion of the operand while processing the individual instruction lines. - -There is another, not syntax related, restriction when using symbolic operands defined by an `EQU` statement: Predefined symbols. As given in the end of the manual there is a set of predefined symbolic operands which should be omitted as "Name" literals within an `EQU` statement. It is not that these predefined symbols are prohibited, it is just that using them within an `EQU` statement will overwrite their predefined value. - -With the literal "Value" things are slightly more complicated since its format has to comply with the syntactical rules defined for the operand type it is to represent. Although it is suggested to place `EQU` statements at the beginning of the source code file, this is not mandatory. However, the `EQU` statement has to be defined before the literal "Name" can be used as a symbolical operand within an instruction line. - -### Remark -SPINAsm has no way of performing range checking while processing the EQU statement. This is because the operand type of value is not known to SPINAsm at the time the EQU statement is processed. As a result, range checking is performed when assembling the instruction line in which "Name" is to be replaced by "Value". - -### Example -```assembly -Attn EQU 0.5 ; 0.5 = -6dB attenuation -Tmp_Reg EQU 63 ; Temporary register within register file -Tmp_Del EQU $2000 ; Temporary memory location within delay ram -; -;------------------------------ -sof 0,0 ; Clear ACC -rda Tmp_Del,Attn ; Load sample from delay ram $2000, - ; multiply it by 0.5 and add ACC content -wrax Tmp_Reg,1.0 ; Save result to Tmp_Reg but keep it in ACC -wrax DACL,0 ; Move ACC to DAC left (predefined symbol) - ; and then clear ACC -``` - -If `Tmp_Del` was accidentally replaced by `Tmp_Reg` within the `rda` instruction line, SPINAsm would not detect this semantic error – simply because using `Tmp_Reg` would be syntactically correct. - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/assemblers/mem.md b/src/spinasm_lsp/docs/assemblers/mem.md deleted file mode 100644 index 8ca98af..0000000 --- a/src/spinasm_lsp/docs/assemblers/mem.md +++ /dev/null @@ -1,64 +0,0 @@ -## `MEM` - ------------------- - -The `MEM` Statement allows the user to partition the delay ram memory into individual blocks. A memory block declared by the statement - -```assembly -Name `MEM` Size [;Comment] -``` - -can be referenced by `Name` from within an instruction line. `Name` has to comply with the same syntactical rules previously defined with the EQU statement, "Size" is an unsigned integer in the range of 1 to 32768 which might be entered either in decimal or in hexadecimal. - -Besides the explicit identifier `Name` the assembler defines two additional implicit identifiers, `Name#` and `Name^`. `Name` refers to the first memory location within the memory block, whereas `Name#` refers to the last memory location. The identifier `Name^` references the middle of the memory block, or in other words its center. If a memory block of size 1 is defined, all three identifiers will address the same memory location. In case the memory block is of size 2, `Name` and `Name^` will address the same memory location, if the size is an even number the memory block cannot exactly be halved – the midpoint `Name^` will be calculated as: `size MOD 2`. - -Optionally all three identifiers can be offset by a positive or negative integer which is entered in decimal. Although range checking is performed when using offsets, there is no error generated if the result of the address calculation exceeds the address range of the memory block. This is also true for those cases in which the result will "wrap around" the physical 32k boundary of the delay memory. However, a warning will be issued in order to alert the user regarding the out of range condition. - -Mapping the memory blocks to their physical delay ram addresses is solely handled by SPINAsm. The user has no possibility to explicitly force SPINAsm to place a certain memory block to a specific physical address range. This of course does not mean that the user has no control over the layout of the delay ram at all: Knowing that SPINAsm will map memory blocks in the order they become defined within the source file, the user can implicitly control the memory map of the delay ram. - -### Example -```assembly -DelR MEM 1024 ; Right channel delay line -DelL MEM 1024 ; Left channel delay line - ; -;------------------------------ -sof 0,0 ; Clear ACC -rdax ADCL,1.0 ; Read in left ADC -wra DelL,0.25 ; Save it to the start of the left delay - ; line and keep a -12dB replica in ACC -rdax DelL^+20,0.25; Add sample from "center of the left delay - ; line + 20 samples" times 0.25 to ACC -rdax DelL#,0.25 ; Add sample from "end of the left delay - ; line" times 0.25 to ACC -rdax DelL-512,0.25; Add sample from "start of the left delay - ; line - 512 samples" times 0.25 to ACC -``` - -### Remark -At this point the result of the address calculation will reference a sample from outside the `DelL` memory block. While being syntactically correct, the instruction might not result in what the user intended. In order to make the user aware of that potential semantic error, a warning will be issued. - -```assembly -wrax DACL,0 ; Result to DACL, clear ACC - ; -rdax ADCR,1.0 ; Read in right ADC -wra DelR,0.25 ; Save it to the start of the right delay - ; line and keep a -12dB replica in ACC -rdax DelR^-20,0.25; Add sample from center of the right delay - ; line - 20 samples times 0.25 to ACC -rdax DelR#,0.25 ; Add sample from end of the right delay line - ; times 0.25 to ACC -rdax DelR-512,0.25; Add sample from start of the right delay - ; line - 512 samples times 0.25 to ACC -``` - -### Remark -At this point the result of the address calculation will reference a sample from outside the `DelR` memory block. And even worse than the previous case: This time the sample be fetched from delay ram address 32256 which will contain a sample that is apx. 1 second old! - -Again, syntactically correct but most likely a semantic error – warnings will be issued. - -```assembly -wrax DACR,0 ; Result to DACR, clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions.py b/src/spinasm_lsp/docs/instructions.py new file mode 100644 index 0000000..667e8a7 --- /dev/null +++ b/src/spinasm_lsp/docs/instructions.py @@ -0,0 +1,732 @@ +"""Markdown documentation generation for SpinASM instructions.""" + +# ruff: noqa: E501 + +from __future__ import annotations + +from dataclasses import dataclass +from functools import cached_property + +from spinasm_lsp.docs.markdown import MarkdownDocumentationGenerator, MarkdownString + + +@dataclass +class InstructionArg: + """Metadata for an argument to a SpinASM instruction.""" + + name: str + width: int + formats: list[str] + + +@dataclass +class InstructionDocumentation(MarkdownDocumentationGenerator): + """Generatable Markdown documentation for a SPINAsm instruction.""" + + name: str + args: list[InstructionArg] + description: str + operation: str + coding: str + example: str + parameter_description: str | None = None + + def __post_init__(self): + if isinstance(self.args, InstructionArg): + self.args = [self.args] + + @cached_property + def markdown(self) -> str: + """A markdown documentation string.""" + md = MarkdownString() + signature = f"`{self.name} {', '.join([arg.name for arg in self.args])}`" + + md.add_heading(signature, level=2) + md.add_horizontal_rule() + + md.add_paragraph(self.description.strip()) + + md.add_heading("Operation", level=3) + md.add_paragraph(f"`{self.operation}`") + + md.add_heading("Parameters", level=3) + if not self.args: + md.add_paragraph("None.") + else: + md.add_table( + cols=["Name", "Width", "Entry formats, range"], + rows=[ + [arg.name, f"{arg.width} Bit", "
".join(arg.formats)] + for arg in self.args + ], + ) + if self.parameter_description: + md.add_paragraph(self.parameter_description) + + md.add_heading("Instruction Coding", level=3) + md.add_paragraph(f"**{self.coding}**") + + md.add_heading("Example", level=3) + md.add_codeblock(self.example.strip(), language="assembly") + + md.add_horizontal_rule() + md.add_paragraph( + "*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference " + "manual. Copyright 2008 by Spin Semiconductor.*" + ) + + return str(md) + + +INSTRUCTIONS = { + "ABSA": InstructionDocumentation( + name="ABSA", + args=[], + description="""Loads the accumulator with the absolute value of the accumulator.""", + parameter_description=None, + operation="|ACC| ­> ACC", + coding="00000000000000000000000000001001", + example=""" absa ; Absolute value of ACC -> ACC +""", + ), + "AND": InstructionDocumentation( + name="AND", + args=[ + InstructionArg("M", 24, ["Binary", "Hex ($000000 - $FFFFFF)", "Symbolic"]), + ], + description="""AND will perform a bit wise "and" of the current ACC and the 24-­bit MASK specified within the instruction word. The instruction might be used to load a constant into ACC provided ACC contains $FFFFFF or to clear ACC if MASK equals $000000. (see also the pseudo opcode section)""", + parameter_description=None, + operation="ACC & MASK", + coding="MMMMMMMMMMMMMMMMMMMMMMMM000001110", + example="""AMASK EQU $F0FFFF + +;---------------------------------------- + or ­­­­­­­­­­­­­­­­­­$FFFFFF ; Set all bits within ACC + and $FFFFFE ; Clear LSB + and %01111111_11111111_11111111 ; Clear MSB + and AMASK ; Clear ACC[19..16] + and $0 ; Clear ACC +""", + ), + "CHO RDA": InstructionDocumentation( + name="CHO RDA", + args=[ + InstructionArg("N", 2, ["LFO select: SIN0,SIN1,RMP0,RMP1"]), + InstructionArg("C", 6, ["Binary", "Bit flags"]), + InstructionArg("D", 16, ["Real (S.15)", "Symbolic"]), + ], + description="""Like the `RDA` instruction, `CHO RDA` will read a sample from the delay ram, multiply it by a coefficient and add the product to the previous content of ACC. However, in contrast to `RDA` the coefficient is not explicitly embedded within the instruction and the effective delay ram address is not solely determined by the address parameter. Instead, both values are modulated by the selected LFO at run time, for an in depth explanation please consult the FV­1 datasheet alongside with application note AN­0001. `CHO RDA` is a very flexible and powerful instruction, especially useful for delay line modulation effects such as chorus or pitch shifting. + +The coefficient field of the `CHO` instructions are used as control bits to select various aspects of the LFO. These bits can be set using predefined flags that are `OR`ed together to create the required bit field. For a sine wave LFO (SIN0 or SIN1), valid flags are: + +`SIN COS REG COMPC COMPA` + +While for a ramp LFO (RMP0 and RMP1), valid flags are: + +`REG COMPC COMPA RPTR2 NA` + +These flags are defined as: + +| Flag | HEX value | Description | +| ----- | --------- | -------------------------------------------------------- | +| SIN | $0 | Select SIN output (default) (Sine LFO only) | +| COS | $1 | Select COS output (Sine LFO only) | +| REG | $2 | Save the output of the LFO into an internal LFO register | +| COMPC | $4 | Complement the coefficient (1-coeff) | +| COMPA | $8 | Complement the address offset from the LFO | +| RPTR2 | $10 | Select the ramp+1/2 pointer (Ramp LFO only) | +| NA | $20 | Select x-fade coefficient and do not add address offset |""", + parameter_description=None, + operation="See description", + coding="00CCCCCC0NNAAAAAAAAAAAAAAAA10100", + example="""; A chorus +Delay MEM 4097 ; Chorus delay line +Amp EQU 8195 ; Amplitude for a 4097 sample delay line +Freq EQU 51 ; Apx. 2Hz at 32kHz sampling rate + +; Setup SIN LFO 0 + skp run,cont ; Skip if not first iteration + wldr 0,Freq,Amp ; Setup SIN LFO 0 + +cont: + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC * 1.0 + wra Delay,0 ; Write to delay line, clear ACC + cho rda,RMP0,COMPC|REG,Delay ; See application note AN-0001 + cho rda,RMP0,,Delay+1 ; for detailed examples and explanation + wra Temp,0 ; + cho rda,RMP0,COMPC|RPTR2,Delay ; + cho rda,RMP0,RPTR2,Delay+1 ; + cho sof,RMP0,NA|COMPC,0 ; + cho rda,RMP0,NA,Temp ; + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "CHO RDAL": InstructionDocumentation( + name="CHO RDAL", + args=[ + InstructionArg("N", 2, ["LFO select: SIN0,COS0,SIN1,COS1,RMP0,RMP1"]), + ], + description="""`CHO RDAL` will read the current value of the selected LFO into `ACC`.""", + parameter_description=None, + operation="LFO * 1 ­> ACC", + coding="110000100NN000000000000000010100", + example="""cho rdal,SIN0 ; Read LFO S0 into ACC +wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "CHO SOF": InstructionDocumentation( + name="CHO SOF", + args=[ + InstructionArg("N", 2, ["LFO select: SIN0,SIN1,RMP0,RMP1"]), + InstructionArg("C", 6, ["Binary", "Bit flags"]), + InstructionArg("D", 16, ["Real (S.15)", "Symbolic"]), + ], + description="""Like the `SOF` instruction, `CHO SOF` will multiply ACC by a coefficient and add the constant `D` to the result. However, in contrast to `SOF` the coefficient is not explicitly embedded within the instruction. Instead, based on the selected LFO and the 6 bit vector `C`, the coefficient is picked from a list of possible coefficients available within the LFO block of the FV­1. For an in depth explanation please consult the FV­-1 datasheet alongside with application note AN­0001. `CHO SOF` is a very flexible and powerful instruction, especially useful for the cross fading portion of pitch shift algorithms. + +Please see `CHO RDA` for a description of field flags.""", + parameter_description=None, + operation="See description", + coding="10CCCCCC0NNDDDDDDDDDDDDDDDD10100", + example="""; Pitch shift +Delay MEM 4096 ; Pitch shift delay line +Temp MEM 1 ; Temporary storage +Amp EQU 4096 ; RAMP LFO amplitude (4096 samples) +Freq EQU -8192 ; RAMP LFO frequency + +; Setup RAMP LFO 0 + skp run,cont ; Skip if not first iteration + wldr 0,Freq,Amp ; Setup RAMP LFO 0 + +cont: + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC * 1.0 + wra Delay,0 ; Write to delay line, clear ACC + cho rda,RMP0,COMPC|REG,Delay ; See application note AN-0001 + cho rda,RMP0,,Delay+1 ; for detailed examples and explanation + wra Temp,0 ; + cho rda,RMP0,COMPC|RPTR2,Delay ; + cho rda,RMP0,RPTR2,Delay+1 ; + cho sof,RMP0,NA|COMPC,0 ; + cho rda,RMP0,NA,Temp ; + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "CLR": InstructionDocumentation( + name="CLR", + args=[], + description="""`CLR` will clear the accumulator.""", + parameter_description=None, + operation="0 ­> ACC", + coding="00000000000000000000000000001110", + example=""" clr ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + ;----------------- + .... ; ...Left channel processing... + ;----------------- + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "EXP": InstructionDocumentation( + name="EXP", + args=[ + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($0000 - $FFFF)", "Symbolic"] + ), + InstructionArg("D", 11, ["Real (S.10)", "Symbolic"]), + ], + description="""`EXP` will multiply 2^`ACC` with `C` and add the constant `D` to the result. + +Since `ACC` (in it’s role as the destination for the `EXP` instruction) is limited to linear values from 0 to ++0.99999988, the `EXP` instruction is limited to logarithmic `ACC` values (in it’s role as the source operand +for the `EXP` instruction) from –16 to 0. Like the LOG instruction, `EXP` will treat the `ACC` content as a +S4.19 number. Positive logarithmic `ACC` values will be clipped to +0.99999988 which is the most positive +linear value that can be represented within the accumulator. + +`D` is intended to allow the linear `ACC` to be offset by a constant in the range from –1 to +0.9990234375""", + parameter_description=None, + operation="C * EXP(ACC) + D", + coding="CCCCCCCCCCCCCCCCDDDDDDDDDDD01100", + example="""exp 0.8,0 +""", + ), + "JAM": InstructionDocumentation( + name="JAM", + args=[ + InstructionArg("N", 1, ["RAMP LFO select: (0, 1)"]), + ], + description="""`JAM` will reset the selected RAMP LFO to its starting point.""", + parameter_description=None, + operation="0 ­> RAMP LFO N", + coding="0000000000000000000000001N010011", + example="""jam 0 ; Force ramp 0 LFO to it's starting position +""", + ), + "LDAX": InstructionDocumentation( + name="LDAX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + ], + description="""Loads the accumulator with the contents of the addressed register.""", + parameter_description=None, + operation="REG[ADDR]­> ACC", + coding="00000000000000000000000000000101", + example=""" ldax adcl ; ADC left input -> ACC +""", + ), + "LOG": InstructionDocumentation( + name="LOG", + args=[ + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($0000 - $FFFF)", "Symbolic"] + ), + InstructionArg("D", 11, ["Real (S4.6)", "Symbolic"]), + ], + description="""`LOG` will multiply the Base2 `LOG` of the current absolute value in `ACC` with `C` and add the constant `D` to the result. + +It is important to note that the `LOG` function returns a fixed point number in S4.19 format instead of the standard S.23 format, which in turn means that the most negative Base2 `LOG` value is -16. + +The `LOG` instruction can handle absolute linear accumulator values from 0.99999988 to 0.00001526 which translates to a dynamic range of apx. 96dB. + +`D` is an offset to be added to the logarithmic value in the range of -16 to + 15.999998.""", + parameter_description=None, + operation="C * LOG(|ACC|) + D", + coding="CCCCCCCCCCCCCCCCDDDDDDDDDDD01011", + example="""log 1.0,0 +""", + ), + "MAXX": InstructionDocumentation( + name="MAXX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""`MAXX` will compare the absolute value of `ACC` versus C times the absolute value of the register pointed to by `ADDR`. If the absolute value of `ACC` is larger `ACC` will be loaded with `|ACC|`, otherwise the accumulator becomes overwritten by `|REG[ADDR] * C|`.""", + parameter_description="""In order to simplify the MAXX syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="MAX( |REG[ADDR] * C| , |ACC| )", + coding="CCCCCCCCCCCCCCCC00000AAAAAA01001", + example="""; Peak follower +; +Peak EQU 32 ; Peak hold register + + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + maxx Peak,1.0 ; Keep larger absolute value in ACC + +; For a peak meter insert decay code here... + + wrax Peak,0.0 ; Save (new) peak and clear ACC +""", + ), + "MULX": InstructionDocumentation( + name="MULX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + ], + description="""`MULX` will multiply `ACC` by the value of the register pointed to by `ADDR`. An important application of the `MULX` instruction is squaring the content of `ACC`, which combined with a single order LP is especially useful in calculating the RMS value of an arbitrary waveform.""", + parameter_description="""In order to simplify the `MULX` syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="ACC * REG[ADDR]", + coding="000000000000000000000AAAAAA01010", + example="""; RMS conversion +Tmp_LP EQU 32 ; Temporary register for first order LP + + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + ; RMS calculation = ACC^2 -> first order LP + + mulx ADCL ; ACC^2 + rdfx Tmp_LP,x.x ; First order... + wrax Tmp_LP,1.0 ; ...LP filter + +; At this point ACC holds the RMS value of the input +""", + ), + "NOT": InstructionDocumentation( + name="NOT", + args=[], + description="""`NOT` will negate all bit positions within accumulator thus performing a 1’s complement.""", + parameter_description=None, + operation="/ACC ­> ACC", + coding="11111111111111111111111100010000", + example=""" not ; 1's comp ACC +""", + ), + "OR": InstructionDocumentation( + name="OR", + args=[ + InstructionArg("M", 24, ["Binary", "Hex ($000000 - $FFFFFF)", "Symbolic"]), + ], + description="""`OR` will perform a bit wise "or" of the current `ACC` and the 24-­bit MASK specified within the instruction word. The instruction might be used to load a constant into `ACC` provided `ACC` contains `$000000`.""", + parameter_description=None, + operation="ACC | MASK", + coding="MMMMMMMMMMMMMMMMMMMMMMMM000001111", + example="""0MASK EQU $0F0000 + +;---------------------------------------- + sof 0,0 ; Clear all bits within ACC + or $1 ; Set LSB + or %10000000_00000000_00000000 ; Set MSB + or 0MASK ; Set ACC[19..16] + and %S=[15..8] ; Set ACC[15..8] +""", + ), + "RDA": InstructionDocumentation( + name="RDA", + args=[ + InstructionArg( + "ADDR", + (1) + 15, + ["Decimal (0 - 32767)", "Hex ($0 - $7FFF)", "Symbolic"], + ), + InstructionArg( + "C", 11, ["Real (S1.9)", "Hex ($400 - $000 - $3FF)", "Symbolic"] + ), + ], + description="""RDA will fetch the sample [ADDR] from the delay RAM, multiply it by C, and add the result to the previous content of ACC. This multiply-accumulate operation is probably the most popular operation found in DSP algorithms.""", + parameter_description=None, + operation="SRAM[ADDR] * C + ACC", + coding="CCCCCCCCCCCAAAAAAAAAAAAAAAA00000", + example="""Delay MEM 1024 +Coeff EQU 1.55 +Tmp EQU $2000 + + rda 1000,1.9 + rda Delay+20,Coeff + rda Tmp,-2 + rda $7FFF,$7FF +""", + ), + "RDAX": InstructionDocumentation( + name="RDAX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""RDAX will fetch the value contained in `[ADDR]` from the register file, multiply it with `C` and add the result to the previous content of `ACC`. This multiply accumulate is probably the most popular operation found in DSP algorithms.""", + parameter_description="""In order to simplify the RDAX syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="C * REG[ADDR] + ACC", + coding="CCCCCCCCCCCCCCCC00000AAAAAA00100", + example="""; Crude mono +; + sof 0,0 ; Clear ACC + rdax ADCL,0.5 ; Get ADCL value and divide it by two + rdax ADCR,0.5 ; Get ADCR value, divide it by two + ; and add to the half of ADCL + wrax DACL,1.0 ; Result to DACL + wrax DACR,0 ; Result to DACR and clear ACC +""", + ), + "RDFX": InstructionDocumentation( + name="RDFX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""`RDFX` will subtract the value of the register pointed to by `ADDR` from `ACC`, multiply the result by `C` and then add the value of the register pointed to by `ADDR`. `RDFX` is an extremely powerful instruction in that it represents the major portion of a single order low pass filter.""", + parameter_description="""In order to simplify the `RDFX` syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="(ACC­REG[ADDR])*C + REG[ADDR]", + coding="CCCCCCCCCCCCCCCC00000AAAAAA00101", + example="""; Single order LP filter +Tmp_LP EQU 32 ; Temporary register for first order LP + + ldax ADCL ; Read left ADC + rdfx Tmp_LP,x.x ; First order... + wrax Tmp_LP,1.0 ; ...LP filter + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "RMPA": InstructionDocumentation( + name="RMPA", + args=[ + InstructionArg( + "C", 11, ["Real (S1.9)", "Hex ($400 - $000 - $3FF)", "Symbolic"] + ), + ], + description="""`RMPA` provides indirect delay line addressing in that the delay line address of the sample to be multiplied by `C` is not explicitly given in the instruction itself but contained within the pointer register `ADDR_PTR` (absolute address 24 within the internal register file.) + +`RMPA` will fetch the indirectly addressed sample from the delay ram, multiply it by `C` and add the result to the previous content of `ACC`.""", + parameter_description=None, + operation="SRAM[PNTR[N]] * C + ACC", + coding="CCCCCCCCCCC000000000001100000001", + example="""; Crude variable delay line addressing + sof 0,0 ; Clear ACC + rdax POT1,1.0 ; Read POT1 value + wrax ADDR_PTR,0 ; Write value to pointer register, clear ACC + rmpa 1.0 ; Read sample from delay line + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "SKP": InstructionDocumentation( + name="SKP", + args=[ + InstructionArg("CMASK", 5, ["Binary", "Hex ($00 - $1F)", "Symbolic"]), + InstructionArg("N", 6, ["Decimal (1 - 63)", "Label"]), + ], + description="""The SKP instruction allows conditional program execution. The FV-1 features five condition flags that can be used to conditionally skip the next N instructions. The selection of which condition flag(s) must be asserted in order to skip the next N instructions is made by the five-bit condition mask "CMASK". Only if all condition flags that correspond to a logic "1" within CMASK are asserted are the following N instructions skipped. The individual bits within CMASK correspond to the FV-1 condition flags as follows: + +| CMASK | Flag | Description | +| ----- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| b4 | RUN | The RUN flag is cleared after the program has executed for the first time after it was loaded into the internal program memory. The purpose of the RUN flag is to allow the program to initialize registers and LFOs during the first sample iteration then to skip those initializations from then on. | +| b3 | ZRC | The ZRC flag is asserted if the sign of ACC and PACC is different, a condition that indicates a Zero Crossing. | +| b2 | ZRO | Z is asserted if ACC = 0 | +| b1 | GEZ | GEZ is asserted if ACC >= 0 | +| b0 | NEG | N is asserted if ACC is negative |""", + parameter_description="""Maybe the most efficient way to define the condition mask is using its symbolic representation. In order to simplify the SKP syntax, SPINAsm has a predefined set of symbols which correspond to the name of the individual condition flags. (RUN, ZRC, ZRO, GEZ, NEG). Although most of the condition flags are mutually exclusive, SPINAsm allows you to specify more than one condition flag to become evaluated simply by separating multiple predefined symbols by the "|" character. Accordingly, "skp ZRC|N, 6" would skip the following six instructions in case of a zero crossing to a negative value.""", + operation="CMASK N", + coding="CCCCCNNNNNN000000000000000010001", + example="""; A bridge rectifier +; + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read from left ADC channel + skp GEZ,pos ; Skip next instruction if ACC >= 0 + sof -1.0,0 ; Make ACC positive +pos: + wrax DACL,0 ; Result to DACL, clear ACC + rdax ADCL,1.0 ; Read from left ADC channel + skp N,neg ; Skip next instruction if ACC < 0 + sof -1.0,0 ; Make ACC negative +neg: + wrax 0,DACR ; Result to DACR, clear +""", + ), + "SOF": InstructionDocumentation( + name="SOF", + args=[ + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($0000 - $FFFF)", "Symbolic"] + ), + InstructionArg("D", 11, ["Real (S.10)", "Symbolic"]), + ], + description="""SOF will multiply the current value in ACC with C and will then add the constant D to the result. + +Please note the absence of an integer entry format for D. This is not by mistake but it should emphasize that D is not intended to become used for integer arithmetic. The reason for this instruction is that the 11 bit constant D would be placed into ACC left justified or in other words 13 bits shifted to the left. D is intended to offset ACC by a constant in the range from -1 to +0.9990234375.""", + parameter_description=None, + operation="C * ACC + D", + coding="CCCCCCCCCCCCCCCCDDDDDDDDDDD01101", + example="""Off EQU 1.0 + +; Halve way rectifier­­­ + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read from left ADC channel + sof 1.0,­-Off ; Subtract offset + sof 1.0,Off ; Add offset +""", + ), + "WLDR": InstructionDocumentation( + name="WLDR", + args=[ + InstructionArg("N", 1, ["RAMP LFO select: (0, 1)"]), + InstructionArg( + "F", + 16, + ["Decimal (-16384 - 32768)", "Hex ($4000 - $000 - $7FFF)", "Symbolic"], + ), + InstructionArg("A", 2, ["Decimal (512, 1024, 2048, 4096)", "Symbolic"]), + ], + description="""`WLDR` will load frequency and amplitude control values into the selected RAMP LFO. (0 or 1) This instruction is intended to setup the selected RAMP LFO which is typically done within the first sample iteration after a new program became loaded. As a result `WLDR` will in most cases be used in combination with a `SKP RUN` instruction. For a more detailed description regarding the frequency and amplitude control values see application note AN­0001.""", + parameter_description=None, + operation="See description", + coding="01NFFFFFFFFFFFFFFFF000000AA10010", + example="""Amp EQU 4096 ; LFO will module a 4096 sample delay line +Freq EQU $100 ; +;------------------------ + +; Setup RAMP LFO 0 ; + skp run,start ; Skip next instruction if not first iteration + wldr 0,Freq,Amp ; Setup RAMP LFO 0 + +start: and 0,0 ; + .... ; + .... ; +""", + ), + "WLDS": InstructionDocumentation( + name="WLDS", + args=[ + InstructionArg("N", 1, ["SIN LFO select: (0, 1)"]), + InstructionArg( + "F", 9, ["Decimal (0 - 511)", "Hex ($000 - $1FF)", "Symbolic"] + ), + InstructionArg( + "A", 15, ["Decimal (0 - 32767)", "Hex ($0000 - $7FFF)", "Symbolic"] + ), + ], + description="""`WLDS` will load frequency and amplitude control values into the selected SIN LFO (0 or 1). This instruction is intended to setup the selected SIN LFO which is typically done within the first sample iteration after a new program is loaded. As a result `WLDS` will in most cases be used in combination with a `SKP RUN` instruction. For a more detailed description regarding the frequency and amplitude control values see application note AN­0001.""", + parameter_description=None, + operation="See description", + coding="00NFFFFFFFFFAAAAAAAAAAAAAAA10010", + example="""Amp EQU 8194 ; Amplitude for a 4097 sample delay line +Freq EQU 51 ; Apx. 2Hz at 32kHz sampling rate +;------------------------ + +; Setup SIN LFO 0 ; + skp run,start ; Skip next instruction if not first iteration + wlds 0,Freq,Amp ; Setup SIN LFO 0 + +start: sof 0,0 ; + .... ; + .... ; +""", + ), + "WRA": InstructionDocumentation( + name="WRA", + args=[ + InstructionArg( + "ADDR", + (1) + 15, + ["Decimal (0 - 32767)", "Hex ($0 - $7FFF)", "Symbolic"], + ), + InstructionArg( + "C", 11, ["Real (S1.9)", "Hex ($400 - $000 - $3FF)", "Symbolic"] + ), + ], + description="""`WRA` will store `ACC` to the delay ram location addressed by `ADDR` and then multiply `ACC` by `C`.""", + parameter_description=None, + operation="ACC­>SRAM[ADDR], ACC * C", + coding="CCCCCCCCCCCAAAAAAAAAAAAAAAA00010", + example="""Delay MEM 1024 +Coeff EQU 0.5 + + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + wra Delay,Coeff ; Write to start of delay line, halve ACC + rda Delay#,Coeff ; Add half of the sample from the end of the delay line + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "WRAP": InstructionDocumentation( + name="WRAP", + args=[ + InstructionArg( + "ADDR", + (1) + 15, + ["Decimal (0 - 32767)", "Hex ($0 - $7FFF)", "Symbolic"], + ), + InstructionArg( + "C", 11, ["Real (S1.9)", "Hex ($400 - $000 - $3FF)", "Symbolic"] + ), + ], + description="""`WRAP` will store `ACC` to the delay ram location addressed by ADDR then multiply `ACC` by `C` and finally add the content of the `LR` register to the product. Please note that the `LR` register contains the last sample value read from the delay ram memory. This instruction is typically used for all­pass filters in a reverb program.""", + parameter_description=None, + operation="ACC­>SRAM[ADDR], (ACC*C) + LR", + coding="CCCCCCCCCCCAAAAAAAAAAAAAAAA00011", + example=""" rda ap1#,kap ; Read output of all-pass 1 and multiply it by kap + wrap ap1,-kap ; Write ACC to input of all-pass 1 and do + ; ACC*(-kap)+ap1# (ap1# is in LR register) +""", + ), + "WRAX": InstructionDocumentation( + name="WRAX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""`WRAX` will save the current value in `ACC` to `[ADDR]` and then multiply `ACC` by `C`. This instruction can be used to write `ACC` to one DAC channel while clearing `ACC` for processing the next audio channel.""", + parameter_description="""In order to simplify the `WRAX` syntax, see the list of predefined symbols for all registers within the FV­1.""", + operation="ACC­>REG[ADDR], C * ACC", + coding="CCCCCCCCCCCCCCCC00000AAAAAA00110", + example="""; Stereo processing +; + rdax ADCL,1.0 ; Read left ADC into previously cleared ACC + ;--------------- + .... ; ...left channel processing + ;--------------- + wrax DACL,0 ; Result to DACL and clear ACC for right channel processing + rdax ADCR,1.0 ; Read right ADC into previously cleared ACC + ;--------------- + .... ; ...right channel processing + ;--------------- + wrax DACR,0 ; Result to DACR and clear ACC for left channel processing +""", + ), + "WRHX": InstructionDocumentation( + name="WRHX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""The current ACC value is stored in the register pointed to by ADDR, then ACC is multiplied by C. Finally, the previous content of ACC (PACC) is added to the product. WRHX is an extremely powerful instruction; when combined with RDFX, it forms a single order high pass shelving filter.""", + parameter_description="""In order to simplify the WRHX syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="ACC -> REG[ADDR], (ACC * C) + PACC", + coding="CCCCCCCCCCCCCCCC00000AAAAAA00111", + example="""; Single order HP shelving filter +Tmp_HP EQU 32 ; Temporary register for first order HP + +;---------------------------------------- + + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + rdfx Tmp_HP,x.x ; First order HP... + wrhx Tmp_HP,y.y ; ...shelving filter + wrax DACL,0 ; Result to DACL and clear ACC +""", + ), + "WRLX": InstructionDocumentation( + name="WRLX", + args=[ + InstructionArg( + "ADDR", 6, ["Decimal (0 - 63)", "Hex ($0 - $3F)", "Symbolic"] + ), + InstructionArg( + "C", 16, ["Real (S1.14)", "Hex ($8000 - $0000 - $7FFF)", "Symbolic"] + ), + ], + description="""First, the current ACC value is stored into the register pointed to by ADDR, then ACC is subtracted from the previous content of ACC (PACC). The difference is then multiplied by C and finally PACC is added to the result. WRLX is an extremely powerful instruction in that when combined with RDFX, it forms a single order low-pass shelving filter.""", + parameter_description="""In order to simplify the WRLX syntax, see the list of predefined symbols for all registers within the FV-1 register file.""", + operation="ACC -> REG[ADDR], (PACC - ACC) * C + PACC", + coding="CCCCCCCCCCCCCCCC00000AAAAAA01000", + example="""; Single order LP shelving filter +Tmp_LP EQU 32 ; Temporary register for first order LP + +;---------------------------------------- + + sof 0,0 ; Clear ACC + rdax ADCL,1.0 ; Read left ADC + rdfx Tmp_LP,x.x ; First order LP... + wrlx Tmp_LP,y.y ; ...shelving filter + wrax DACL,1.0 ; Result to DACL and clear ACC +""", + ), + "XOR": InstructionDocumentation( + name="XOR", + args=[ + InstructionArg("M", 24, ["Binary", "Hex ($000000 - $FFFFFF)", "Symbolic"]), + ], + description="""`XOR` will perform a bit wise "xor" of the current `ACC` and the 24­-bit MASK specified within the instruction word. The instruction will invert `ACC` provided `MASK` equals `$FFFFFF`. (see also the pseudo opcode section).""", + parameter_description=None, + operation="ACC ^ MASK", + coding="MMMMMMMMMMMMMMMMMMMMMMMM000010000", + example="""XMASK EQU $AAAAAA + +;---------------------------------------- + sof 0,0 ; Clear all bits within ACC + xor $0 ; Set all ACC bits + xor %01010101_01010101_01010101 ; Invert all even numbered bits + xor XMASK ; Invert all odd numbered bits +""", + ), +} diff --git a/src/spinasm_lsp/docs/instructions/absa.md b/src/spinasm_lsp/docs/instructions/absa.md deleted file mode 100644 index 767c51a..0000000 --- a/src/spinasm_lsp/docs/instructions/absa.md +++ /dev/null @@ -1,23 +0,0 @@ -## `ABSA` - ------------------- - -Loads the accumulator with the absolute value of the accumulator. - -### Operation -`|ACC| ­> ACC` - -### Parameters - -None. - -### Instruction Coding -**00000000000000000000000000001001** - -### Example -```assembly - absa ; Absolute value of ACC -> ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/and.md b/src/spinasm_lsp/docs/instructions/and.md deleted file mode 100644 index 5e63a04..0000000 --- a/src/spinasm_lsp/docs/instructions/and.md +++ /dev/null @@ -1,31 +0,0 @@ -## `AND M` - ------------------- - -AND will perform a bit wise "and" of the current ACC and the 24-­bit MASK specified within the instruction word. The instruction might be used to load a constant into ACC provided ACC contains $FFFFFF or to clear ACC if MASK equals $000000. (see also the pseudo opcode section) - -### Operation -`ACC & MASK` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| M | 24 Bit| Binary
Hex ($000000 - $FFFFFF)
Symbolic | - -### Instruction Coding -**MMMMMMMMMMMMMMMMMMMMMMMM000001110** - -### Example -```assembly -AMASK EQU $F0FFFF - -;---------------------------------------- - or ­­­­­­­­­­­­­­­­­­$FFFFFF ; Set all bits within ACC - and $FFFFFE ; Clear LSB - and %01111111_11111111_11111111 ; Clear MSB - and AMASK ; Clear ACC[19..16] - and $0 ; Clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/cho rda.md b/src/spinasm_lsp/docs/instructions/cho rda.md deleted file mode 100644 index c2aa195..0000000 --- a/src/spinasm_lsp/docs/instructions/cho rda.md +++ /dev/null @@ -1,66 +0,0 @@ -## `CHO RDA N, C, D` - ------------------- - -Like the `RDA` instruction, `CHO RDA` will read a sample from the delay ram, multiply it by a coefficient and add the product to the previous content of ACC. However, in contrast to `RDA` the coefficient is not explicitly embedded within the instruction and the effective delay ram address is not solely determined by the address parameter. Instead, both values are modulated by the selected LFO at run time, for an in depth explanation please consult the FV­1 datasheet alongside with application note AN­0001. `CHO RDA` is a very flexible and powerful instruction, especially useful for delay line modulation effects such as chorus or pitch shifting. - -The coefficient field of the `CHO` instructions are used as control bits to select various aspects of the LFO. These bits can be set using predefined flags that are `OR`ed together to create the required bit field. For a sine wave LFO (SIN0 or SIN1), valid flags are: - -`SIN COS REG COMPC COMPA` - -While for a ramp LFO (RMP0 and RMP1), valid flags are: - -`REG COMPC COMPA RPTR2 NA` - -These flags are defined as: - -| Flag | HEX value | Description | -| --- | --- | --- | -| SIN | $0 | Select SIN output (default) (Sine LFO only) | -| COS | $1 | Select COS output (Sine LFO only) | -| REG | $2 | Save the output of the LFO into an internal LFO register | -| COMPC | $4 | Complement the coefficient (1-coeff) | -| COMPA | $8 | Complement the address offset from the LFO | -| RPTR2 | $10 | Select the ramp+1/2 pointer (Ramp LFO only) | -| NA | $20 | Select x-fade coefficient and do not add address offset | - -### Operation -`See description` - -### Parameters -| Name | Width | Entry formats, range | -|---|---|---| -| N | 2 Bit | LFO select: SIN0,SIN1,RMP0,RMP1| -| C | 6 Bit | Binary
Bit flags | -| D | 16 Bit | Real (S.15)
Symbolic | - -### Instruction Coding -**00CCCCCC0NNAAAAAAAAAAAAAAAA10100** - -### Example -```assembly -; A chorus -Delay MEM 4097 ; Chorus delay line -Amp EQU 8195 ; Amplitude for a 4097 sample delay line -Freq EQU 51 ; Apx. 2Hz at 32kHz sampling rate - -; Setup SIN LFO 0 - skp run,cont ; Skip if not first iteration - wldr 0,Freq,Amp ; Setup SIN LFO 0 - -cont: - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC * 1.0 - wra Delay,0 ; Write to delay line, clear ACC - cho rda,RMP0,COMPC|REG,Delay ; See application note AN-0001 - cho rda,RMP0,,Delay+1 ; for detailed examples and explanation - wra Temp,0 ; - cho rda,RMP0,COMPC|RPTR2,Delay ; - cho rda,RMP0,RPTR2,Delay+1 ; - cho sof,RMP0,NA|COMPC,0 ; - cho rda,RMP0,NA,Temp ; - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/cho rdal.md b/src/spinasm_lsp/docs/instructions/cho rdal.md deleted file mode 100644 index e9a7113..0000000 --- a/src/spinasm_lsp/docs/instructions/cho rdal.md +++ /dev/null @@ -1,25 +0,0 @@ -## `CHO RDAL, N` - ------------------- - -`CHO RDAL` will read the current value of the selected LFO into `ACC`. - -### Operation -`LFO * 1 ­> ACC` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| N | 2 Bit | LFO select: SIN0,COS0,SIN1,COS1,RMP0,RMP1| - -### Instruction Coding -**110000100NN000000000000000010100** - -### Example -```assembly -cho rdal,SIN0 ; Read LFO S0 into ACC -wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/cho sof.md b/src/spinasm_lsp/docs/instructions/cho sof.md deleted file mode 100644 index 2b6321e..0000000 --- a/src/spinasm_lsp/docs/instructions/cho sof.md +++ /dev/null @@ -1,49 +0,0 @@ -## `CHO SOF N, C, D` - ------------------- - -Like the `SOF` instruction, `CHO SOF` will multiply ACC by a coefficient and add the constant `D` to the result. However, in contrast to `SOF` the coefficient is not explicitly embedded within the instruction. Instead, based on the selected LFO and the 6 bit vector `C`, the coefficient is picked from a list of possible coefficients available within the LFO block of the FV­1. For an in depth explanation please consult the FV­-1 datasheet alongside with application note AN­0001. `CHO SOF` is a very flexible and powerful instruction, especially useful for the cross fading portion of pitch shift algorithms. - -Please see `CHO RDA` for a description of field flags. - -### Operation -`See description` - -### Parameters -| Name | Width | Entry formats, range | -|---|---|---| -| N | 2 Bit | LFO select: SIN0,SIN1,RMP0,RMP1| -| C | 6 Bit | Binary
Bit flags | -| D | 16 Bit | Real (S.15)
Symbolic | - -### Instruction Coding -**10CCCCCC0NNDDDDDDDDDDDDDDDD10100** - -### Example -```assembly -; Pitch shift -Delay MEM 4096 ; Pitch shift delay line -Temp MEM 1 ; Temporary storage -Amp EQU 4096 ; RAMP LFO amplitude (4096 samples) -Freq EQU -8192 ; RAMP LFO frequency - -; Setup RAMP LFO 0 - skp run,cont ; Skip if not first iteration - wldr 0,Freq,Amp ; Setup RAMP LFO 0 - -cont: - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC * 1.0 - wra Delay,0 ; Write to delay line, clear ACC - cho rda,RMP0,COMPC|REG,Delay ; See application note AN-0001 - cho rda,RMP0,,Delay+1 ; for detailed examples and explanation - wra Temp,0 ; - cho rda,RMP0,COMPC|RPTR2,Delay ; - cho rda,RMP0,RPTR2,Delay+1 ; - cho sof,RMP0,NA|COMPC,0 ; - cho rda,RMP0,NA,Temp ; - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/clr.md b/src/spinasm_lsp/docs/instructions/clr.md deleted file mode 100644 index 0066af9..0000000 --- a/src/spinasm_lsp/docs/instructions/clr.md +++ /dev/null @@ -1,28 +0,0 @@ -## `CLR` - ------------------- - -`CLR` will clear the accumulator. - -### Operation -`0 ­> ACC` - -### Parameters - -None. - -### Instruction Coding -**00000000000000000000000000001110** - -### Example -```assembly - clr ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - ;----------------- - .... ; ...Left channel processing... - ;----------------- - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/exp.md b/src/spinasm_lsp/docs/instructions/exp.md deleted file mode 100644 index fdcbc96..0000000 --- a/src/spinasm_lsp/docs/instructions/exp.md +++ /dev/null @@ -1,33 +0,0 @@ -## `EXP C, D` - ------------------- - -`EXP` will multiply 2^`ACC` with `C` and add the constant `D` to the result. - -Since `ACC` (in it’s role as the destination for the `EXP` instruction) is limited to linear values from 0 to -+0.99999988, the `EXP` instruction is limited to logarithmic `ACC` values (in it’s role as the source operand -for the `EXP` instruction) from –16 to 0. Like the LOG instruction, `EXP` will treat the `ACC` content as a -S4.19 number. Positive logarithmic `ACC` values will be clipped to +0.99999988 which is the most positive -linear value that can be represented within the accumulator. - -`D` is intended to allow the linear `ACC` to be offset by a constant in the range from –1 to +0.9990234375 - -### Operation -`C * EXP(ACC) + D` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| C | 16 Bit| Real (S1.14)
Hex ($0000 - $FFFF)
Symbolic | -| D | 11 Bit| Real (S.10)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCCCCCCDDDDDDDDDDD01100** - -### Example -```assembly -exp 0.8,0 -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/jam.md b/src/spinasm_lsp/docs/instructions/jam.md deleted file mode 100644 index e9a4918..0000000 --- a/src/spinasm_lsp/docs/instructions/jam.md +++ /dev/null @@ -1,24 +0,0 @@ -## `JAM N` - ------------------- - -`JAM` will reset the selected RAMP LFO to its starting point. - -### Operation -`0 ­> RAMP LFO N` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| N | 1 Bit | RAMP LFO select: (0, 1) | - -### Instruction Coding -**0000000000000000000000001N010011** - -### Example -```assembly -jam 0 ; Force ramp 0 LFO to it's starting position -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/ldax.md b/src/spinasm_lsp/docs/instructions/ldax.md deleted file mode 100644 index c2d8b48..0000000 --- a/src/spinasm_lsp/docs/instructions/ldax.md +++ /dev/null @@ -1,24 +0,0 @@ -## `LDAX ADDR` - ------------------- - -Loads the accumulator with the contents of the addressed register. - -### Operation -`REG[ADDR]­> ACC` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | - -### Instruction Coding -**00000000000000000000000000000101** - -### Example -```assembly - ldax adcl ; ADC left input -> ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/log.md b/src/spinasm_lsp/docs/instructions/log.md deleted file mode 100644 index db8da44..0000000 --- a/src/spinasm_lsp/docs/instructions/log.md +++ /dev/null @@ -1,32 +0,0 @@ -## `LOG C, D` - ------------------- - -`LOG` will multiply the Base2 `LOG` of the current absolute value in `ACC` with `C` and add the constant `D` to -the result. - -It is important to note that the `LOG` function returns a fixed point number in S4.19 format instead of the standard S.23 format, which in turn means that the most negative Base2 `LOG` value is -16. - -The `LOG` instruction can handle absolute linear accumulator values from 0.99999988 to 0.00001526 which translates to a dynamic range of apx. 96dB. - -`D` is an offset to be added to the logarithmic value in the range of -16 to + 15.999998. - -### Operation -`C * LOG(|ACC|) + D` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| C | 16 Bit| Real (S1.14)
Hex ($0000 - $FFFF)
Symbolic | -| D | 11 Bit| Real (S4.6)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCCCCCCDDDDDDDDDDD01011** - -### Example -```assembly -log 1.0,0 -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/maxx.md b/src/spinasm_lsp/docs/instructions/maxx.md deleted file mode 100644 index e975ce3..0000000 --- a/src/spinasm_lsp/docs/instructions/maxx.md +++ /dev/null @@ -1,37 +0,0 @@ -## `MAXX ADDR, C` - ------------------- - -`MAXX` will compare the absolute value of `ACC` versus C times the absolute value of the register pointed to by `ADDR`. If the absolute value of `ACC` is larger `ACC` will be loaded with `|ACC|`, otherwise the accumulator becomes overwritten by `|REG[ADDR] * C|`. - -### Operation -`MAX( |REG[ADDR] * C| , |ACC| )` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit| Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the MAXX syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA01001** - -### Example -```assembly -; Peak follower -; -Peak EQU 32 ; Peak hold register - - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - maxx Peak,1.0 ; Keep larger absolute value in ACC - -; For a peak meter insert decay code here... - - wrax Peak,0.0 ; Save (new) peak and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/mulx.md b/src/spinasm_lsp/docs/instructions/mulx.md deleted file mode 100644 index f1da305..0000000 --- a/src/spinasm_lsp/docs/instructions/mulx.md +++ /dev/null @@ -1,37 +0,0 @@ -## `MULX ADDR` - ------------------- - -`MULX` will multiply `ACC` by the value of the register pointed to by `ADDR`. An important application of the `MULX` instruction is squaring the content of `ACC`, which combined with a single order LP is especially useful in calculating the RMS value of an arbitrary waveform. - -### Operation -`ACC * REG[ADDR]` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | - -In order to simplify the `MULX` syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**000000000000000000000AAAAAA01010** - -### Example -```assembly -; RMS conversion -Tmp_LP EQU 32 ; Temporary register for first order LP - - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - ; RMS calculation = ACC^2 -> first order LP - - mulx ADCL ; ACC^2 - rdfx Tmp_LP,x.x ; First order... - wrax Tmp_LP,1.0 ; ...LP filter - -; At this point ACC holds the RMS value of the input -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/not.md b/src/spinasm_lsp/docs/instructions/not.md deleted file mode 100644 index 34e1555..0000000 --- a/src/spinasm_lsp/docs/instructions/not.md +++ /dev/null @@ -1,23 +0,0 @@ -## `NOT` - ------------------- - -`NOT` will negate all bit positions within accumulator thus performing a 1’s complement. - -### Operation -`/ACC ­> ACC` - -### Parameters - -None. - -### Instruction Coding -**11111111111111111111111100010000** - -### Example -```assembly - not ; 1's comp ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/or.md b/src/spinasm_lsp/docs/instructions/or.md deleted file mode 100644 index d2612f0..0000000 --- a/src/spinasm_lsp/docs/instructions/or.md +++ /dev/null @@ -1,31 +0,0 @@ -## `OR M` - ------------------- - -`OR` will perform a bit wise "or" of the current `ACC` and the 24-­bit MASK specified within the instruction word. The instruction might be used to load a constant into `ACC` provided `ACC` contains `$000000`. - -### Operation -`ACC | MASK` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| M | 24 Bit| Binary
Hex ($000000 - $FFFFFF)
Symbolic | - -### Instruction Coding -**MMMMMMMMMMMMMMMMMMMMMMMM000001111** - -### Example -```assembly -0MASK EQU $0F0000 - -;---------------------------------------- - sof 0,0 ; Clear all bits within ACC - or $1 ; Set LSB - or %10000000_00000000_00000000 ; Set MSB - or 0MASK ; Set ACC[19..16] - and %S=[15..8] ; Set ACC[15..8] -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/rda.md b/src/spinasm_lsp/docs/instructions/rda.md deleted file mode 100644 index dc39701..0000000 --- a/src/spinasm_lsp/docs/instructions/rda.md +++ /dev/null @@ -1,33 +0,0 @@ -## `RDA ADDR, C` - ------------------- - -RDA will fetch the sample [ADDR] from the delay RAM, multiply it by C, and add the result to the previous content of ACC. This multiply-accumulate operation is probably the most popular operation found in DSP algorithms. - -### Operation -`SRAM[ADDR] * C + ACC` - -### Parameters - -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | (1)+15 Bit| Decimal (0 - 32767)
Hex ($0 - $7FFF)
Symbolic | -| C | 11 Bit | Real (S1.9)
Hex ($400 - $000 - $3FF)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCAAAAAAAAAAAAAAAA00000** - -### Example -```assembly -Delay MEM 1024 -Coeff EQU 1.55 -Tmp EQU $2000 - - rda 1000,1.9 - rda Delay+20,Coeff - rda Tmp,-2 - rda $7FFF,$7FF -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/rdax.md b/src/spinasm_lsp/docs/instructions/rdax.md deleted file mode 100644 index a1ff8d5..0000000 --- a/src/spinasm_lsp/docs/instructions/rdax.md +++ /dev/null @@ -1,34 +0,0 @@ -## `RDAX ADDR, C` - ------------------- - -RDAX will fetch the value contained in `[ADDR]` from the register file, multiply it with `C` and add the result to the previous content of `ACC`. This multiply accumulate is probably the most popular operation found in DSP algorithms. - -### Operation -`C * REG[ADDR] + ACC` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit| Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the RDAX syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA00100** - -### Example -```assembly -; Crude mono -; - sof 0,0 ; Clear ACC - rdax ADCL,0.5 ; Get ADCL value and divide it by two - rdax ADCR,0.5 ; Get ADCR value, divide it by two - ; and add to the half of ADCL - wrax DACL,1.0 ; Result to DACL - wrax DACR,0 ; Result to DACR and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/rdfx.md b/src/spinasm_lsp/docs/instructions/rdfx.md deleted file mode 100644 index 10906c1..0000000 --- a/src/spinasm_lsp/docs/instructions/rdfx.md +++ /dev/null @@ -1,33 +0,0 @@ -## `RDFX ADDR, C` - ------------------- - -`RDFX` will subtract the value of the register pointed to by `ADDR` from `ACC`, multiply the result by `C` and then add the value of the register pointed to by `ADDR`. `RDFX` is an extremely powerful instruction in that it represents the major portion of a single order low pass filter. - -### Operation -`(ACC­REG[ADDR])*C + REG[ADDR]` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit| Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the `RDFX` syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA00101** - -### Example -```assembly -; Single order LP filter -Tmp_LP EQU 32 ; Temporary register for first order LP - - ldax ADCL ; Read left ADC - rdfx Tmp_LP,x.x ; First order... - wrax Tmp_LP,1.0 ; ...LP filter - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/rmpa.md b/src/spinasm_lsp/docs/instructions/rmpa.md deleted file mode 100644 index 20bd728..0000000 --- a/src/spinasm_lsp/docs/instructions/rmpa.md +++ /dev/null @@ -1,32 +0,0 @@ -## `RMPA C` - ------------------- - -`RMPA` provides indirect delay line addressing in that the delay line address of the sample to be multiplied by `C` is not explicitly given in the instruction itself but contained within the pointer register `ADDR_PTR` (absolute address 24 within the internal register file.) - -`RMPA` will fetch the indirectly addressed sample from the delay ram, multiply it by `C` and add the result to the previous content of `ACC`. - -### Operation -`SRAM[PNTR[N]] * C + ACC` - -### Parameters - -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| C | 11 Bit | Real (S1.9)
Hex ($400 - $000 - $3FF)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCC000000000001100000001** - -### Example -```assembly -; Crude variable delay line addressing - sof 0,0 ; Clear ACC - rdax POT1,1.0 ; Read POT1 value - wrax ADDR_PTR,0 ; Write value to pointer register, clear ACC - rmpa 1.0 ; Read sample from delay line - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/skp.md b/src/spinasm_lsp/docs/instructions/skp.md deleted file mode 100644 index 10461ab..0000000 --- a/src/spinasm_lsp/docs/instructions/skp.md +++ /dev/null @@ -1,47 +0,0 @@ -## `SKP CMASK,N` - ------------------- - -The SKP instruction allows conditional program execution. The FV-1 features five condition flags that can be used to conditionally skip the next N instructions. The selection of which condition flag(s) must be asserted in order to skip the next N instructions is made by the five-bit condition mask "CMASK". Only if all condition flags that correspond to a logic "1" within CMASK are asserted are the following N instructions skipped. The individual bits within CMASK correspond to the FV-1 condition flags as follows: - -| CMASK | Flag | Description | -| --- | --- | --- | -| b4 | RUN | The RUN flag is cleared after the program has executed for the first time after it was loaded into the internal program memory. The purpose of the RUN flag is to allow the program to initialize registers and LFOs during the first sample iteration then to skip those initializations from then on. | -| b3 | ZRC | The ZRC flag is asserted if the sign of ACC and PACC is different, a condition that indicates a Zero Crossing. | -| b2 | ZRO | Z is asserted if ACC = 0 | -| b1 | GEZ | GEZ is asserted if ACC >= 0 | -| b0 | NEG | N is asserted if ACC is negative | - -### Operation -`CMASK N` - -### Parameters -| Name | Width | Entry formats, range | -| --- | --- | --- | -| CMASK | 5 Bit | Binary
Hex ($00 - $1F)
Symbolic | -| N | 6 Bit | Decimal (1 - 63)
Label | - -Maybe the most efficient way to define the condition mask is using its symbolic representation. In order to simplify the SKP syntax, SPINAsm has a predefined set of symbols which correspond to the name of the individual condition flags. (RUN, ZRC, ZRO, GEZ, NEG). Although most of the condition flags are mutually exclusive, SPINAsm allows you to specify more than one condition flag to become evaluated simply by separating multiple predefined symbols by the "|" character. Accordingly, "skp ZRC|N, 6" would skip the following six instructions in case of a zero crossing to a negative value. - -### Instruction Coding -**CCCCCNNNNNN000000000000000010001** - -### Example -```assembly -; A bridge rectifier -; - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read from left ADC channel - skp GEZ,pos ; Skip next instruction if ACC >= 0 - sof -1.0,0 ; Make ACC positive -pos: - wrax DACL,0 ; Result to DACL, clear ACC - rdax ADCL,1.0 ; Read from left ADC channel - skp N,neg ; Skip next instruction if ACC < 0 - sof -1.0,0 ; Make ACC negative -neg: - wrax 0,DACR ; Result to DACR, clear -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/sof.md b/src/spinasm_lsp/docs/instructions/sof.md deleted file mode 100644 index 67ec6d2..0000000 --- a/src/spinasm_lsp/docs/instructions/sof.md +++ /dev/null @@ -1,33 +0,0 @@ -## `SOF C, D` - ------------------- - -SOF will multiply the current value in ACC with C and will then add the constant D to the result. - -Please note the absence of an integer entry format for D. This is not by mistake but it should emphasize that D is not intended to become used for integer arithmetic. The reason for this instruction is that the 11 bit constant D would be placed into ACC left justified or in other words 13 bits shifted to the left. D is intended to offset ACC by a constant in the range from -1 to +0.9990234375. - -### Operation -`C * ACC + D` - -### Parameters -| Name | Width | Entry formats, range | -|---|---|---| -| C | 16 Bit | Real (S1.14)
Hex ($0000 - $FFFF)
Symbolic | -| D | 11 Bit | Real (S.10)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCCCCCCDDDDDDDDDDD01101** - -### Example -```assembly -Off EQU 1.0 - -; Halve way rectifier­­­ - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read from left ADC channel - sof 1.0,­-Off ; Subtract offset - sof 1.0,Off ; Add offset -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/template.txt b/src/spinasm_lsp/docs/instructions/template.txt deleted file mode 100644 index 38a8166..0000000 --- a/src/spinasm_lsp/docs/instructions/template.txt +++ /dev/null @@ -1,35 +0,0 @@ -## `INSTRUCTION SYNTAX` - ------------------- - -Instruction description. - -### Operation -`Operation` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0-63)
Hex ($0-$3F)
Symbolic | -| C | 16 Bit| Real (S1.14)
Hex ($8000-$0000-$7FFF)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA00100** - -### Example -```assembly -; Crude mono -; - -;---------------------------------------- - - sof 0,0 ; Clear ACC - rdax ADCL,0.5 ; Get ADCL value and divide it by two - rdax ADCR,0.5 ; Get ADCR value, divide it by two - ; and add to the half of ADCL - wrax DACL,1.0 ; Result to DACL - wrax DACR,0 ; Result to DACR and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wldr.md b/src/spinasm_lsp/docs/instructions/wldr.md deleted file mode 100644 index 83e89e6..0000000 --- a/src/spinasm_lsp/docs/instructions/wldr.md +++ /dev/null @@ -1,37 +0,0 @@ -## `WLDR N, F, A` - ------------------- - -`WLDR` will load frequency and amplitude control values into the selected RAMP LFO. (0 or 1) This instruction is intended to setup the selected RAMP LFO which is typically done within the first sample iteration after a new program became loaded. As a result `WLDR` will in most cases be used in combination with a `SKP RUN` instruction. For a more detailed description regarding the frequency and amplitude control values see application note AN­0001. - -### Operation -`See description` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| N | 1 Bit | RAMP LFO select: (0, 1) | -| F | 16 Bit | Decimal (-16384 - 32768)
Hex ($4000 - $000 - $7FFF)
Symbolic | -| A | 2 Bit| Decimal (512, 1024, 2048, 4096)
Symbolic | - - -### Instruction Coding -**01NFFFFFFFFFFFFFFFF000000AA10010** - -### Example -```assembly -Amp EQU 4096 ; LFO will module a 4096 sample delay line -Freq EQU $100 ; -;------------------------ - -; Setup RAMP LFO 0 ; - skp run,start ; Skip next instruction if not first iteration - wldr 0,Freq,Amp ; Setup RAMP LFO 0 - -start: and 0,0 ; - .... ; - .... ; -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wlds.md b/src/spinasm_lsp/docs/instructions/wlds.md deleted file mode 100644 index aa5873b..0000000 --- a/src/spinasm_lsp/docs/instructions/wlds.md +++ /dev/null @@ -1,37 +0,0 @@ -## `WLDS N, F, A` - ------------------- - -`WLDS` will load frequency and amplitude control values into the selected SIN LFO (0 or 1). This instruction is intended to setup the selected SIN LFO which is typically done within the first sample iteration after a new program is loaded. As a result `WLDS` will in most cases be used in combination with a `SKP RUN` instruction. For a more detailed description regarding the frequency and amplitude control values see application note AN­0001. - -### Operation -`See description` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| N | 1 Bit | SIN LFO select: (0, 1) | -| F | 9 Bit | Decimal (0 - 511)
Hex ($000 - $1FF)
Symbolic | -| A | 15 Bit| Decimal (0 - 32767)
Hex ($0000 - $7FFF)
Symbolic | - - -### Instruction Coding -**00NFFFFFFFFFAAAAAAAAAAAAAAA10010** - -### Example -```assembly -Amp EQU 8194 ; Amplitude for a 4097 sample delay line -Freq EQU 51 ; Apx. 2Hz at 32kHz sampling rate -;------------------------ - -; Setup SIN LFO 0 ; - skp run,start ; Skip next instruction if not first iteration - wlds 0,Freq,Amp ; Setup SIN LFO 0 - -start: sof 0,0 ; - .... ; - .... ; -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wra.md b/src/spinasm_lsp/docs/instructions/wra.md deleted file mode 100644 index b017bbf..0000000 --- a/src/spinasm_lsp/docs/instructions/wra.md +++ /dev/null @@ -1,33 +0,0 @@ -## `WRA ADDR, C` - ------------------- - -`WRA` will store `ACC` to the delay ram location addressed by `ADDR` and then multiply `ACC` by `C`. - -### Operation -`ACC­>SRAM[ADDR], ACC * C` - -### Parameters - -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | (1)+15 Bit| Decimal (0 - 32767)
Hex ($0 - $7FFF)
Symbolic | -| C | 11 Bit | Real (S1.9)
Hex ($400 - $000 - $3FF)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCAAAAAAAAAAAAAAAA00010** - -### Example -```assembly -Delay MEM 1024 -Coeff EQU 0.5 - - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - wra Delay,Coeff ; Write to start of delay line, halve ACC - rda Delay#,Coeff ; Add half of the sample from the end of the delay line - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wrap.md b/src/spinasm_lsp/docs/instructions/wrap.md deleted file mode 100644 index d86eb1f..0000000 --- a/src/spinasm_lsp/docs/instructions/wrap.md +++ /dev/null @@ -1,27 +0,0 @@ -## `WRAP ADDR, C` - ------------------- - -`WRAP` will store `ACC` to the delay ram location addressed by ADDR then multiply `ACC` by `C` and finally add the content of the `LR` register to the product. Please note that the `LR` register contains the last sample value read from the delay ram memory. This instruction is typically used for all­pass filters in a reverb program. - -### Operation -`ACC­>SRAM[ADDR], (ACC*C) + LR` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | (1)+15 Bit| Decimal (0 - 32767)
Hex ($0 - $7FFF)
Symbolic | -| C | 11 Bit | Real (S1.9)
Hex ($400 - $000 - $3FF)
Symbolic | - -### Instruction Coding -**CCCCCCCCCCCAAAAAAAAAAAAAAAA00011** - -### Example -```assembly - rda ap1#,kap ; Read output of all-pass 1 and multiply it by kap - wrap ap1,-kap ; Write ACC to input of all-pass 1 and do - ; ACC*(-kap)+ap1# (ap1# is in LR register) -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wrax.md b/src/spinasm_lsp/docs/instructions/wrax.md deleted file mode 100644 index c2a78b8..0000000 --- a/src/spinasm_lsp/docs/instructions/wrax.md +++ /dev/null @@ -1,38 +0,0 @@ -## `WRAX ADDR, C` - ------------------- - -`WRAX` will save the current value in `ACC` to `[ADDR]` and then multiply `ACC` by `C`. This instruction can be used to write `ACC` to one DAC channel while clearing `ACC` for processing the next audio channel. - -### Operation -`ACC­>REG[ADDR], C * ACC` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit| Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the `WRAX` syntax, see the list of predefined symbols for all registers within the FV­1. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA00110** - -### Example -```assembly -; Stereo processing -; - rdax ADCL,1.0 ; Read left ADC into previously cleared ACC - ;--------------- - .... ; ...left channel processing - ;--------------- - wrax DACL,0 ; Result to DACL and clear ACC for right channel processing - rdax ADCR,1.0 ; Read right ADC into previously cleared ACC - ;--------------- - .... ; ...right channel processing - ;--------------- - wrax DACR,0 ; Result to DACR and clear ACC for left channel processing -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wrhx.md b/src/spinasm_lsp/docs/instructions/wrhx.md deleted file mode 100644 index 3c1436c..0000000 --- a/src/spinasm_lsp/docs/instructions/wrhx.md +++ /dev/null @@ -1,36 +0,0 @@ -## `WRHX ADDR, C` - ------------------- - -The current ACC value is stored in the register pointed to by ADDR, then ACC is multiplied by C. Finally, the previous content of ACC (PACC) is added to the product. WRHX is an extremely powerful instruction; when combined with RDFX, it forms a single order high pass shelving filter. - -### Operation -`ACC -> REG[ADDR], (ACC * C) + PACC` - -### Parameters -| Name | Width | Entry formats, range | -|-------|--------|--------------------------------------------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit | Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the WRHX syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA00111** - -### Example -```assembly -; Single order HP shelving filter -Tmp_HP EQU 32 ; Temporary register for first order HP - -;---------------------------------------- - - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - rdfx Tmp_HP,x.x ; First order HP... - wrhx Tmp_HP,y.y ; ...shelving filter - wrax DACL,0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/wrlx.md b/src/spinasm_lsp/docs/instructions/wrlx.md deleted file mode 100644 index dc9ed09..0000000 --- a/src/spinasm_lsp/docs/instructions/wrlx.md +++ /dev/null @@ -1,37 +0,0 @@ -## `WRLX ADDR, C` - ------------------- - -First, the current ACC value is stored into the register pointed to by ADDR, then ACC is subtracted from the previous content of ACC (PACC). The difference is then multiplied by C and finally PACC is added to the result. WRLX is an extremely powerful instruction in that when combined with RDFX, it forms a single order low-pass shelving filter. - -### Operation -`ACC -> REG[ADDR], (PACC - ACC) * C + PACC` - -### Parameters - -| Name | Width | Entry formats, range | -|-------|--------|---------------------------------------------------------------------------------------| -| ADDR | 6 Bit | Decimal (0 - 63)
Hex ($0 - $3F)
Symbolic | -| C | 16 Bit | Real (S1.14)
Hex ($8000 - $0000 - $7FFF)
Symbolic | - -In order to simplify the WRLX syntax, see the list of predefined symbols for all registers within the FV-1 register file. - -### Instruction Coding -**CCCCCCCCCCCCCCCC00000AAAAAA01000** - -### Example -```assembly -; Single order LP shelving filter -Tmp_LP EQU 32 ; Temporary register for first order LP - -;---------------------------------------- - - sof 0,0 ; Clear ACC - rdax ADCL,1.0 ; Read left ADC - rdfx Tmp_LP,x.x ; First order LP... - wrlx Tmp_LP,y.y ; ...shelving filter - wrax DACL,1.0 ; Result to DACL and clear ACC -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/instructions/xor.md b/src/spinasm_lsp/docs/instructions/xor.md deleted file mode 100644 index 70663a5..0000000 --- a/src/spinasm_lsp/docs/instructions/xor.md +++ /dev/null @@ -1,30 +0,0 @@ -## `XOR M` - ------------------- - -`XOR` will perform a bit wise "xor" of the current `ACC` and the 24­-bit MASK specified within the instruction word. The instruction will invert `ACC` provided `MASK` equals `$FFFFFF`. (see also the pseudo opcode section). - -### Operation -`ACC ^ MASK` - -### Parameters -| Name | Width | Entry formats, range | -|-------|-------|-------------------------------------| -| M | 24 Bit| Binary
Hex ($000000 - $FFFFFF)
Symbolic | - -### Instruction Coding -**MMMMMMMMMMMMMMMMMMMMMMMM000010000** - -### Example -```assembly -XMASK EQU $AAAAAA - -;---------------------------------------- - sof 0,0 ; Clear all bits within ACC - xor $0 ; Set all ACC bits - xor %01010101_01010101_01010101 ; Invert all even numbered bits - xor XMASK ; Invert all odd numbered bits -``` - ------------------- -*Adapted from Spin Semiconductor SPINAsm & FV-1 Instruction Set reference manual. Copyright 2008 by Spin Semiconductor.* \ No newline at end of file diff --git a/src/spinasm_lsp/docs/markdown.py b/src/spinasm_lsp/docs/markdown.py new file mode 100644 index 0000000..9bb99d2 --- /dev/null +++ b/src/spinasm_lsp/docs/markdown.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Literal + + +class MarkdownDocumentationGenerator(ABC): + @property + @abstractmethod + def markdown(self) -> str: + """A markdown documentation string.""" + + def __str__(self) -> str: + return self.markdown + + +class MarkdownTable: + def __init__( + self, + cols: list[str], + rows: list[list[str]], + justify: list[Literal["left", "center", "right"]] | None = None, + ): + ncol = len(cols) + if any([len(row) != ncol for row in rows]): + raise ValueError(f"All row lengths must match col length of {ncol}.") + if justify is not None and len(justify) != ncol: + raise ValueError("There must be one justify position per column.") + + if justify is None: + self.justify = ["left"] * ncol + self.cols = cols + self.rows = rows + + def __str__(self) -> str: + header = " | ".join(self.cols) + + separators = [] + for just in self.justify: + if just == "left": + separators.append(":-") + elif just == "right": + separators.append("-:") + else: + separators.append(":-:") + separator = " | ".join(separators) + rows = "\n".join([" | ".join(row) for row in self.rows]) + + return f"{header}\n{separator}\n{rows}" + + +class MarkdownString: + def __init__(self): + self._content = "" + + def __str__(self): + return self._content + + def _add_line(self, s: str): + self._content += f"\n{s}\n" + + def add_heading(self, title: str, level: int): + if level < 1 or level > 4: + raise ValueError("Level must be > 0 and < 5.") + self._add_line(f"{'#' * level} {title}") + + def add_horizontal_rule(self): + self._add_line("-" * 24) + + def add_paragraph(self, s: str): + self._add_line(s) + + def add_table(self, cols: list[str], rows: list[list[str]]): + self._add_line(str(MarkdownTable(cols, rows))) + + def add_codeblock(self, source: str, language: str | None = None): + block = f"```{language}\n{source}\n```" + self._add_line(block) diff --git a/src/spinasm_lsp/documentation.py b/src/spinasm_lsp/documentation.py deleted file mode 100644 index 197768e..0000000 --- a/src/spinasm_lsp/documentation.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Documentation utilities for the SPINAsm LSP.""" - -from __future__ import annotations - -from collections import UserDict -from importlib import resources -from pathlib import Path -from typing import Any - -DOC_DIR = resources.files("spinasm_lsp.docs") - - -class MarkdownFile: - def __init__(self, path: str | Path): - self.path = Path(path) - self.name = self.path.stem - - def read(self) -> str: - with self.path.open() as src: - return src.read() - - -class DocMap(UserDict): - """A mapping of instructions to markdown documentation strings.""" - - def __init__(self, folders: list[str]): - self.folders = [Path(str(DOC_DIR.joinpath(folder))) for folder in folders] - self.data = self.load_markdown() - - @staticmethod - def to_lower(s: Any) -> Any: - """Attempt to convert a value to lowercase, or return unchanged.""" - return s.lower() if hasattr(s, "lower") else s - - def __getitem__(self, key: Any) -> Any: - """Get item with case-insensitive keys.""" - return super().__getitem__(self.to_lower(key)) - - def __contains__(self, key): - """Contains with case-insensitive keys.""" - return self.to_lower(key) in self.data - - def load_markdown(self) -> dict[str, str]: - data = {} - for folder in self.folders: - if not folder.exists(): - raise FileNotFoundError(f"Folder {folder} does not exist.") - files = folder.glob("*.md") - for file in files: - md = MarkdownFile(file) - # Store with lowercase keys to allow case-insensitive searches - data[md.name.lower()] = md.read() - - return data diff --git a/src/spinasm_lsp/server.py b/src/spinasm_lsp/server.py index a61b50d..e730dea 100644 --- a/src/spinasm_lsp/server.py +++ b/src/spinasm_lsp/server.py @@ -8,9 +8,9 @@ from lsprotocol import types as lsp from pygls.server import LanguageServer -from . import __version__ -from .documentation import DocMap -from .parser import SPINAsmParser +from spinasm_lsp import __version__ +from spinasm_lsp.docs import DocumentationManager +from spinasm_lsp.parser import SPINAsmParser @lru_cache(maxsize=1) @@ -26,7 +26,7 @@ def _parse_document(source: str) -> SPINAsmParser: class SPINAsmLanguageServer(LanguageServer): def __init__(self, *args, **kwargs) -> None: self._prev_parser: SPINAsmParser | None = None - self.documentation = DocMap(folders=["instructions", "assemblers"]) + self.documentation = DocumentationManager() super().__init__(*args, name="spinasm-lsp", version=__version__, **kwargs) @@ -132,7 +132,7 @@ async def hover(ls: SPINAsmLanguageServer, params: lsp.HoverParams) -> lsp.Hover ) if token.symbol["type"] in ("ASSEMBLER", "MNEMONIC"): - hover_msg = ls.documentation.get(str(token), "") + hover_msg = ls.documentation.get(str(token)) return ( None @@ -174,7 +174,7 @@ async def completions( kind=lsp.CompletionItemKind.Function, detail="(opcode)", documentation=lsp.MarkupContent( - kind=lsp.MarkupKind.Markdown, value=ls.documentation[opcode] + kind=lsp.MarkupKind.Markdown, value=ls.documentation.get(opcode) ), ) for opcode in [k.upper() for k in ls.documentation] diff --git a/tests/conftest.py b/tests/conftest.py index 8c8bd6f..451122e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -221,7 +221,7 @@ class RenameDict(TypedDict): { "symbol": "skp", "position": lsp.Position(line=37, character=2), - "contains": "## `SKP CMASK,N`", + "contains": "## `SKP CMASK, N`", }, { "symbol": "endclr", diff --git a/tests/test_documentation.py b/tests/test_documentation.py index d244c9a..0fc315a 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -8,10 +8,8 @@ import mistletoe.ast_renderer import pytest -from spinasm_lsp.documentation import DocMap +from spinasm_lsp.docs import ASSEMBLERS, INSTRUCTIONS -INSTRUCTIONS = DocMap(folders=["instructions"]) -ASSEMBLERS = DocMap(folders=["assemblers"]) VALID_ENTRY_FORMATS = ( "Decimal (0 - 63)", "Decimal (1 - 63)", @@ -94,6 +92,7 @@ def test_instructions_are_unique(): titles = {} for name, content in INSTRUCTIONS.items(): + content = content.markdown ast = json.loads( mistletoe.markdown(content, renderer=mistletoe.ast_renderer.ASTRenderer) ) @@ -142,6 +141,7 @@ def value_duplicated(value, d: dict) -> bool: def test_assembler_formatting(assembler): """Test that all assembler markdown files follow the correct format.""" assembler_name, content = assembler + content = content.markdown ast = json.loads( mistletoe.markdown(content, renderer=mistletoe.ast_renderer.ASTRenderer) @@ -174,6 +174,7 @@ def test_assembler_formatting(assembler): def test_instruction_formatting(instruction): """Test that all instruction markdown files follow the correct format.""" instruction_name, content = instruction + content = content.markdown ast = json.loads( mistletoe.markdown(content, renderer=mistletoe.ast_renderer.ASTRenderer)