PIC18 assembly
Assembly translates human-readable instructions into machine code (bytes in the PIC’s Flash). In the UNEXPO course you use MPASM inside MPLAB v8: you write a .ASM file, assemble it, and get a .HEX file to program the microcontroller.
This page completes the Foundations block with Architecture, Registers, and Bits and hexadecimal. You already know what WREG, SFRs, and masks are; here you learn how to write the program line by line.
Program flow — from .ASM to the PIC
Section titled “Program flow — from .ASM to the PIC” Your code (.ASM) ↓ MPASM (assembler) .HEX file ↓ Programmer / MPLAB PIC18F4550 Flash → the chip executes instructionsLater in MPLAB and Proteus you will see the full toolchain. Config bits and the configuration word go in the same .ASM but are stored in a special configuration area, not as normal executable code.
Anatomy of an MPASM file — “sandwich” layout
Section titled “Anatomy of an MPASM file — “sandwich” layout”In the UNEXPO course your instructor asks for a sandwich order in the .ASM, similar to int main() in C:
| Layer | Contents | C analogy |
|---|---|---|
| Top | Header, CONFIG, variables (CBLOCK) | #include, defines, global variables |
| Middle | Subroutines (CALL / RETURN) | Helper functions |
| Bottom | Main program | main() |
| End | END | End of file |
Only at the top goes the reset vector (ORG 0x0000 / 0000H + GOTO programa): the hardware starts at 0000H and must jump to main at the bottom.
; ══ TOP — header, config, variables ═══════════════════ LIST P=18F4550 #include <P18F4550.INC>
CONFIG FOSC = HS CONFIG WDT = OFF CONFIG LVP = OFF
CBLOCK 0x20counter RES 1
; ── Reset vector (jumps to main at the bottom) ─────── ORG 0x0000 GOTO programa
; ══ MIDDLE — subroutines ═════════════════════════════setup_ports: BCF TRISB, 0 ; RB0 output RETURN
turn_on_led: BSF LATB, 0 ; RB0 high RETURN
; ══ BOTTOM — main program (like main) ════════════════programa: CALL setup_ports CALL turn_on_ledloop: GOTO loop ; infinite loop
ENDDirectives you must know
Section titled “Directives you must know”| Directive | Function |
|---|---|
LIST P=18F4550 | Tells the assembler which MCU |
#include <P18F4550.INC> | Imports register and bit names |
CONFIG … | Configuration word (oscillator, WDT…) |
ORG addr | Sets the Flash address where code starts |
CBLOCK / RES | Reserves RAM bytes for GPR variables |
END | Marks end of file |
Labels and comments
Section titled “Labels and comments”- Label (
start:,loop:): name of a Flash location; target ofGOTOorCALL. Not a number — MPASM turns it into an address when assembling. - Comment (
; text): ignored by MPASM — documents your code.
What is a literal
Section titled “What is a literal”A literal is a fixed value written in the source line. It is not a register name (LATB, counter) or a label (programa:): it is the actual number you want to use.
Think of it like a constant in C: the value you already chose and write as-is, not something you read from a RAM variable. If in C you would write result = 10;, in assembly you write MOVLW D'10' — that 10 is the literal (your constant on that line).
In the MPASM manual it appears as k when the instruction embeds a value:
| Manual | Your code | Meaning |
|---|---|---|
MOVLW k | MOVLW D'10' | Load literal 10 into WREG |
ANDLW k | ANDLW 0xF0 | AND between WREG and literal 0xF0 |
ADDLW k | ADDLW B'00000001' | Add literal 1 to WREG |
The assembler encodes that literal into bytes inside the Flash instruction. The PIC does not “look up” a literal in RAM: the value travels inside the opcode.
MPASM notations for literals
Section titled “MPASM notations for literals”MPASM requires prefixes to make the number base explicit. The same value can be written several ways:
| Notation | Example in .ASM | Same value (report) | Base |
|---|---|---|---|
| Decimal | D'200' | 200 | 10 |
| Binary | B'11110000' | 240 | 2 |
Hex (0x) | 0xF0 | F0H | 16 |
| Hex (h suffix) | 0F0h | F0H | 16 in MPASM |
Useful rules when writing literals:
- Decimal: always
D'number'with quotes —D'10',D'255'. - Binary:
B'01010101'— handy for masks (see Bits and hexadecimal). - Hex in
.ASM:0xF0or0F0h(MPASM accepts both; sometimes0FFhavoids confusion with a label). - Byte range: an 8-bit literal goes from 0 to 255 (
0x00…0xFF). Out-of-range values trigger an MPASM error.
Examples in real instructions:
MOVLW D'10' ; WREG = 10 (decimal)MOVLW 0xF0 ; WREG = 240 = F0HMOVLW B'00000001' ; WREG = mask for bit 0 onlyANDLW 0x0F ; WREG = WREG AND 0FH (keep low nibble)BSF LATB, 0 ; the 0 is also a literal: bit numberFor conversion between decimal, binary, and hex (tables, masks, 0x vs FFH), use Bits and hexadecimal. Here the focus is how to write the value inside the .ASM.
Literals in directives (addresses)
Section titled “Literals in directives (addresses)”Directives are not executed on the PIC; they tell MPASM where or how much to reserve. They also use numeric literals:
| Directive | Example | What the literal means |
|---|---|---|
ORG | ORG 0x0000 | Flash address (0000H = reset vector) |
CBLOCK | CBLOCK 0x20 | Starting RAM address for variables (20H) |
LIST | LIST P=18F4550 | Here the “literal” is the MCU name (symbol, not a number) |
ORG 0x0000 ; code starts at 0000H CBLOCK 0x20 ; variables from 20H in RAMcounter RES 1 ; RES 1 = reserve 1 byte (another kind of literal)Same notation idea: 0x0000 = 0000H; 0x20 = 20H. See equivalents in Bits and hexadecimal — hex notations.
The letter f in instructions — what it is and how to use it
Section titled “The letter f in instructions — what it is and how to use it”Before suffixes ,W and ,F, you need a MPASM manual convention that shows up in almost every instruction.
Step 1 — How instructions look in the manual
Section titled “Step 1 — How instructions look in the manual”In documentation, instructions do not use concrete names like LATB or counter. They use the letter f as a slot you fill in:
| Manual instruction | Meaning in plain words |
|---|---|
MOVF f, W | Copy register f into WREG |
MOVWF f | Copy WREG into register f |
INCF f, F | Add 1 to register f and store the result in f |
BSF f, b | Set bit b of register f to 1 |
CLRF f | Clear register f to zero |
Manual f means: “put here whichever file register you need”. In English: file register — any byte in data RAM.
Step 2 — Replace f with the register you need
Section titled “Step 2 — Replace f with the register you need”When you program, you substitute f with the concrete register you want to read, write, or modify:
| Manual (generic) | Your code (concrete) | What it does |
|---|---|---|
MOVF f, W | MOVF LATB, W | Copy LATB → WREG |
MOVF f, W | MOVF counter, W | Copy counter → WREG |
MOVWF f | MOVWF LATB | Copy WREG → LATB |
INCF f, F | INCF counter, F | counter = counter + 1 |
In the line MOVF LATB, W, the manual would write MOVF f, W. In that example, f and LATB are the same thing: the only register involved (besides WREG) is LATB. There is no hidden register called F.
; Manual: MOVF f, W; Your code: MOVF LATB, W; └─┬──┘; └── this is what the manual calls "f"Same with your own variable:
; Manual: INCF f, F; Your code: INCF counter, F; └────┬────┘; └── "f" = counter in this programStep 3 — Which registers can be f
Section titled “Step 3 — Which registers can be f”Any data RAM byte can take the f slot. Two families (see Registers and Architecture):
If you replace f with… | Type | Example use |
|---|---|---|
TRISB, LATB, PORTB, INTCON… | SFR | Control PIC hardware |
counter, temp (with RES) | GPR | Store your program data |
LATB and counter are file registers because they can be the f in an instruction. SFR vs GPR differs by purpose, not by being a “file register”.
Step 4 — Do not confuse f with suffix ,F
Section titled “Step 4 — Do not confuse f with suffix ,F”On one line you may see two different uses of the letter F:
| Symbol | What it is | Example |
|---|---|---|
f (lowercase, from manual) | Which register you use — replace with LATB, counter… | MOVF LATB, W |
,F (suffix at end) | Where the result goes — back into that same register (not WREG) | INCF counter,F |
,W (suffix) | Where the result goes — into WREG | MOVF LATB,W |
Full example broken down:
MOVF LATB, W; └─┬─┘ └┬┘; f destination = WREG (suffix ,W); in real code, f = LATBINCF counter, F; └───┬───┘ └┬┘; f destination = same register (suffix ,F); in real code, f = counterThe next section details ,W vs ,F with more instruction examples.
Operation destination — ,W vs ,F
Section titled “Operation destination — ,W vs ,F”With the previous section clear: in MOVF LATB, W, manual f = LATB in your code; suffix ,W means the result goes to WREG.
Many instructions have two destination forms:
; f = counter; destination ,W → result in WREGMOVF counter, W ; WREG = counter (counter unchanged)
; f = LATB; destination ,WMOVF LATB, W ; WREG = LATB
; f = counter; destination ,F → result in same registerINCF counter, F ; counter = counter + 1Mental rule: ,W → result in WREG; ,F → result in the same register that replaces f (LATB, counter…).
Basic instructions — data movement
Section titled “Basic instructions — data movement”| Instruction | What it does | Example |
|---|---|---|
MOVLW k | Load literal k into WREG | MOVLW D'10' |
MOVWF f | Copy WREG → register f | MOVWF LATB |
MOVF f,d | Copy register f → d (,W or ,F) | MOVF PORTB, W |
CLRF f | Set register f to 0 | CLRF counter |
CLRW | Set WREG to 0 | CLRW |
Typical load-and-store pattern:
MOVLW D'55' ; WREG = 55MOVWF counter ; counter = 55Arithmetic instructions
Section titled “Arithmetic instructions”| Instruction | Operation | Updates STATUS |
|---|---|---|
ADDLW k | WREG = WREG + k | Yes (Z, C, DC, OV, N) |
ADDWF f,d | WREG + f → d | Yes |
SUBLW k | WREG = k − WREG | Yes |
SUBWF f,d | f − WREG → d | Yes |
INCF f,d | f + 1 → d | Yes (Z, N…) |
DECF f,d | f − 1 → d | Yes |
DECFSZ f,d | Decrement; if result is 0, skip next instruction | Yes |
Example — loop for N iterations:
MOVLW D'10'MOVWF counterloop: ; ... work ... DECFSZ counter, F ; counter--; if 0, skip GOTO GOTO loop ; loop ends hereAfter adds and subtracts check STATUS flags: Z (zero?), C (carry?), N (sign).
Logic instructions
Section titled “Logic instructions”| Instruction | Operation |
|---|---|
ANDLW k / ANDWF f,d | Bitwise AND |
IORLW k / IORWF f,d | Bitwise OR |
XORLW k / XORWF f,d | Bitwise XOR |
COMF f,d | Complement (NOT) of f |
Details and masks in Bits and hexadecimal. Practice 1 requires routines with these operations.
Bit instructions — one pin without touching the rest
Section titled “Bit instructions — one pin without touching the rest”| Instruction | Effect |
|---|---|
BSF f,b | Bit b of f → 1 |
BCF f,b | Bit b of f → 0 |
BTFSS f,b | Skip next line if bit = 1 |
BTFSC f,b | Skip next line if bit = 0 |
; Read button on RB3 (input)BSF TRISB, 3 ; RB3 inputBTFSS PORTB, 3 ; RB3 = 1?GOTO button_released ; if RB3 = 1 → jump to destination; ... code if button pressed (RB3 = 0) ...button_released: ; continues if button is released (RB3 = 1)Branches, subroutines, and the stack
Section titled “Branches, subroutines, and the stack”| Instruction | Function |
|---|---|
GOTO label | Unconditional jump — PC goes to label |
CALL sub | Call subroutine — saves return address on stack |
RETURN | Return from CALL — restores PC from stack |
RETFIE | Return from interrupt (ISR) |
; ── subroutine (middle of sandwich) ──delay_1s: ; ... timing loop ... RETURN
; ── main program (bottom, like main) ──programa: CALL delay_1s ; go to delay_1s; save return address BSF LATB, 0 ; on return, turn LED on GOTO programaThe stack is LIFO: the last CALL returns first with RETURN. See Architecture — PC for more detail.
Compare and decide — conditional branches
Section titled “Compare and decide — conditional branches”PIC18 has no CMP instruction. You compare by operating and reading STATUS:
; counter == 0?MOVLW D'0'XORWF counter, W ; if equal → W=0 → Z=1BTFSS STATUS, Z ; skip if Z=1 (counter was 0)GOTO not_zero ; if not zero → jump to destination; counter == 0 ...GOTO zero_donenot_zero: ; counter != 0 ...zero_done:
; RB0 on?BTFSS LATB, 0 ; skip if LATB bit 0 = 1GOTO led_off ; if RB0 = 0 → jump to destination; LED on (RB0 = 1) ...GOTO led_doneled_off: ; LED off (RB0 = 0) ...led_done:| Instruction | Skips if… |
|---|---|
BTFSS STATUS, Z | Result was zero (Z=1) |
BTFSC STATUS, Z | Result was not zero (Z=0) |
BTFSS STATUS, C | There was carry (C=1) |
Full example — LED on RB0
Section titled “Full example — LED on RB0”Translate “turn on an LED on RB0” into concrete steps using GPIO registers:
LIST P=18F4550 #include <P18F4550.INC> CONFIG FOSC = HS, WDT = OFF, LVP = OFF
ORG 0x0000 GOTO programa
; ── (no subroutines in this minimal example) ──
programa: BCF TRISB, 0 ; 1) RB0 as output BSF LATB, 0 ; 2) RB0 high → LED ONloop: GOTO loop ; 3) loop (program never exits)
ENDIn assembly you do not say “turn on an LED.” You say: set pin direction, write the latch, keep the program running.
Ejercicio · Parcial
Trace this fragment
MOVLW D'3'MOVWF counterINCF counter, FDECFSZ counter, FGOTO $-1counterbecomes 3, thenINCF→ 4.DECFSZdecrements: 3 — not zero → no skip.GOTO $-1goes back toDECFSZ.- Repeats until
counter= 0 →DECFSZskips theGOTOand the loop ends.
$-1 is the previous instruction (MPASM implicit label).
Common mistakes
Section titled “Common mistakes”- Forgetting
END: MPASM may error or produce incomplete code. MOVLWvsMOVWF:MOVLWloads WREG;MOVWFstores WREG into a register — not interchangeable.- Writing outputs to
PORTx: useLATxfor outputs; read inputs withPORTx. - Mixing up
BTFSSandBTFSC: one skips if bit is 1, the other if 0 — check button wiring. - Missing
P18F4550.INC: without the include,TRISBandLATBare undefined symbols. - Label without colon (
programainstead ofprograma:): the assembler will not recognize the jump target. - Main program at top, subroutines below: in UNEXPO it is the opposite —
programa:at the bottom, subroutines in the middle (sandwich /mainat the end).
Exam-style questions
Section titled “Exam-style questions”What does f mean in manual MPASM MOVWF f?
It is a placeholder: the manual does not know which register you will use. You write MOVWF LATB or MOVWF counter — f becomes that name. In MOVF LATB, W, f and LATB are the same thing.
What is a literal in MPASM?
A fixed value written in the line (D'10', 0xF0, B'10000000') — like a constant in C — that the assembler embeds in the instruction. It is not a register (counter) or a label (loop:).
What does MOVLW D'5' followed by MOVWF counter do?
Loads 5 into WREG and copies it to GPR variable counter.
What is ORG 0x0000 (0000H) for?
It places code at address 0000H in program memory (reset vector).
What is the difference between GOTO and CALL?
GOTO jumps without saving a return address. CALL jumps to a subroutine and pushes the address to return with RETURN.
What does DECFSZ counter, F do?
Decrements counter; if the result is 0, it skips the next instruction (useful for loops).
MPASM assembly
Based on: instrucciones pic.pdf
0 of 0 answered
Sign in with CALETAS to sync this result across devices.
Sign in with CALETAS