The MODE register serves as a pointer to all port control registers. The control registers for the ports are denoted as !Rx (!RA, !RB, or !RC) but writing to !RA may set the input/output configuration or the TTL/CMOS, or the pull-up/hi-z configuration depending on the setting of the MODE register.

In order to write or read any ports control register, the MODE register must contain the appropriate value to point to that port register. The W register is used to write into the MODE register.

The format of the MODE register is shown below. The four least significant bits serve as the pointer to the set of port registers to be accessed. Upon reset, the MODE register is initialized to 0Fh. In this default configuration, the program can access the !RA, !RB, and !RC (data direction) registers.

0 0 0 0 M3 M2 M1 M0
bit 7 bit 0

In order for the program to gain access to the other port control registers (to set the pullup resistor, input voltage levels, Schmitt triggers, etc.), the program must write a different value into the MODE register. Figure 3-3 shows how the MODE register controls access to the port registers.

Example:

Mode = 0F writing to !RA, !RB or !RC will affect the direction of the port pins. A 1 will set that port pin to an input. A 0 will set it to an output

Mode = 0E writing to !RA, !RB or !RC will affect the use of the weak pullup resister on this port. A 1 will disable it and a 0 will enable it.

Mode = 0D writing to !RA, !RB or !RC will affect the input level for each port pin. A 1 will set the level to TTL and a 0 will set it to CMOS.

Mode = 0C writing to !RB or !RC will affect the trigger mode of the port pin. A 1 will disable Schmitt-Trigger input and a 0 will enable it.

Mode = 0B writing to !RB will affect the Wake Up mode for that pin. A 1 will disable multi-input wake up (MIWU)

Mode = 0A writing to !RB will affect the Wake Up Edge Selection for that pin. A 1 will select falling edge detection and a 0 will selecte rising edge detection

Mode = 09 !RB Multi Input Wake Up Pending Register

Mode = 08 !RB Comparator Enable Register. Bit 7 is the comparator enable bit. A 1 will disable the comparator and a 0 will enable it if port B pins 1 and 2 are configured as inputs. Pin 1 is used as the negative input and pin 2 as the positive input. Bit 0 of the !RB register is the result of the comparison. Bit 6 is the comparator output enable. A 1 will disable output of the comparitor result on port B pin 0 and a 0 will enable it if pin 0 has been configured as an output. Bit 0 of the !RB register is the result of the comparison even if output on pin 0 is not enabled by Bit 6

The following example shows common code used to configure the ports:

mov W,#0Fh
mov M,W 	;Set up MODE for Data
		;Direction configuration
mov !RA,#03h 	;RA3, RA2 are Outputs,
		;RA1, RA0 are Inputs
mov W,#0Eh
mov M,W 	;Set up MODE for Pull-Up
		;configuration
mov !RA,#01h 	;RA3, RA1 Unaffected,
		;Pull-up enabled on RA0
mov W,#0Dh
mov M,W 	;Set up MODE for
		;TTL/CMOS configuration
mov !RA,#02h 	;RA3,RA2,RA0 unaffected,
		;RA1 has CMOS voltage level

The following Macro will setup the ports using a simpler command:

in	EQU	$FF
out	EQU	$F0
pull	EQU	$E0
float	EQU	$EF
cmos	EQU	$D0
ttl	EQU	$DF

reghelp	MACRO
	ERROR 'USAGE: reg r[a,b,c] [in,out,pull,float,cmos,ttl] bits'
	ENDM

reg	MACRO
IF \1=RA OR \1=RB OR \1=RC
ELSE
	reghelp
ENDIF
IF \2=in OR \2=out OR \2=pull OR \2=float OR \2=cmos OR \2=ttl
ELSE
	reghelp
ENDIF
	mov w, #(\2 / $10)
	mov m,w
	mov !\1,\3

	ENDM

	noexpand
	reg RA, in, 1

The Read-Modify-Write problem

Michael Rigby-Jones [mrjones@NORTELNETWORKS.COM] says: When you perform any operation, apart from a MOVWF on a register that changes it's contents, the PIC {or SX} first reads the register, then it performs the operation on the number it has just read and finally it write the number back to the register. This is fine when dealing with normal registers and most special function registers. However, if you perform RMW (read modify write) operation on a port register (PORTA PORTB etc) then you are heading for trouble. Why? Because when the PIC reads a port register, it reads the actual state of the pins, rather than the output latch. This can cause two problems:

  1. If the pin is an input, then the input pin state will be read, the operation performed on it, and the result sent to the output latch. This may not immediately cause problems, but if that pin is made into an output later on, the state of the output latch may have changed from the time it was deliberately set by the code.
  2. Now, if the pin is defined as an output, the output latch and the actual pin *ought* to be in the same state. In practice sometimes they aren't. If you are driving a capacitive load, the pin will take time to respond as it charges and discharges the capacitor. A common problem occurs when using the bit set (bsf) or bit clear (bcf) directly on a port.
    bsf portb,0
    bsf portb,1
    

    Which *might* work. However, if pin B0 is loaded in any way, then it may not have time to respond to the first instruction before the second one is executed. The second instruction reads the port and sees that the pin B0 is low (because it hasn't got time to go high) and writes the low state back into the output latch. The result would be that B0 never gets set.

How do you avoid this issue? Well, it's bad practice to use RMW instructions directly on a port. So you use whats known as a shadow register. The shadow register is simply a ram location you reserve. All operations are performed on this register, and when you are finished, you copy it to the port register. It's a bit more trouble, and it can slow things down a tiny bit, but the effort is worth it for reliable operation.

Note that it's not only bsf and bcf that are RMW instructions, although they are the most common. Other examples are

andwf   portb,f
xorwf   portb,f

1