# Make linker use bank 0 in relocatable mode

I'm tryin to build assembly project with MPASM/MPLINK for PIC16F628A. Microchip advice that it's better to stay on bank 0 and switch whenever you want different bank, and then get back to bank 0 (to minimise bank switching). That's why I want to put most GPR file registers in bank 0. Also most used SFR registers are in bank0. However the linker is trying to fit data starting from smallest databank which is GPR in bank 2. There is no way to tell the linker to start filling from bank 0, except make bank 2 protected in linker script. There should be better way...

UPDATE: For those who didn't understand the question (or didn't want): "How to reserve memory in bank 0 for PIC16F628A addresses from 0x20 to 0x6F in relocatable code mode in more than one file."

What I tried:

1.

file1.asm

FirstBank   udata_ovr 0x20
register1  res 1
register2  res 1


file2.asm

FirstBank   udata_ovr 0x20
register3  res 1
register4  res 1


Of course that doesn't do it because register1 overlaps with register3. I need register3 to be placed on unused bank.

2.

file1.asm

FirstBank   udata
register1  res 1
register2  res 1


file2.asm

FirstBank   udata
register3  res 1
register4  res 1


This will put all these registers in bank2

3.

file1.asm

FirstBank   udata   0x20
register1  res 1
register2  res 1


file2.asm

FirstBank   udata   0x20
register3  res 1
register4  res 1


update 2: The reason I want to fit my variables in bank0 is because most commonly used SPF registers are in bank 0 and I don't need to switch between banks when I use them (which means less code). And no, I shared memory (70-7F) is not enough to fit all variables I need. It wouldn't be optimal to switch banks when I can fit all variables that I use in 99% of the code in bank 0.

Now it should be clear. If anyone that's discussing my feelings actually wants to help I'll still appreciate it.

P.S. I guess I missed the very important keyword "relocatable mode", so I apologize about that. But please keep your comments/answer on topic. If something is not clear ask and I'll try to change question to be more clear. Thanks to all that actually want to help.

This can be done using directives in both the assembler source and in the linker script. When you use the udata primitive, the label becomes the section name.

Consider the following example source files similar to what you have provided:

file1.asm

SomeSection   udata
register1  res 1
register2  res 1

SomeOtherSection   udata
register5  res 1
end


file2.asm

SomeSection   udata
register3  res 1
register4  res 1
end


In file1.asm, there will be two output sections generated by the assembler named SomeSection and SomeOtherSection. You can verify this by turning on the output map file and looking at the listed section names.

The next step is to tell the link where those sections should be placed in memory, this requires a change to the default linker script.

Locate and copy the default linker script for your device (16f627a_g.lnk) to your project directory and add it in the Linker Script project settings.

At the bottom of the linker script you'll notice a set of SECTION specifiers like this:

SECTION    NAME=PROG       ROM=page            // ROM code space
SECTION    NAME=IDLOCS     ROM=.idlocs         // ID locations
SECTION    NAME=DEVICEID   ROM=.device_id      // Device ID
SECTION    NAME=DEEPROM    ROM=eedata          // Data EEPROM


SECTION    NAME=SomeSection RAM=gpr0
SECTION    NAME=SomeOtherSection RAM=gpr2


You can verify in the map output file that your variables have been placed in the correct location. From the example above, registers1-4 go into gpr0, register5 goes into gpr2.

• Excellent answer! You deserve a beer! I hope there are more people like you in stackexchange. – NickSoft Sep 30 '12 at 16:59

The udata_shr should do what you want. See the assembler/linker manual section 4.64:

This directive declares the beginning of a section of shared uninitialized data. If label is not specified, the section is named .udata_shr. The starting address is initialized to the specified address or will be assigned at link time if no address is specified. This directive is used to declare variables that are allocated in RAM that is shared across all RAM banks (i.e. unbanked RAM). No code can be generated in this segment. The res directive should be used to reserve space for data.

• thanks, but the shared ram (16 bytes) is not enough in my case. Yes, it's useful for some variables, but can't fit all in it. I just need to fit my variables in (the rest of) bank 0 which is 80 bytes and more than engough for my case. I'll up your answer because this might be good solution for someone else – NickSoft Sep 21 '12 at 9:52

I want to add a non-optimal solution that actually works, but it's not that convenient. Option 1 If all variables that need to be in bank 0 are listed in only one file and exported with "GLOBAL" an udata section with fixed address would do the job. I'm still waiting if someone will sugest something that allows to address bank 0 from multiple assembler files (objects). Maybe there is a way to pass the name of bank as mentioned in linker script or some way to force linker to start with bank 0.

option 2: Of course if you don't use bank2 at all a "place-holder" can be used to allocate all GPRs in bank2:

placeHolder   udata    0x011F
res    48


option 3: The closest thing to a solution is to name all udata sections across the files with the same name and (very important) pad the space to more than 48 bytes:

in file1.asm

FirstBank    udata
variable1    res   1
variable2    res   1
res   48    ; this needs to be decreased if data in this section becomes
; Larger than 32 bytes. The idea is to keep
; data+padding > 48 so the section cat't fit in bank 2


in file1.asm FirstBank udata variable3 res 1 variable4 res 1

But these are just workarounds, not real solutions.

UPDATE: Note that you shouldn't have to worry about liker starting from bank other than 0 with MCUs that have all banks with the same size. For example for PIC16F648A linker starts from bank 0.