Hi,
Until now, I've been coding for PICs in assembly by
using the 'Absolute Code' style assembly.I've been
simply cutting and pasting code from one file to another.
And this method is not so good especially since there might
be namespace conflicts. Now that i've got quite some routines
which I use regularly in most of my projects. I'm thinking of
going for Object code, so that I can maintain my library as a bunch
of OBJ files. I think this can make things a lot easier for me.
Any suggestions or Gotchas ? Or is Absoulte mode assembly in any way
superior to Object code based assembly ?
> Until now, I've been coding for PICs in assembly by
> using the 'Absolute Code' style assembly.I've been
> simply cutting and pasting code from one file to another.
> And this method is not so good especially since there might
> be namespace conflicts. Now that i've got quite some routines
> which I use regularly in most of my projects. I'm thinking of
> going for Object code, so that I can maintain my library as a
bunch
> of OBJ files. I think this can make things a lot easier for me.
>
> Any suggestions or Gotchas ? Or is Absoulte mode assembly in any
way
> superior to Object code based assembly ?
I exclusively use object mode and the linker. It eliminates the need for
"manual" control over some things, and of course each module gets its own
local namespace. This is clearly the way to go for anything but a toy
project.
However, the binary library idea won't work as you think. The problem is
that each project has a little (sometimes a lot) different configuration.
This includes different processors, different banks reserved for things,
different size and location of the software stack, etc. The linker isn't
flexible enough to allow one precompiled object module to be configured for
the different setups at link time. To get around this, my libraries of
standard routines are source code include files instead of object files.
Each project that uses a library routine has to create a 3 line module that
references the project configuration include file, the library routine
include file, followed by the END statement. For example, here is the 24
bit floating point multiply module for the HED project:
include "hedlib.inc"
include "..\pic\fp24mul.inc"
end
For a wider discussion of how I use the linker, include files, how I break
up projects into modules, and template standard modules see http://www.embedinc.com/pic.
> I exclusively use object mode and the linker. It eliminates the need for
> "manual" control over some things, and of course each module gets its own
> local namespace. This is clearly the way to go for anything but a toy
> project.
I beg to differ on this. When writing very cycle concious assembly you often
have to make sure everything is aligned properly on page boundaries. For
example, I will often put all the state routines for a state machine on the
same 256-byte page, and have the state variable be the offset of the routine
for the current state on that page. Now going to the current state is as
simple as:
> I beg to differ on this. When writing very cycle concious assembly you
often
> have to make sure everything is aligned properly on page boundaries. For
> example, I will often put all the state routines for a state machine on
the
> same 256-byte page, and have the state variable be the offset of the
routine
> for the current state on that page. Now going to the current state is as
> simple as:
>
> movlw high(state_machine_state_0)
> movwf pclath
> movf current_state,W
> movwf pcl
Using the linker doesn't proclude this type of programming. In fact, I
think it would make your allocation of routines to pages easier. This
example has two contraints. First, you need a block of code (table) that
starts on a 256 word boundary. Second, you need certain modules (the ones
that contain the state routines) to be loaded onto the same program memory
page as the table. If it makes sense to partition the code so that all the
state routines end up in the same module, then the problem becomes trivial
so I'll show how to do this assuming the state routines are in different
modules.
Here is the relevant section of my default linker control file for the
16F876:
CODEPAGE NAME=config START=0x2007 END=0x2007 //special processor config
word
DATABANK NAME=bank0 START=0x20 END=0x6F //register bank 0
DATABANK NAME=bank1 START=0xA0 END=0xEF //register bank 1
DATABANK NAME=bank2 START=0x110 END=0x16F //register bank 2
DATABANK NAME=bank3 START=0x190 END=0x1EF //register bank 3
SHAREBANK NAME=globalram START=0x70 END=0x7F PROTECTED //global regs, bank
0
SHAREBANK NAME=globalram START=0xF0 END=0xFF PROTECTED //global regs, bank
1
SHAREBANK NAME=globalram START=0x170 END=0x17F PROTECTED //global regs, bank
2
SHAREBANK NAME=globalram START=0x1F0 END=0x1FF PROTECTED //global regs, bank
3
SECTION NAME=.udata_shr RAM=globalram //global memory mapped to all register
banks
SECTION NAME=.BANK0 RAM=bank0 //for registers explicitly in bank 0
SECTION NAME=.BANK1 RAM=bank1 //for registers explicitly in bank 1
SECTION NAME=.BANK2 RAM=bank2 //for registers explicitly in bank 2
SECTION NAME=.BANK3 RAM=bank3 //for registers explicitly in bank 3
I can use this setup without modification for the vast majority of projects,
including this example. Note that each code page is defined as a separate
linker section. This prevents individual modules from straddling code
pages, which guarantees you can do GOTOs and CALLs within a module without
having to worry about PCLATH each time, assuming you aren't explicitly
setting code sections or addresses.
Let's pick page 1 to be the page that will hold the states jump table and
all the state routines. To locate the table, just force it to start at any
of the 256 word boundaries on that page. The linker will place code at
fixed addresses first, then place relocatable code around it. I would stick
the table right at the start of the page so that it introduces no additional
memory fragmentation:
code h'800' ;start of 256 word block on states page
tbl_states
goto state_0
goto state_1
...
goto state_49
Now you only have to make sure that sections of code that contain the state
routines jumped to from the states table are also loaded into page 1:
;
; State routines. The STATE_xx labels are jumped to from the
; states table, and must be on the same page as the states table.
;
code1 code ;force same page as states table
state_0
blah
blah
state_1
blah
blah
I believe this now gets you everything you asked for. However, there are
also some additional advantages. You only specify the modules or sections
of code that must be on the states page, and the linker allocates everything
else. You can (should) break up the code into separate modules. Each of
these modules now gets its own name space for local symbols. As long as all
the code you've specified to go onto the states page fits, there will be no
problem. If it doesn't fit, you will get a linker error telling you so. It
will not silently overflow to another page leaving you to figure out why
things are broken at debug time.
All in all the linker is a powerful tool that can be a big help if let it.
You can think of it as a superset of absolute mode. You can still force
fixed addresses when you want to, but you can let the linker allocate space
in the majority of cases where you don't care. Even if you forced all
addresses, it would still give you separate local name spaces for modules
and detect memory collisions between your forced allocations.
********************************************************************
Olin Lathrop, embedded systems consultant in Littleton Massachusetts
(978) 742-9014, olinKILLspamembedinc.com,http://www.embedinc.com
I've never before seen a linker that lets me force code to absolute
locations (as opposed to just into a named section which of course implies a
location).
I will be looking closer into this.
Bob Ammerman
RAm Systems
(contract development of high performance, high function, low-level
software)
> Ah, the light dawns....
>
> I've never before seen a linker that lets me force code to absolute
> locations (as opposed to just into a named section which of course implies
a
> location).
>
> I will be looking closer into this.
If you want to see an example, take a look at HAL_INTR.ASPIC at http://www.embedinc.com/pic/hal.htm. This is the interrupt module and
contains two CODE sections. The interrupt routine itself is forced to start
at location 4, and the remaining code is relocatable. There is no guarantee
that the relocatable code will even end up on page 0.
Hi, Olin, I looked at your code and it's well thought out. My experience
using the linker was not good solely because I couldn't seem to get the
macro expansion working, hence my .lst files were a bit useless. I would
like to go this route though. What's you experience with macro expansion?
> Hi, Olin, I looked at your code and it's well thought out. My experience
> using the linker was not good solely because I couldn't seem to get the
> macro expansion working, hence my .lst files were a bit useless. I would
> like to go this route though. What's you experience with macro expansion?
Since the debugger works with the source files, the list files don't have
much purpose. Every once in a great while you have to look in the list file
to understand an assembly error message because it is in a macro, or you
want to check the value of a symbolic constant. Because of this I set macro
expansion on. This also makes the list files very big, so I usually have
the build system blow them away unless errors were encountered. All in all,
macro expansion is an issue that comes up very rarely.
The biggest pain in the butt is that the debugger "step over" and "step
into" commands don't work for macros. In fact, the debugger just refuses to
update the highlighted source line until it hits a plain instruction. If
you try to step to a macro, it will keep cruising until the next non-macro
source line. This is a real pain if you have lots of macros in a row. I
deal with this by stepping in the program memory window instead of the
source code window. It will follow along in the source code window within a
module, but won't switch between modules properly. What a pain! Doesn't
Microchip ever use this stuff themselves!?