Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] PIC Assembly - need help with subroutines

Status
Not open for further replies.

T3STY

Full Member level 4
Full Member level 4
Joined
Apr 17, 2012
Messages
239
Helped
24
Reputation
48
Reaction score
24
Trophy points
1,308
Activity points
3,715
After successfully experimenting with C on the PIC 16F84, I've run into some code weight issues and even some time calibration issues, which made me want to start digging in assembly. I wrote the first straight-trough ASM program, compiled - worked just fine. Now, when I wanted to levelup the experience by working with subroutines I just couldn't get them right.
That might be the dumbest error I've ever made, I'm not sure it's my fault (actually, it's always user/programmer 's fault...) or simulation program's fault (ISIS), but I can't figure out why I'm not able to create any subroutine call. This is the code I'm using:

Code ASM - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PROCESSOR       16F84A
RADIX           DEC
INCLUDE         "P16F84A.INC"
__CONFIG        0x3FF1
 
ORG 0x00
TestRoutine:
        MOVLW 0xFF
        MOVWF PORTA
RETURN
 
CALL TestRoutine
SLEEP
END



Compilation in MPLAB succeeds. But when I run the program in ISIS I get this error:
[PIC16 CORE] PC=0x0003. Stack underflow executing RETURN instruction.
Oh, you don't want a return instruction? Then I won't give it to you. I commented the RETURN instruction and, guess what...
[PIC16 CORE] PC=0x0003. Stack overflow executing CALL instruction.
Oh, ok, you don't want CALLs, you don't want RETURNs.. then you mean you don't want subroutines at all, do you?

I tried writing subroutines in many many ways, I even copy-pasted code from websites (which I suppose it's not written by me, and it's working) but still it won't work. It always fails with the RETURN instruction. And this is crazy because when I make a CALL there has to be a RETURN or I'm possibly stuck in the subroutine without returning where I left.

Please, guys, tell me what's the right way to write subroutines in PIC assembly? I've checked out PIC manuals all over the web and as I said, I wasn't able to understand the mistake I'm making. If you could write me a small working example it would be even better.
Thank you!
 
Last edited:

You have ORG 0 and then put your routine. So the first thing it will do is go into the routine, reach a RETURN and not know where to go - it was never called in the first place.

Put you routine AFTER you main code. You might want to make your main code a loop.

Keith

- - - Updated - - -

Try this:

Code:
	RADIX           DEC
	INCLUDE         "P16F84A.INC"
	__CONFIG        0x3FF1
 
	ORG 0x00
	
	
	banksel TRISA
	clrf TRISA
loop 
	call TestRoutine
	banksel PORTA
	clrf	PORTA
	goto loop
TestRoutine
	banksel	PORTA
    MOVLW 0xFF
    MOVWF PORTA
	RETURN

	end

Keith
 
  • Like
Reactions: T3STY

    T3STY

    Points: 2
    Helpful Answer Positive Rating
Let me get this right... in C and other programming languages, no matter where you define a function (subroutine), it'll always be executed only when a specific call to it is made. You're now telling me that Assembly just goes straight-through and won't consider the code as a subroutine, but just normal code to execute. So if I'm going to write any amount of subroutines I have to make sure they're defined after the "main" (ORG 0x00) code or they'll be getting executed?
 

Correct. Assembler starts at the beginning and runs to the end. If you put anything in the way it will execute it. It is pretty linear. You have to spend a lot of time jumping over stuff - think about how to do an 'if,then'else'!

That is why, after many many years of assembler I use C if I can.

Keith

- - - Updated - - -

Just to add, if you write a simple C equivalent and look at the dissassembly listing you will get an idea of what is involved in converting a simple C statement to assembler. It won't necessarily be the best way, depending on the assembler, processor and level of optimisation, but it will give you some clues.

I am sure I am not alone one here in that I started on processors (not so 'micro') that were very limited and had only 7 instructions. Now that was RISC!

Keith
 
  • Like
Reactions: T3STY

    T3STY

    Points: 2
    Helpful Answer Positive Rating
Hi,

As you will see when you get into more complex code, it is normal to use a Goto main after the Org 0x000.
Normally before your main code there are the Interrupt Vectors so you must jump over them.
( look in the Asm /templates folder for the pic16f84 template which shows what I mean )

Just adding a Goto as shown with you code is all that needed as well as setting up the ports as Keith mentions

Code:
		PROCESSOR       16F84A
		RADIX           DEC
		INCLUDE         "P16F84A.INC"
		__CONFIG        0x3FF1
	 
		ORG 0x00
		goto main
	
		TestRoutine:
	    MOVLW 0xFF
	    MOVWF PORTA
		RETURN
	 
main   	CALL TestRoutine
		SLEEP
		END
 
  • Like
Reactions: T3STY

    T3STY

    Points: 2
    Helpful Answer Positive Rating
