Optimising Swordfish code

Coding and general discussion relating to the compiler

Moderators: David Barker, Jerry Messina

Post Reply
SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Optimising Swordfish code

Post by SHughes_Fusion » Tue Sep 02, 2014 12:46 pm

I've been doing quite a lot of work optimising code generated by Swordfish recently - partly to fit a bootloader in to the 2k bootblock on the 18F46K22 and partly because I don't like bloat!

I thought I'd start a thread to share some of my observations and hope that others will chip in with things they've found. I hope this will end up being as useful to the more experienced users as it will be to beginners.
  • Only pass what you need to
    If you can pass a byte rather than a Word or a pointer rather than a string then do so. Obvious to most but it's amazing how much you can save. Even if you need to use a Word or whatever to do calculations, if you know the result will always fit in a byte then use variable.byte0 to only pass the byte.
  • Don't pass string expressions to Subs or Functions
    Passing string expressions such as StrA + ":" + StrB is often very inefficient. It is often better to create a temporary string, create the string in that and then pass it to the sub / function.
  • Take care with DecToStr
    If you are only converting a single digit it is much more efficient to just add 48 and cast to a Char - char(variable+48) - you can save even more space if you can add the 48 before casting. For simple usage you are better creating your own DecToStr routine as the supplied ones are written to be very flexible which unfortunately means they use a lot of resources.
  • Think about creating your own number formats
    If you need to deal with fractions but only need limited resolution then consider using integers and multiply by / divide by 10 or 100 when necessary. The floating point routines use much more code than integer multiplication and division. A Word can store a value from 0 to 6553.5 in tenths for example. Often 'internal' calculations can be made in this format and conversions are only needed when the number is input or output.
  • Do your own basic EEPROM access
    If you only need very limited access to the internal EEPROM then considerable code savings are possible by writing your own functions. The library seems to use a fair bit of unnecessary code when you are only accessing one byte at a time.
  • Make use of the FSRs
    When accessing RAM arrays and strings it is often much slicker to use the FSR registers. Care must be taken if other subs or functions (including in-built ones) are called while using an FSR as they are also used by the compiler. FSR2 seems to be free the most. Usage is simply to set the FSR to the address of the variable - for example FSR2 = @ArrayName - then use the POSTINCn, PREINCn and INDFn SFRs. Reading or Writing to POSTINC will access the memory location pointed to the increment the pointer. PREINC will increment the pointer before accessing the memory and INDF will access the memory at the FSR location without changing the pointer. This avoids the need to use index variables for arrays and strings which are read or written in a linear manner.
Those are just ones I can think of quickly, if there is interest in this thread then I'll keep adding them when I come across a new one.

DannyScott
Posts: 22
Joined: Thu Sep 05, 2013 4:45 pm
Location: United Kingdom

Re: Optimising Swordfish code

Post by DannyScott » Fri Sep 05, 2014 2:58 pm

Thanks for starting this thread!

Swordfish has lots of lovely undocumented features which can be used such as passing and returning FSR's and the TABLEPTR which you have mentioned already.

Another important point I would like to make which other forum users will agree with is to break down complex expressions into smaller more manageable parts, which very often produces more concise code. This doesn't look like much, but if the project you are working on requires a great deal of string processing, it is amazing how much code space can be saved!

For example:-

Code: Select all

Print("Hello World!", 13, 10)
Can coded more efficiently as follows:-

Code: Select all

Print(@("Hello World!"))     'Passes the string constant using TABLEPTR
PrintCrLf()
Using the code excerpt below.

Code: Select all

Dim PrintByte As USART1.WriteByte
Dim PrintString As USART1.Write       

'******************************************************************************
' Constant declarations
'******************************************************************************

  Const CR = #13
  Const LF = #10

'******************************************************************************
' Subs to print CrLf
'******************************************************************************
  
  Public Sub PrintCrLf()
    PrintByte(LF)
    PrintByte(CR)
  End Sub

'******************************************************************************
' Print strings passed by address
'******************************************************************************

  Public Sub PrintStr(saddr As FSR2) 
    While (INDF2 <> 0) 
      PrintByte(POSTINC2) 
    End While 
  End Sub 

'******************************************************************************
'  Overload - Print strings stored as constants passed by address
'******************************************************************************

  Public Sub PrintItem(Const_Addr As TABLEPTR) 

    Dim t_tblptru As Byte      ' copy of TBLPTRU (only needed on devices > 64K) 
    ' save current tblptru 
    t_tblptru = TBLPTRU 
    ' const strings are in the lower 64K 
    TBLPTRU = 0 
    Asm 
        TBLRD*+ 
    End Asm 
    ' write until we find a null char 
    While (TABLAT <> 0) 
        PrintByte(TABLAT) 
        Asm 
            TBLRD*+ 
        End Asm 
    End While 
    ' restore tblptru 
    TBLPTRU = t_tblptru 
  End Sub 

'******************************************************************************
'  Overload - Print strings passed byval
'******************************************************************************
  
  Public Sub PrintItem(ByVal pStr As String)
    PrintString(pStr)
  End Sub

'******************************************************************************
'  Overload - Print a byte
'******************************************************************************
  
  Public Sub PrintItem(ByVal Print_Byte As Byte)
    PrintByte(Print_Byte)
  End Sub
  
'******************************************************************************
'  Overload - Print a Char
'******************************************************************************
    
  Public Sub PrintItem(ByVal PrintChar As Char)
    PrintByte(PrintChar)
  End Sub

'******************************************************************************
'  Main Print() Compound Sub
'******************************************************************************

Public Compound Sub Print(PrintItem)


Post Reply