BBC BASIC Assembler VFP/SIMD Extension
(c)2011 TBA Software
Development by Alan Peters

Version 0.02 - 06/07/2011

--------
Contents
--------
      Introduction
      BASIC VFPLib
      Use of Pattern Tables
      VFP Assembler Source Code
      VFPData Assembler Pattern Tables
      Validation and Testing
      Known Limitations and Variations
      References


Introduction
------------
This project adds support to the BBC BASIC assembler for the ARM VFP and NEON (SIMD) co-processor extensions present in the Cortex A8 and other compatible hardware.  The instruction set has been fully implemented as described in the ARM manual (1) subject to a small set of limitations and variations described below.

The VFP and SIMD instruction set provides a comprehensive set of IEEE standards compliant (2) floating point operations, plus Single Instruction Multiple Data operations that can enhance the performance of many applications that transform data in a wide variety of ways.  Having access to up to 64 additional registers can in itself make a significant difference to application performance.

In addition to the VFP assembler, a small BASIC support library "VFPLibAsm" is provided to simplify access to the VFPSupport module.  Use of VFPSupport is essential as it enables the co-processor, and allows RISC OS to manage/preserve VFP state between tasks and across operations.


BASIC VFPLib
------------
A BASIC version of the VFP assembler called VFPLib is included in the source code.  This was developed initially as a proof of concept, and it makes it easy to test any changes to the pattern tables and encoding logic.  VFPLib also generates the VFPData source file, which contains optimised pattern tables for the assembly language version of the VFP assembler.


Use of Pattern Tables
---------------------
In order to minimise the amount of code required, the VFP assembler makes use of extensive pattern tables describing the syntax of each instruction and how it requires encoding into the resulting 32bit Word.  The SIMD instructions in particular have a considerable amount of variations in the encoding logic, much of which is handled within the pattern tables.

There are two version of the pattern tables.  The first version sits within the BASIC VFPLib source code, and is created in a friendly format using ASCII strings for all of the parameters.   This makes it easy to edit and understand.  The second version is created by VFPLib and is held in the VFPData file within the BASIC source code.  This version is in binary format and is heavily optimised to minimise the assembler code required to use it.  The tables are created using Macros from the VFPMacro header file in order to make things as readable as possible.

So how do the pattern tables work?

The code searches through the syntax lookups to find a pattern string that matches the instruction presented.  During the matching process "variables" are created that hold values required for the final encoding.  The code ensures the data-type matches the data-type filter, and alignment the alignment filter.  For 32bit immediate constants the opcmode constant created is matched against the opcmode list specified on the related encoding.  Once the syntax is fully matched, the params list is processed to set any additional variables required for encoding.  Finally, the encoding bit pattern is parsed to create the 32bit word.

The pattern tables have the following structure

