Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 4046811

Who's Online

Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home arrow Conference Proceedings arrow Assembly arrow Assembling for PICs
Assembling for PICs
User Rating: / 1
PoorBest 
Written by Jan Verhoeven   


Below is a piece of assembly language for the MicroChip PIC processor. This particular program will flash some LED's and activate some relays based on the status of some control-inputs. The target MCU was the PIC 16C54, one of the most simple chips in that range.

 

 

To give some indication of what we're upto:

  RAM                25 bytes
ROM               512 words (of 12 bits each)
I/O                12 bits
Clockspeed          8 kHz (this project, max = 4 MHz)
Instructions       33
On-Chip-Stack       2 levels

Compare this to a modern PC clone....

RISC and Harvard architecture.


The PIC line of MCU's are RISC chips, so they use the Harvard architecture, and one of the results is that they have different code- and data-memories.

Higher PIC's have more features, like INTerrupt sources on 4 or more pins, internal interrupts etcetera. All models have a watchdogtimer (WDT) which needs to be reset regularly (if enabled) else the MCU will reset itself.

The PIC registers.


The register architecture of the PIC is somewhat odd to Intel programmer's but programming resembles that of the Hewlett Packard HP 11 range of calculators.

Here is an overview of the registerset. Microchip refers to this as the "register file".

    file address          name                  comment
------------        --------------          --------------------
00              indirect calls          not a real register!
01              RTCC                    timer counter
02              PC (or IP)              lower 8 bits of it
03              STATUS                  flags register
04              FSR                     bank select of PIC 16C57
05              Port A                  has 4 I/O lines
06              Port B                  has 8 I/O lines
07              Port C                  8 I/O, only 16C55 and 16C57
GP register on 'C54 and 'C56
08              GP register             General purpose register
..              ..                      ..
1F              GP register             General purpose register

Besides these "transparant registers" there are also some hidden registers (which also are write only...) for processor control. These are:

        TRISA           The "tristate A/B/C" registers determine the status
TRISB           of each pin of the I/O ports.
TRISC           A "1" makes it "input" and a "0" makes it an output.
OPTION          is for controlling the WDT and the RTCC

And there's the ubiquitous "W" register. This is the "Working register" and is used to haul data back and forth. PIC registers (or "files") cannot process constants (or "literals"). This can only be done with the W-file. It takes some getting used to, but the concept is simple and straightforward and eventually you will get used to it and learn to appreciate it.

From that moment on, you will only have to get used to the fact that data is nbot always ending up where you would like to have it. All instructions between W and F (any register or file) end with a "d" option. If "d" is a "1", the destination is the file F, if "d" is "0", the result will be stored in the W file...
This took me some time to get used to and still is the main source of errors. Apart from having selected the wrong osciallator and not disabling the WDT....

The PIC instructions.


The instructions for the PIC 16C54 are as follows:

mnemonic description


    ADDWF   F, d        d := W + F