OK, I pretty much got it. Actually, I'm not sure about interrupts but I'm not going to use them for next few days, I'll work on basics to make sure I really got it. I guess interrupts will drive me crazy in Assembly - though, in C was pretty easy to manage them.
About C code converted to Assembly, I actually opened some files complied from C to HEX files (the ones I use for ISIS), you can find them in the project directory if you use the HITECH PIC C Compiler. When I opened one of those (for a very minimal C program like "Hello world") I got lost in the thousand of lines involved for such a 10 line C coding... that was shocking for an Assembly newbie like me! For example, a simple C function for writing two nibbles of data on a HD44780 LCD Driver has turned from:

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void LCD_Write(unsigned char c){
    LCD_DB7 = (c >> 7) & 0x01;
    LCD_DB6 = (c >> 6) & 0x01;
    LCD_DB5 = (c >> 5) & 0x01;
    LCD_DB4 = (c >> 4) & 0x01;
    #if LCD_DL == 0   //
        LCD_Clock();  // solo in modalità 4-bit
    #endif            //
    LCD_DB3 = (c >> 3) & 0x01;
    LCD_DB2 = (c >> 2) & 0x01;
    LCD_DB1 = (c >> 1) & 0x01;
    LCD_DB0 = (c >> 0) & 0x01;
    LCD_Clock();
    __delay_ms(1);
}


to.... assembly (mind that I cut out the comment lines...)

Code ASM - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
psect   text225
        file    "C:\Users\T3STY\Progetti\Bromografo\HD44780.c"
        line    3
        global  __size_of_LCD_Write
        __size_of_LCD_Write     equ     __end_of_LCD_Write-_LCD_Write
        
_LCD_Write:     
        opt     stack 5
        bcf     status, 5       ;RP0=0, select bank0
        movwf   (LCD_Write@c)
        line    4
        
l1766:  
        opt asmopt_off
movlw   13
movwf   (??_LCD_Write+0)+0,f
u2867:
decfsz  (??_LCD_Write+0)+0,f
        goto    u2867
opt asmopt_on
 
        line    5
        
l1768:  
        bcf     status, 5       ;RP0=0, select bank0
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   07h
u2515:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2515
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2521
        goto    u2520
        
u2521:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (55/8),(55)&7
        goto    u2534
u2520:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (55/8),(55)&7
u2534:
        line    6
        
l1770:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   06h
u2545:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2545
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2551
        goto    u2550
        
u2551:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (54/8),(54)&7
        goto    u2564
u2550:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (54/8),(54)&7
u2564:
        line    7
        
l1772:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   05h
u2575:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2575
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2581
        goto    u2580
        
u2581:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (53/8),(53)&7
        goto    u2594
u2580:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (53/8),(53)&7
u2594:
        line    8
        
l1774:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   04h
u2605:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2605
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2611
        goto    u2610
        
u2611:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (52/8),(52)&7
        goto    u2624
u2610:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (52/8),(52)&7
u2624:
        line    9
        
l1776:  
        bsf     (51/8),(51)&7
        
l1778:  
        bcf     (51/8),(51)&7
        line    10
        
l1780:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   03h
u2635:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2635
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2641
        goto    u2640
        
u2641:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (55/8),(55)&7
        goto    u2654
u2640:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (55/8),(55)&7
u2654:
        line    11
        
l1782:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   02h
u2665:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2665
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2671
        goto    u2670
        
u2671:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (54/8),(54)&7
        goto    u2684
u2670:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (54/8),(54)&7
u2684:
        line    12
        
l1784:  
        movf    (LCD_Write@c),w
        movwf   (??_LCD_Write+0)+0
        movlw   01h
u2695:
        clrc
        rrf     (??_LCD_Write+0)+0,f
        addlw   -1
        skipz
        goto    u2695
        btfsc   0+(??_LCD_Write+0)+0,0
        goto    u2701
        goto    u2700
        
u2701:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (53/8),(53)&7
        goto    u2714
u2700:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (53/8),(53)&7
u2714:
        line    13
        
l1786:  
        btfsc   (LCD_Write@c),0
        goto    u2721
        goto    u2720
        
u2721:
        bcf     status, 5       ;RP0=0, select bank0
        bsf     (52/8),(52)&7
        goto    u2734
u2720:
        bcf     status, 5       ;RP0=0, select bank0
        bcf     (52/8),(52)&7
u2734:
        line    14
        
l1788:  
        bsf     (51/8),(51)&7
        
l1790:  
        bcf     (51/8),(51)&7
        line    15
        
l171:   
        return
        opt stack 0
GLOBAL  __end_of_LCD_Write
        __end_of_LCD_Write:
        signat  _LCD_Write,4216



Thank you for help, and beware that I'll be back ;)
 

printf isn't a great example - it is notorious for making a tiny program huge, but the LCD routine would help to show you the effort you are saving by writing in C. A better example would be to write a very simple program such as the port toggling example you were trying in assembler to see the difference.

Keith
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top