Coding and general discussion relating to the compiler
Moderators: David Barker, Jerry Messina
-
SHughes_Fusion
- Posts: 219
- Joined: Wed Sep 11, 2013 1:27 pm
- Location: Chesterfield
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
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:-
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)