PIC18F24k22 MPASM Code Generation Issue

Thread Starter


Joined Jun 19, 2012

I am writing assembly code for the PIC18F24K22 part under MPLAB / MPASM.

I have discovered that MPASM sets the 'a' bit in an instruction automatically in a complex and somewhat disturbing manner, consider the following list file snippet: (intended only to illustrate the assembler behavior- not real code)


000138 0101 00706 movlb 1 ;point to bank 1
00013A 0EEA 00707 movlw 0xEA ;load W with value
00013C 6E5F 00708 movwf 0x5F ;A bit =0, BSR ignored!
00013E 6F60 00709 movwf 0x60 ;A bit =1, BSR included!
000140 6F5F 00710 movwf CCPR3H ;A bit =1, BSR included!
000142 6E60 00711 movwf SLRCON ;A bit =0, BSR ignored!

Note how MPASM decides to set the 'a' bit in the instruction differently for
the SAME ADDRESS, depending on if it's specified by a mnemonic
or literal address! Passing the boundary 0x5F-0x60 reverses everything!

I understand that this boundary is where GPR and SFR meet in the access bank in this part, which is a big clue.

How are you supposed to write code that works with sneaky little things like this going on under your nose?

Is there some documentation that explains this?
Microchip has extended the SFR register number to beyond its 256 byte limitation. In these newer 18f chips, with all the multiplexing that is going on, so you may have to use bankselect, depending on the register map. This happen to me when trying to access CCP3CON. I am no assembly guy, so no doubt there are some extra bankselect's in the below code, but it did work.

Rich (BB code):
;******** CCP3 *****************
	movlw	12		;std PWM mode
	banksel CCP3CON
	movwf	CCP3CON
	movlw	128		;set CCP3 PWM timer as TMR6
	banksel CCPTMRS0
	iorwf	CCPTMRS0
	movlw	100		;100 instr cycles * 16 prescale * 1/16000000 = 100 us or freq. of 10kHz
	banksel PR6
	movwf	PR6
	movlw	50		;50% D.C. with PR4=100: 50 (instr cycles) * 16 (prescale) * 1/16000000 = 50 us
	banksel	CCPR3L
	movwf	CCPR3L
	movlw	7		;Prescale is 16, TMR6ON=1
	banksel T6CON
	movwf	T6CON


Joined Apr 28, 2012
I wrote one larger program for 13k50 (18f) using assembler.

I could post the source for reference, to see how messy it is to use assembler for 18F. But that's a bit off the thread topic?

It's also written in the datasheet, how the access bank is correlating to the actual address used.

Otherwise you have to use banksel all the time or you have to track the actual banks very carefully. So it's true assembler takes 15x the time, and can be somehow easy to write intially, but is very hard to modify for larger programs.


Joined Jun 26, 2012
The assembler knows where ACCESS RAM begins and ends and, if the label specifies the full 12-bit address of the RAM, will correctly generate the ,0 ,1 suffixes by default to address RAM. The same goes for the SFRs. In most 18Fs, all SFRs are in upper ACCESS RAM and can be addressed without banking but in THIS case, SOME SFRs are indeed in banked RAM and you must 1)set BSR to point to the bank for direct access or 2) use an FSR to get to it indirectly (without changing BSR) as you would any other non-ACCESS RAM.

You can help yourself along by specifying ,ACCESS or ,BANKED in your label references and MPASM will issue warnings if you confuse the two spaces. It can't however ensure that you are in the correct bank at run-time, that's up to you, the programmer.

I've done a TON of MPASM/18F (and 10x/12x/16x/17x for almost 20yrs now - geeze) and find that MPASM does a great job. Still, ANY banked target architecture ultimately requires time to understand all of the quirks and some programming experience/technique to cope with them.

Thread Starter


Joined Jun 19, 2012
Thank you for that clear explanation, I appreciate it.

Specifying ACCESS or BANKED will save me from ruin.

I have been programming PICs almost exclusively in assembly for a long time using MPLAB, most of the time bank switching is not a big deal, but the devil is in the details.


Joined Apr 28, 2012
18F + assembler is complex and disturbing as such.

It is still possible to write code in assembler. If you consider 24F, or PIC32, this becomes too much a learning curve. Even if you master it, you then will experience massive portability problems, as banking is all different!

C code can in most cases become ported accross almost all PICs- from 10F to PIC32. Configuration is easier as well, in MPLABX you get a small framework already for new projects.

I have used assembler (on PIC) for more than 5 years. I have even written some small assembler programs which run under Windows. What's the gain? In the end you only find yourself emulating things compilers do, using rather complex and complicate means, and still in no way portable.

For instance this simple array access (on 8bit chip) results in about 30 lines assembler, nearly impossible to maintain if you code this directly in assembler. Very complex and disturbing!

Rich (BB code):
      LCD_DataWrite((unsigned char)(palentry>>8),(unsigned char)palentry);
If you take this code and move over to 16bit PIC, the assembler code will be completely different, much easier for the 24F MCU, and less lines. But, you won't have to change anything!

And shift by 8 is replaced by special constructs, not shifted 8 times...


Joined Apr 28, 2012
What's really disturbing is the resulting assembler source if you need for whatever reason to use 16bit, or for instance arrays. You have to code it different for each different PIC because the RAM areas are different. Yes possible, but too much effort to read all the details. In C = simply don't care, if it is not fast enough, maybe optimize a little.

If you are good at assembler then you can use C almost like assembler. For instance you can shift by one, and then test
Rich (BB code):
, and things like that. By no means documentated, but there are many such details to improve code efficiency. The resulting assembler will be nearly 1:1, just one shift instruction, and one BTFSC for the carry flag.