1) Syntax lookup

   Contains the following for each variation of each instruction
   a) Pattern String, e.g. "VORR{.<dt>} <Qd>,<#32>"
   b) Encoding name , e.g. "VORR (immediate) A1"
   c) Data-type list, e.g. "I16,I32"
   d) Align list    , e.g. "64x01,128x10,256x11"
   e) Params List   , e.g. "op=0,sf=1,sx=bp_size[1]"

  The pattern string has the following syntax:
    {|}~ surrounds an optional section
         | separates options, where one must match
         ~ signifies that the option repeats until it isn't matched
           (i.e. some register lists)
    <op> where op is code in the table below
    all other characters are matched exactly
    space signifies optional spaces in the input
    comma signifies optional spaces before and after the comma character

  Op table
  c     = condition code
  dt    = data-type           - matching against data-type list
  size  = data-type size only - matching against data-type list
  spec  = special register - FPSID,FPSCR,MVFR1,MVFR0,FPEXC
  Rn    = ARM core register (R0-R15,SP,LR,PC,<expr> returning integer 0-15)
  Sn    = Single float (S0-S31)  (32bit) register
  Dn    = Double float (D0-D31)  (64bit) register
  Qn    = Quad word    (Q0-Q31) (128bit) register
    -   = end of register list specifier e.g. <Dn->
    +x  = Register number with offset x from base            e.g. <Dn+2>
    +?  = Register number with incrementing offset from base e.g. <Dn+?>
  ()    = Register list brackets - actually {} within instruction
  @     = Alignment e.g. @128
  !     = Writeback flag
  #n    = immediate constant of +  1-8 bits, 0 to (1<<n)-1
  #-n   = immediate constant of +- 1-8 bits, -((1<<n)-1) to (1<<n)-1
     sx = shift number left x bits when encoding e.g. #1s2
   where n can also be one of:
   c    = sets n to data-type size in bits-3 (i.e. 8=0, 16=1, 32=2, 64=3)
   d    = shifts value right by data-type size in bits-3 (e.g. <#c>)
   e    = shifts value right by data-type size in bits-4
   32   = special immediate constant of 32bits, encoded using opcmode logic
  #+-10 = special version of #n for encoding register offsets in words
  lbl   = label - special version of #+-10 for evaluating VLDR/VSTR reg,label

  Encoding name is used to match an entry in the encoding table

  Data-type list is a comma separated list of available data-types
  (When a repeating optional register list is used, the first entry in the
  data-type list is used to signify the maximum register number permitted)

  Align-list is a comma separated list for VLDx and VSTx @ alignments
    Syntax is <alignment>x<encoding> (eg 64x11) where <encoding> is in bits

  Params List contains a list of optional "variables" created before encoding
    Syntax is <variable>=<value>
    <value> is a number in BASIC notation or
            bp_  signifies a bitpattern
            bp_? signifies a pipe delimited list of variations per data-type
                 (the variation used matches the index in the data-type list)
            bitpattern syntax is shared with the encoding (see below)

2) Encoding table
   Contains the following for each encoding specified in ARM (1)
   a) Versions      , e.g. "23S"
   b) Encoding name , e.g. "VORR (immediate) A1"
   c) Bitpattern    , e.g.
      "1111001imm[7]1Vd[4]000imm[654]Vd[3210]opcmode[3210]0Q[0]01imm[3210]"
   d) opcmode list  , e.g. "00xx1,010x1"

   Versions contains a list of supported versions for this encoding:
     2 = VFPv2
     3 = VFPv3
     4 = VFPv4
     S = SIMD (NEON)
     $ = SIMDv2

   Encoding name is used to match this encoding from a syntax lookup

   Bitpattern describes how to encode each bit from right to left
      Each bit can either be 0,1, or a variable name followed by a [bitlist]
      For encodings a bit pattern always contains 32 bits
      For parameters a bit pattern will contain a lower number of bits

The Variables used are as follows.  These match names from  ARM (1)  encoding
   a        = used for encoding alignment for VLDx/VSTx
   align    = used for encoding alignment for VLDx/VSTx
   cond     = condition code encoding
   E        = used in VCMP encoding
   esize    = variation of data-type size (8=2,16=1,32=0)
   F        = floating point data-type flag
   ia       = used in VLDx/VSTx encoding
   imm      = immediate constant
   imm3     = variation of data-type size (8=1,16=2,32=4)
   imm4     = (1<<n) - immediate (used for encoding certain shift constants)
   imm6l    = immediate shift left
   imm6r    = immediate shift right
   imn      = immediate negative (0-imm)
   L        = long flag - set to 1 when data-type size is 64 bits
   len      = used in VTBL encoding for length of register list
   op       = used as flag in many encodings
   opc      = used in VMOV encoding
   opc2     = used in VCVT encoding
   opcmode  = used in 32bit immediate constant encoding
   P        = used in VLDMIA/VSTMIA encoding
   Q        = quadword register flag (1/0)
   regcount = number of registers in list (encoded differently for S/D types)
   Rt       = ARM register t
   Ru       = ARM register u (t+1)
   sf       = used in VCVT encoding
   size     = data-type size in bits - 3 (e.g 8=0, 16=1, 32=2, 64=3)
   size1    = size - 1
   spec     = special register number (used in VMRS/VMSR)
   sx       = used in VCVT encoding
   sz       = float point size flag (1=64bit,0=32bit) used in many encodings
   T        = used in VLDx encoding
   type     = used in VLDx/VSTx encoding
   U        = unsigned data-type flag
   Vd       = register number d
   Vm       = register number m
   Vmx      = register number m variation
   Vn       = register number n
   W        = used in VLDMDB/VSTMDB encoding
   x        = scalar offset [x]


