Quote Originally Posted by Bruce
Converting most C programs over to PBP is a fairly simple process. Here's a converted version with blinkng LEDs to indicate which task is currently being run. Change timing to suit whatever osc speed you use.

I left most of the C code in place for reference, and bumped task0 up to 100 ticks so you can actually see the task0 LED blinking.
Code:
' Conversion from C to BASIC
'//multitasking system – handle multiple tasks with one microprocessor

DEFINE OSC 4        ' Adjust this & TMR1 values for your osc freq
DEFINE LOADER_USED 1
DEFINE INTHAND ISR

'Variables for saving state in interrupt handler
wsave	VAR	BYTE $70 system		' Saves W
ssave	VAR	BYTE bank0 system	' Saves STATUS
psave	VAR	BYTE bank0 system	' Saves PCLATH
fsave	VAR	BYTE bank0 system	' Saves FSR

'//task counters used to tell when a task is ready to be executed
'//all these counters are incremented every time a 500us interrupt happens
'//every task has its own counter that is updated every time a 500us interrupt happens
'unsigned int task0_counter=0;
task0_counter VAR BYTE
task0_counter = 0
'unsigned int task1_counter=0;
task1_counter VAR WORD
task1_counter = 0
'unsigned int task2_counter=0;
task2_counter VAR WORD
task2_counter = 0

FALSE CON 0
TRUE CON 1

'//this tells when a task is going to happen again
'//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
'#define TASK0_COUNTER_MAX 1         //high frequency task – every 500us, maybe PWM
'#define TASK1_COUNTER_MAX 2000      //low frequency task – every 1000ms
'#define TASK2_COUNTER_MAX 5000      //low frequency and low priority task, every 2500ms
TASK2_COUNTER_MAX CON 5000
TASK1_COUNTER_MAX CON 2000
TASK0_COUNTER_MAX CON 100

'#define TICKS_BETWEEN_INTERRUPTS 2000
'#define INTERRUPT_OVERHEAD 19
'#define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
'#define TMR1RESET_HIGH TMR1RESET >> 8
'#define TMR1RESET_LOW TMR1RESET & 0xFF
TICKS_BETWEEN_INTERRUPTS CON 2000
INTERRUPT_OVERHEAD CON 19
INTER CON  TICKS_BETWEEN_INTERRUPTS - INTERRUPT_OVERHEAD
TMR1RESET CON $FFFF-INTER '$FFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD)
TMR1RESET_HIGH CON TMR1RESET >> 8
TMR1RESET_LOW CON TMR1RESET & $FF

'//this enables/disables a task
'volatile unsigned char task0_enable=TRUE;
'volatile unsigned char task1_enable=TRUE;
'volatile unsigned char task2_enable=TRUE;
'//this allows tasks triggered by interrupt to run in the background in main()
'volatile unsigned char task2_go=FALSE;
task0_enable VAR BIT
task0_enable = TRUE
task1_enable VAR BIT
task1_enable = TRUE
task2_enable VAR BIT
task2_enable = TRUE
task2_go VAR BIT
task2_go = FALSE

SYMBOL TMR1ON = T1CON.0
SYMBOL TMR1IF = PIR1.0
SYMBOL TMR1IE = PIE1.0
SYMBOL TMR1CS = T1CON.1
SYMBOL T1CKPS0 = T1CON.4
SYMBOL T1CKPS1 = T1CON.5
SYMBOL PEIE = INTCON.6
SYMBOL GIE = INTCON.7

PORTB = 0
TRISB = 0

GOTO Main

setup_multitasking:
'void setup_multitasking(void)
'{
'   //set up tmr1  to interrupt every 500us
    TMR1CS=0;
    T1CKPS0=0;
    T1CKPS1=0;
'/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock).  Timer 1 interrupts when it gets to 0xFFFF or 65535.  Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535.  To test, use simulator to find that its exactly correct*/
'   #define  TICKS_BETWEEN_INTERRUPTS      2000
'   #define  INTERRUPT_OVERHEAD            19
'   #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
'   #define TMR1RESET_HIGH TMR1RESET >> 8
'   #define TMR1RESET_LOW TMR1RESET & 0xFF
    TMR1ON=0;
    TMR1H = TMR1RESET_HIGH
    TMR1L = TMR1RESET_LOW
    TMR1ON=1;
    TMR1IF=0;
    TMR1IE=1;
    PEIE=1;
    GIE=1;
