I have a small project for a course I am doing that requires us to produce a PWM signal using PIC assembly language. To try and simplify things I have set the high time to 5ms and the low time to 15ms so I can call the same delay sub routine multiple times. Calling this delay multiple times seems to be causing the problem with the stack underflow.
I am not really sure what I can try to resolve this as I am very fresh to programming. I have tried searching on this site as well as generally but haven't been able to find a solution. I am using MPLab 9.82 as well.
The code is as follows:
list p=16F84A
#include <p16F84A.inc>
__CONFIG _CP_OFF & _XT_OSC & _PWRTE_ON & _WDT_OFF ;turn off watchdog timer
org 0x00 ; program starts at 0x00
counter equ 4Fh ; create a counter at position 4Fh in RAM
BSF STATUS, RP0 ; select bank 1
BCF TRISB, D'1' ; set port B pin 1 to output
BCF STATUS, RP0 ; select bank 0
goto main
main
BSF PORTB,1 ; turn port B pin 1 on
call delay_5ms ; calls sub routine for 5ms delay
BCF PORTB,1 ; turn port B pin 1 off
call delay_5ms ; calls sub routine for 5ms delay
call delay_5ms ; calls sub routine for 5ms delay
call delay_5ms ; calls sub routine for 5ms delay
delay_5ms
movlw D'200' ; put decimal number 200 into working register
movwf counter ; move 200 from working register into counter
lp nop ; no operation. just take 1 instruction
nop ; 1 instruction
decfsz counter ; 1 instruction and decreases counter by 1
goto lp ; 2 instructions (goto takes 2 instructions)
return
end
As Hans Passant pointed out, main needs to have some way to prevent fallthrough. For a detailed explanation of what's going on here, let's look at a C function:
Not the best example, but I needed something that didn't have a
returnstatement. At least, in C there is noreturnstatement, but in assembly this function (assuming it doesn't get inlined) would look like this:A function written in C will always compile to an assembly function with a return instruction (unless the compiler inlines the function), even if your function doesn't have a C
returnstatement.Why is this necessary? Because ASM labels don't exist.
A labeled line of code in an assembly source file is just a reference to a specific memory address. When I have the following code:
The
goto mainis just a hardware abstraction which means "goto whatever memory address main happens to be. Whether the branch is relative or absolute, it matters not-- the assembler does the math for you and replaces themainingoto mainwith the required offset to make the branch take you where you want. However, when your executable is created, themain:(note the colon after, I'm talking about the label itself) is gone. This leads to a few quirks with assembly that don't happen in other languages:Second, and most importantly, there is nothing stopping execution from falling through a lable. The CPU sees
exactly the same as
That being said, you can use fallthrough to your advantage, if for example you want to execute some function a fixed number of times, without the overhead of a loop.