ANDLW   k           W := W AND k
ANDWF   F, d        d := W AND F
BCF     F, b        bit b in F is cleared   (i.e. made zero)
BSF     F, b        bit b in F is set       (i.e. made one)
BTFSC   F, b        if bit b in F is CLEAR, skip next instruction
BTFSS   F, b        if bit b in F is SET, skip next instruction
CALL    k           push PC, PC := k
CLRF    F           Clear file F
CLRW                Clear file W
CLRWDT              Clear Watchdogtimer
COMF    F           F := NOT F              (1's complement)
DECF    F, d        d := F - 1
DECFSZ  F, d        d := F - 1; If 0 => skip next instruction
GOTO    k           PC = k
INCF    F, d        d := F + 1
INCFSZ  F, d        d := F + 1; If 0 => skip next instruction
IORLW   k           W := W OR k
IORWF   F, d        d := W OR F
MOVF    F, d        d := F          (zero flag affected)
MOVLW   k           W := k
MOVWF   F           F := W
NOP                 No operation
OPTION              OPTION := W
RETLW   k           W := k, pop PC
RLF     F, d        d := rotate left through carry (F)
RRF     F, d        d := rotate right through carry (F)
SLEEP               enter powerdown mode
SUBWF   F, d        d := F - W              (2's complement)
SWAPF   F, d        d := swap-nibbles (F)
TRIS    F           TRIState information for I/O pins
XORLW   k           W := W XOR k
XORWF   F, d        d := W XOR F

Especially the "F, d" construct takes some getting used to.

Below is the source for the "LEGO controller":


title "LEGO 003"
subtitl "control LEGO technic devices"

LIST P=16C54, R=HEX, F=INHX8M, C=120, E=0, N=80 PIC54 equ 1FFH ; Define Reset Vectors

RTCC    equ     1h              ; define register designators
PC      equ     2h              ; the program counter is a register as well
STATUS  equ     3h              ; F3 Reg is STATUS Reg.
PORT_A  equ     5h
PORT_B  equ     6h              ; I/O Port Assignments
RTCC_tc equ     0Dh             ; time constant for RTCC
count_1 equ     0Eh             ; delay counters and GP registers
count_2 equ     0Fh
file    equ     1
w       equ     0
flag_0  equ     0               ; input bits in RA port
flag_1  equ     1
flag_2  equ     2
flag_3  equ     3
LED_0   equ     0               ; status led 1, in RB Port
LED_1   equ     1               ; status led 2
RL_1    equ     2               ; relays 1 - 3
RL_2    equ     3
RL_3    equ     4
s_clk   equ     5               ; s_clk input
s_data  equ     6               ; s_data input
go      equ     7
delay   movlw   .100            ; mov W with 100 decimal
movwf   count_1         ; xfer W to register
dela_1  clrf    count_2         ; count_2 = 0
dela_2  decfsz  count_2, file   ; count_2 = count_2 - 1
goto    dela_2          ; skip this instruction if count_2 = 0, ...
decfsz  count_1, file   ; ... ending here: count_1 = count_1 - 1
goto    dela_1          ; skip this instruction when count_1 = 0
retlw   0               ; ending here, if so.
flash   bcf     PORT_B, LED_1   ; flash LED's 0 and 1 as an acknowledgement
bsf     PORT_B, LED_0   ; activate the LED's.
call    delay           ; wait a while
bcf     PORT_B, LED_0   ; toggle the LED's
bsf     PORT_B, LED_1
call    delay           ; wait a second!
bcf     PORT_B, LED_1   ; turn LED_1 off as well.
retlw   0               ; return to caller with W = 0
RT_chk  clrwdt                  ; clear the watchdog timer
btfsc   RTCC, 7         ; RELAY_3 follows bit7 of RTCC
bcf     PORT_B, RL_3
btfss   RTCC, 7
bsf     PORT_B, RL_3
movf    RTCC, w
skpz                    ; internal macro for BTFSS  STATUS, 2
retlw   0
movf    RTCC_tc, w      ; if
movwf   RTCC
retlw   0

start clrf RTCC

        clrf    RTCC_tc         ; clear RTCC and RTCC time constant
movlw   B'00001111'
tris    PORT_A          ; define port A as inputs
movlw   B'11100000'
tris    PORT_B          ; define port B as I/O
movlw   B'00110111'
option                  ; define state of WDT, RTCC and prescaler
movlw   B'00011100'
movwf   PORT_B          ; initialize port B
call    flash           ; signal READY
call    flash
btfss   PORT_B, s_clk   ; if s_clkline low, check for mode 2 request
goto    m_chk
repeat  clrwdt                  ; clear watchdog timer
call    flash
movf    PORT_A, w       ; read port A into W
andlw   3               ; mask off sensor inputs
skpnz                   ; skip next instruction if NonZero
goto    set_tc          ; flag_0 and 1 zero => define RTCC time constant
btfsc   PORTA, flag_0
goto    t_left
btfsc   PORT_A, flag_1
goto    t_right
movf    PORT_B, w
andlw   s_clk + s_data + go
skpnz                   ; if no RESET condition, skip
goto    start
call    RT_chk
goto    repeat

t_left btfsc PORT_A, flag_2 ; if in end position, do not turn at all

        goto    l_exit
bcf     PORT_B, RL_1    ; else set direction for Turn Left
bsf     PORT_B, RL_2
bsf     PORT_B, LED_0   ; show direction with LED's
bcf     PORT_B, LED_1
chk_fl2 btfsc   PORT_A, flag_2  ; wait until home-position is reached
goto    l_exit          ; if so, get out
call    RT_chk          ; if not, check again
goto    chk_fl2         ; until done
l_exit  bsf     PORT_B, RL_1    ; release relay 1
bcf     PORT_B, LED_0   ; extinguish light 0
goto    repeat          ; jump back

t_right btfsc PORT_A, flag_3 ; if in end position, do not turn at all

        goto    r_exit
bcf     PORT_B, RL_2    ; else set direction for Turn Right
bsf     PORT_B, RL_1
bsf     PORT_B, LED_1   ; show direction with LED's
bcf     PORT_B, LED_0
chk_fl3 btfsc   PORT_A, flag_3  ; wait until home position reached
goto    r_exit
call    RT_chk
goto    chk_fl3
r_exit  bsf     PORT_B, RL_2    ; deactivate lights and relays
bcf     PORT_B, LED_1
goto    repeat
m_chk   clrf    count_1         ; check inputs and make sure there's no glitch
clrf    count_2
m_chk_1 btfss   PORT_B, s_clk
decf    count_1, file   ; count pulses s_clkline = low
decfsz  count_2, file
goto    m_chk_1
movf    count_1, w      ; w = low-pulses
subwf   count_2, w      ; if count_1 <> count_2, glitch occurred
skpz
goto    start
set_tc  movf    RTCC, w         ; move current value of RTCC
movwf   RTCC_tc         ; to time constant register
goto    repeat
org     PIC54           ; goto highest word in code space
goto    start           ; and place the reset vector.
end

If you ever programmed an HP 11 (or 12, 15 or 16) calculator, the conditional jumps may ring a bell. I don't know how the HP machines handle these jumps, but the PIC line does the following:

      condition         action by PIC
---------         -----------------------------------
FALSE           execute next instruction
TRUE            replace next instruction with a NOP

This enables the programmer to make 100% accurate timingloops since there is no difference between a FALSE and a TRUE condition.

The size of this piece of code is easy to calculate: each line with an mnemonic is one instructionword. This makes 115 words from the 512 word program memoryspace, so we have nearly 400 instructionwords wasted.

The PIC's are marvelous chips to bridge the gap between lots and lots of TTL chips and the overkill of a microcontroller unit with separate RAM, ROM and I/O. If you want to find out more of this kind of CPU's, visit the website at

{http://www.microchip.com}

for PDF datasheets and more. Scenix also has a range of clones out, right now. They are software compatible but offer more hardware features. Which is not difficult since the codeword in the design of the PIC's seemed to have been KISS.