'}
    RETURN

ASM
ISR     ; NOTE: If you're using BASIC in this ISR, then you may need to save
        ; some PBP system vars. Look for Darrel Taylors examples here in the forum.
        
        ; Uncomment the following if the device has less than 2k of code space
		;movwf	wsave			; Save W
		;swapf	STATUS, W		; Swap STATUS to W (swap avoids changing STATUS)
		;clrf	STATUS			; Clear STATUS
		;movwf	ssave			; Save swapped STATUS
		;movf	PCLATH, W		; Move PCLATH to W
		;movwf	psave			; Save PCLATH

; Save the FSR value for later	
		movf	FSR, W			; Move FSR to W
		movwf	fsave			; Save FSR
ENDASM

'void interrupt isr(void)
'{
'   //one tick every 500us at 16Mhz
    IF TMR1IF THEN 'if (TMR1IF)
'   {
'   //set up timer 1 again to interrupt 500us in future
    TMR1IF=0;
    TMR1ON=0;
    TMR1H=TMR1RESET_HIGH;
    TMR1L=TMR1RESET_LOW;
    TMR1ON=1;
      task0_counter = task0_counter +1 'task0_counter++;
      if (task0_counter>=TASK0_COUNTER_MAX) THEN ' //high frequency task – every 1 tick
'      {
            task0_counter=0;
            if (task0_enable==TRUE) THEN
'            {
'                  //do high frequency important task 0, for example PWM
                    TOGGLE 0 ' Toggle PORTB.0
'            }
            ENDIF
'      }
      ENDIF
      task1_counter = task1_counter + 1 'task1_counter++;
      if (task1_counter>=TASK1_COUNTER_MAX) THEN ' //low priority task - every 2000 ticks
'      {
             task1_counter=0;
             if (task1_enable==TRUE) THEN
'            {
'                  //do low frequency yet important task 1
                    TOGGLE 1 ' Toggle PORTB.1
'            }
             ENDIF
'      }
      ENDIF
      
'/*this task takes a long time, 100ms for example, lots of maths.  Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it.  In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/
       task2_counter = task2_counter + 1 'task2_counter++;
      if (task2_counter>=TASK2_COUNTER_MAX) THEN ' //every 250ms
'      {
           task2_counter=0;
           if (task2_enable==TRUE) THEN
'          {
               TOGGLE 2 ' Toggle PORTB.2
' //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
               task2_go=TRUE;
'          }
           ENDIF
'      }
      ENDIF
'    }  //if (TMR1IF)
'} //interrupt routine
  ENDIF
 		
ASM
; Restore FSR, PCLATH, STATUS and W registers
finished
		movf	fsave, W		; retrieve FSR value
		movwf	FSR				; Restore it to FSR
		movf	psave, W		; Retrieve PCLATH value
		movwf	PCLATH			; Restore it to PCLATH
		swapf	ssave, W		; Retrieve the swapped STATUS value (swap to avoid changing STATUS)
		movwf	STATUS			; Restore it to STATUS
		swapf	wsave, F		; Swap the stored W value
		swapf	wsave, W		; Restore it to W (swap to avoid changing STATUS)
		retfie					; Return from the interrupt
EndAsm

Main:
'main()
'{
    'setup_multitasking();
     GOSUB setup_multitasking:

Idle:
      if (task2_go==TRUE) THEN
         task2_go=FALSE;
      ENDIF
      GOTO Idle
'            //take our time, doing heaps of complex maths at our leisure in the background
'      }
'   }
'}
END
Look for some of Darrel Taylors (or Tim Box) interrupt saving routines here in the forum if you're using BASIC commands with assembler interrupts.

Depending on which PBP commands you use, you'll most likely need to save some PBP system variables.

I suspect this would also work fine with ON INTERRUPT, but I hate using it.
THANKS A LOT BRUCE


THANKS BRUCE,
I AM GOING THROUGH YOUR SUGGESTIONS AND HOPE THIS WOULD HELP
TOO MANY PICKERS WHO WISH TO IMPLEMENT MULTITASKING IN THEIR PROJECTS.
I AM A LEARNER AND I WILL DO SOME EXPERIMENTS WITH YOUR ADVICE.
THANKS AGAIN
REGARDS
PRAMOD