0

This is more a question of style than anything else and might strike some as a bad thing to do. If I have a serial port pin on RC4, say, I can make some defines to help out:

#define TXSER_PIN   LATC, RC4

What I realise then is that you can do this:

banksel     LATC
bsf         TXSER_PIN
banksel     ANSELC
bcf         TXSER_PIN
banksel     TRISC
bcf         TXSER_PIN
banksel     INLVLC
bsf         TXSER_PIN 

I'm using the same define all the way down even though, theoretically, I suppose it's wrong to do so. The point is that I can change LATC, RC4 to LATB, RB7 and (after also changing APFCON0 to move the TX function) that's all that changes.

On this PIC I'm using, TRISA = 0x8C and LATA = 0x10C. But only the BANKSEL causes them to be distinguishable - TRISA, LATA, ANSELA, WPUA are really all the same 0x0C offset from the start of the bank and, as compiling the instruction implicitly does an "operand & 0xFF", they can be used interchangeably.

(In fact, that can be quite annoying if you use pairs of operands incorrectly. Something like bsf EEADRL, RC5 will not flag an error even though the compiler could easily do so. I wrote some awk to catch this...)

Can I run into trouble doing this or is it just bad form? Sure makes for less #defines though.

carveone
  • 2,568
  • 1
  • 15
  • 22
  • I realise that Olin and others probably don't do it like this anyway. The professional way is likely to map the function of each pin up front and do "banksel trisc; movlw portc_directions; movwf trisc". Still.... – carveone Jul 12 '14 at 16:18

2 Answers2

2

At the end of the day the assembler doesn't have a clue about #defines.

The assembler doesn't even get to see them. The preprocessor first scans the file expanding the #defines (called preprocessor macros) and converts them into the numbers they represent. So the assembler itself just gets numbers.

So no, the assembler can't understand if

bsf EEADRL, RC5

is valid or not since it never gets to see that. The preprocessor has no concept of assembler syntax or what different symbols mean, so it can't know that it's wrong.

In my opinion macros should always say what they mean. As long as the content of the macro expands out to the correct values it doesn't really matter what any interim macros may say. If the macro TXSER_PIN has a valid contextual meaning to the person reading the code at that point then there is no reason to use it. If it doesn't, then use a macro that does have meaning.

After all, a macro is purely a human representation of numbers or other bits of code / data, in order to make the code more readable.

They don't take up any space in flash or ram, so you can have as many of them as you like.

More important to think about is grouping SFR operations that are in the same bank to reduce the number of banksel calls, since each one takes flash and CPU cycles that could be used elsewhere.

Majenko
  • 55,955
  • 9
  • 105
  • 187
  • "The preprocessor has no concept of assembler syntax or what different symbols mean, so it can't know that it's wrong." Yes, but it could do in this case as EEADRL and RC5 are symbols, and are organised by groups in the INC file. I had some gawk go through and do the grouping and then check my asm source. I checked `SSPOV` in `SSPSTAT` (instead of SSPCON) once and there was a lot of swearing. – carveone Jul 12 '14 at 16:34
  • I understand how defines work alright. In my examples the assembler will translate 0x8C to 0x0C and 0x10C to 0x0C so it works. But, you may be right about macros saying what they mean. As the macro expands to: `banksel ANSELC; bcf LATC, RC4`, that jars programming sensibilities somewhat, even though it works! – carveone Jul 12 '14 at 16:38
  • Hmmm. You're right. It's a hack. I think I'll make a pinmap file and process it into "set" variables. That way you aren't playing "hunt where I set the pin's direction" in the source, nor do you forget to set any unused pins. – carveone Jul 12 '14 at 16:53
2

It's certainly possible to take advantage of the fact that various addresses differ only in the bank-select bits. I would generally avoid doing so, however, since code written in such fashion may be difficult to port to chips where registers' addresses have different relationships to each other. Although Microchip has been pretty good about continuing to produce parts that can run older code, migrating designs to newer parts can often result in major cost savings.

One trick which I've used in assembly language on the PIC 16C505, btw, which wouldn't have worked in C, was to write a cooperative "dual tasker" which used one piece of code to handle bit-banged serial I/O using PORTB, and another piece of code to handle it using PORTC. Global variables were held in "unbanked" RAM, and per-task variables in "banked" RAM. The main loop looked something like:

loop: call springboard movwf tempPC ; Banked RAM movlw 0x21 xorwf FSR ; Bits 0-4 of FSR point to PORTB or PORTC; bit 5 sets RAM bank movf tempPC,w goto loop springBoard: movwf PC

Each task would run a little bit of code and then perform an RETLW with the address of the next piece of code it should execute. The majority of the ROM was taken up by code which would sometimes operate on PORTB using one set of variables, and sometimes on PORTC using the other set. The overall effect was something like a 40% code savings compared with having to use two separate sets of code.

supercat
  • 45,939
  • 2
  • 84
  • 143
  • It's a piece of engineering I'm glad I tried, and it worked well for its purpose, though a lot of factors have to be "just right" for the approach to work. I did something slightly similar on a DSP project which used external RAM; my RAM chip was 64Kx16, and my CPU had a total address space of 64Kx16, of which 40Kx16 was occupied by an internal resources, so I arranged things IIRC so that the top 16K of address space would always map to the top 16K of RAM, and the other 8K of space would map to one of 8 selectable banks. The logic for audio compression needed to work with two data streams... – supercat Jul 12 '14 at 17:30
  • ...and so I put the variables for the audio-compression module into the 8K of bankable storage. On a CPU like the ARM which supports base+displacement addressing there'd be little need for such tricks, but on a CPU without such a mode, using "fixed address" structures can be much more efficient than using pointers to structures. – supercat Jul 12 '14 at 17:32