VFP Assembler Source Code
-------------------------
The VFP assembler is integrated into the BBC BASIC source code.  It includes the following files:

     s.VFP            The source code for the VFP assembler
     s.VFPData        The pattern tables created by VFPlib
   hdr.VFPMacros      The macros used for creating the pattern tables
   doc.VFPdoc         This documentation file

VFPLib.GenData        Creates the s.VFPdata source code file
VFPLib.VFPLib         The BASIC version of the VFP assembler
                      Used to test the pattern tables
 tests.VFP.TestVFP    A test script that assembles a version of each encoding
                      outputs debug.s.TestVFP for use with ObjAsm/DecAof
 tests.VFP.VFPLibTest Tests the BASIC version of the assembler
                      outputs debug.s.VFPLibTest for use with ObjAsm/DecAof

Alterations have been made to the following existing BBC BASIC source files:

    s.Assembler       Calls the VFP assembler when a "V" is the first char
                      LNK statement changed (to s.VFP from s.Lexical)

VFPData Assembler Pattern Tables
--------------------------------
The pattern tables in VFPData are optimised versions of the tables described above in the BASIC version.  A set of Macros in VFPMacros are used to create the binary data to make the code as readable as possible.

In particular the bit patterns are optimised to a fixed constant (VFP_BitField) and a list of arguments (VFP_BitFieldArgs) that contain the variable to use, a bitwise mask, and amount to shift the result by.  This makes it very quick and easy to construct the output value as there are no ASCII strings to parse.

See hdr.VFPMacros for additional notes on the assembler pattern tables.


Validation and Testing
----------------------
TestVFP assembles a version of every encoding, plus creates a file for ObjAsm that can be assembled and disassembled using DecAof to verify each instruction.

VFPLibTest performs the same action as TestVFP but uses the BASIC version of the assembler, useful for testing any alterations to the pattern tables and associated code before applying the changes to the assembler version.


Known Limitations and Variations
--------------------------------
Functions/Expressions are supported for register numbers when prefixed with # for example Q#FNregnumber or Q#(A%+B%).

For ARM registers R0-R15, SP,LR,PC and <expr> returning 0-15 are all supported. Pattern tables are carefully ordered so the ARM register variation comes last so it can throw a bad register error correctly.

VDUP has been renamed to VDPL.   This is because VDU and P. are both BBC BASIC keywords that are stored as tokens, resulting in VDUP.16 being altered to VDUPRINT16 which isn't very useful!

The AL condition code is not supported as an option on all unconditional SIMD instructions.

ARM states that quad word registers may be used in register lists instead of double registers, i.e. Q0-Q1 instead of D0-D3.  This behaviour is unsupported.  ARM p287 (1)

ARM states that multiple data-types can be specified where multiple data-types are implied, such as shortening and widening operations.  This behaviour is unsupported.  ARM p284-p285 (1)

All of the syntax variations in ARM (1) have not been carefully evaluated so it is entirely possible that some other variations from the standard are not supported.


References
----------
(1) ARM Architecture Reference Manual - DDI0406B.pdf
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406b/index.html

(2) IEEE 754 - IEE Standard for Floating Point Arithmetic
    http://en.wikipedia.org/wiki/IEEE_754-1985
