;-- cc1b2sx [rev 0.00.06] 
;-- Tue Aug 28 05:53:23 2007
 

; CC1B Ver 0.6I beta, Copyright (c) B Knudsen Data
; C compiler for the Ubicom SX family
; ************  28. Aug 2007   5:53  *************

        device SX28

FSR_7       EQU   7
;-- INDF        EQU   $00
;-- RTCC        EQU   $01
;-- FSR         EQU   $04
Carry       EQU   0
Zero_       EQU   2
_MainTemp   EQU   $0E
_TaskTemp   EQU   $0B
index       EQU   $1B
addr        EQU   $1B
base        EQU   $19
index_2     EQU   $1A
addr_4      EQU   $19
dest        EQU   $13
source      EQU   $14
len         EQU   $16
i           EQU   $17
k           EQU   $18
TaskToRun   EQU   $3F
TaskCountLSB EQU   $F0
TaskDelay   EQU   $F2
TaskSuspendTicks EQU   $F3
TaskFlags   EQU   $F4
TaskRunning EQU   5
TaskFinished EQU   6
TaskEnabled EQU   7
Task_W      EQU   $FA
Task_M      EQU   $FB
Task_ST     EQU   $FC
Task_FSR    EQU   $FD
Task_PCL    EQU   $FE
Task_PCH    EQU   $FF
slot        EQU   $13
taskid      EQU   $14
interval    EQU   $15
flags       EQU   $16
index_3     EQU   $10
runtime     EQU   $11
delay       EQU   $12
cnt0        EQU   $50
cnt1        EQU   $51
cnt2        EQU   $52
cnt3        EQU   $53
cnt4        EQU   $54
cnt5        EQU   $70

  ; FILE task_test.c
                        ;// File task_test.c --test program for task.h for CC1B compiler
                        ;
                        ;#pragma chip SX28 // select device
	device	TURBO, STACKX, OPTIONX, OSCHS2, BOR42		;sx18/20/28: TURBO, STACKX, OPTIONX, OSCHS2, BOR42
	;device	OSCHS2, XTLBUFD, IFBD, BOR42				;sx48/52: OSCHS2, XTLBUFD, IFBD, BOR42
	irc_cal	irc_fast
	freq	20_000_000
	reset	main
	id	'memtest'
                        ;#define CPUFREQ 20000000
                        ;#define MAXBAUD 57600
                        ;//#define ISRTHREADS 4
                        ;#define INTPERIOD 217
                        ;#include <vpobjDefs.h>
                        ;
                        ;//#pragma mainStack 0 @0xF0	//allocate locals from ram address 0xF0 upwards (use only when <= 16 locals)
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// MACRO DEFINITIONS AND CONSTANTS
                        ;
                        ;// Area for storage of constant data (at end of program code)
                        ;// The size of the area is set by the constant after _CHIP_CODESIZE_
                        ;// The minimal constant value is 1. Set it to the minimal value as required by the application.
                        ;#pragma cdata[_CHIP_CODESIZE_ - 0x100]
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// IO PORT DEFINITIONS
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// GLOBAL VARIABLE DEFINITIONS
                        ;
                        ;#pragma membank0			//allocate globals from global ram
                        ;//define variables that must reside in global ram here
                        ;
                        ;#pragma membank1			//allocate globals from ram address 0x10 upwards
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// PROTOTYPES AND CODEPAGES
                        ;/* Prototypes are needed when a function is called before it
                        ;is defined. It can also be used for fine tuning the code
                        ;layout (i.e. which codepage to use for a function). */
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// INCLUDED C FILES AND MATH ROUTINES
                        ;#pragma codepage 1
        ORG $0200

  ; FILE C:\Program Files\bknd\CC1B\memory.h
                        ;// File memory.h for CC1B
                        ;#ifndef _MEMORY_H_
                        ;#define _MEMORY_H_
                        ;//include other libraries here
                        ;#pragma library 1
                        ;
                        ;shadowDef char _SystemRam[256] @0x00;
                        ;char _MainTemp @0x0E; //used as temporary placeholder for mainlevel code (cc1b already uses 0x0F for temporary storage)
                        ;char _IsrTemp @0x0D; //used as temporary placeholder for isr code
                        ;char _IsrBank @0x0C; //holds rambank for interrupt code
                        ;char _TaskTemp @0x0B; //used as temporary placeholder for tasklevel code
                        ;
                        ;//runtime DDR registers
                        ;char DDRACOPY @0xF5;
                        ;char DDRBCOPY @0xF6;
                        ;#if defined _CHIP_SX28_ || defined _CHIP_SX48_ || defined _CHIP_SX52_
                        ;char DDRCCOPY @0xF7;
                        ;#endif
                        ;#if defined _CHIP_SX48_ || defined _CHIP_SX52_
                        ;char DDRDCOPY @0xF8;
                        ;char DDRECOPY @0xF9;
                        ;#endif
                        ;
                        ;/**
                        ; * Initializes DDR copy registers to 0xFF (same as chip DDR registers)
                        ; */
                        ;void DDRinit() {
DDRinit
                        ;  W = 255;
                        ;  DDRACOPY = W;
                        ;  DDRBCOPY = W;
                        ;#if defined _CHIP_SX28_ || defined _CHIP_SX48_ || defined _CHIP_SX52_
                        ;  DDRCCOPY = W;
                        ;#endif
                        ;#if defined _CHIP_SX48_ || defined _CHIP_SX52_
                        ;  DDRDCOPY = W;
                        ;  DDRECOPY = W;
                        ;#endif
                        ;}
                        ;
                        ;#if defined _CHIP_SX18_ || defined _CHIP_SX20_ || defined _CHIP_SX28_
                        ;
                        ;/**
                        ; * Calculate physical ram address from logical _SystemRam array index (0-143)
                        ; * logical -> physical
                        ; * 0x00-0x0F -> 0x00-0x0F
                        ; * 0x10-0x1F -> 0x10-0x1F
                        ; * 0x20-0x2F -> 0x30-0x3F
                        ; * 0x30-0x3F -> 0x50-0x5F
                        ; * 0x40-0x4F -> 0x70-0x7F
                        ; * 0x50-0x5F -> 0x90-0x9F
                        ; * 0x60-0x6F -> 0xB0-0xBF
                        ; * 0x70-0x7F -> 0xD0-0xDF
                        ; * 0x80-0x8F -> 0xF0-0xFF
                        ; *
                        ; * @param index Logical index into _SystemRam array
                        ; * @return physical ram address
                        ; */
                        ;char _SystemRamAddress(char index) {
_SystemRamAddress
        MOV   index,W
                        ;  W &= 0xF0;
        AND   W,#240
                        ;  if (!Zero_) {
        SNB   3.Zero_
        JMP   m001
                        ;    index += W;
        ADD   index,W
                        ;    index -= 0x10;
        MOV   W,#16
        SUB   index,W
                        ;  }
                        ;  return index;
m001    MOV   W,index
        RET  
                        ;}
                        ;
                        ;/**
                        ; * Calculate logical _SystemRam array index (0-143) from physical ram address
                        ; * physical -> logical
                        ; * 0x00-0x0F -> 0x00-0x0F
                        ; * 0x10-0x1F -> 0x10-0x1F
                        ; * 0x30-0x3F -> 0x20-0x2F
                        ; * 0x50-0x5F -> 0x30-0x3F
                        ; * 0x70-0x7F -> 0x40-0x4F
                        ; * 0x90-0x9F -> 0x50-0x5F
                        ; * 0xB0-0xBF -> 0x60-0x6F
                        ; * 0xD0-0xDF -> 0x70-0x7F
                        ; * 0xF0-0xFF -> 0x80-0x8F
                        ; *
                        ; * @param addr Physical ram address
                        ; * @return logical index into _SystemRam array
                        ; */
                        ;char _SystemRamIndex(char addr) {
_SystemRamIndex
        MOV   addr,W
                        ;  if (addr.4) {
        SB    addr.4
        JMP   m002
                        ;    addr.4 = 0;
        CLRB  addr.4
                        ;	W &= 0x0F;
        AND   W,#15
                        ;    addr += W;
        ADD   addr,W
                        ;    addr = (addr >> 1) + 0x10;
        CLRB  3.Carry
        MOV   W,>>addr
        MOV   addr,W
        MOV   W,#16
        ADD   addr,W
                        ;  }
                        ;  return addr;
m002    MOV   W,addr
        RET  
                        ;}
                        ;
                        ;/**
                        ; * Write value to large array
                        ; * example: LargeArrayWrite(&myArray[0],index,value);
                        ; *
                        ; * @param base Physical ram address (>=0x10) of array element 0
                        ; * @param index Logical index into array
                        ; * @param value Value to write
                        ; */
                        ;#define LargeArrayWrite(base,index,value) \
                        ;  _MainTemp = value; \
                        ;  W = index; \
                        ;  FSR = W; \
                        ;  W &= 0xF0; \
                        ;  FSR += W; \
                        ;  W = base; \
                        ;  FSR += W; \
                        ;  FSR.4 = 1; \
                        ;  INDF = _MainTemp;
                        ;  
                        ;/**
                        ; * Read value from large array
                        ; * example: LargeArrayRead(&myArray[0],index);
                        ; *                 value = W;
                        ; *
                        ; * @param base Physical ram address (>= 0x10) of array element 0
                        ; * @param index Logical index into array
                        ; * @return value
                        ; */
                        ;#define LargeArrayRead(base,index) \
                        ;  W = index; \
                        ;  FSR = W; \
                        ;  W &= 0xF0; \
                        ;  FSR += W; \
                        ;  W = base; \
                        ;  FSR += W; \
                        ;  FSR.4 = 1; \
                        ;  W = INDF;
                        ;  
                        ;/**
                        ; * Calculate physical ram address from array index
                        ; *
                        ; * @param base Physical ram address of array element 0
                        ; * @param index Logical index into array
                        ; * @return Physical ram address of array element index
                        ; */
                        ;char LargeArrayAddress(char base, char index) {
LargeArrayAddress
        MOV   index_2,W
                        ;  return _SystemRamAddress(_SystemRamIndex(base)+index);
        MOV   W,base
        CALL  _SystemRamIndex
        ADD   W,index_2
        JMP   _SystemRamAddress
                        ;}
                        ;
                        ;/**
                        ; * Calculate array index from physical ram address
                        ; *
                        ; * @param base Physical ram address of array element 0
                        ; * @param addr Physical ram address of array element index
                        ; * @return Logical index into array
                        ; */
                        ;char LargeArrayIndex(char base, char addr) {
LargeArrayIndex
                        ;  addr = _SystemRamIndex(addr);
                        ;  return addr - _SystemRamIndex(base);
                        ;}
                        ;
                        ;#endif
                        ;
                        ;#if defined _CHIP_SX48_ || defined _CHIP_SX52_
                        ;
                        ;/**
                        ; * Calculate physical ram address from logical _SystemRam array index (0-255)
                        ; * logical -> physical
                        ; * 0x00-0xFF -> 0x00-0xFF
                        ; *
                        ; * @param index Logical index into _SystemRam array
                        ; * @return physical ram address
                        ; */
                        ;char _SystemRamAddress(char index) {
                        ;  return index;
                        ;}
                        ;
                        ;/**
                        ; * Calculate logical _SystemRam array index (0-255) from physical ram address
                        ; * physical -> logical
                        ; * 0x00-0xFF -> 0x00-0xFF
                        ; *
                        ; * @param addr Physical ram address
                        ; * @return logical index into _SystemRam array
                        ; */
                        ;char _SystemRamIndex(char addr) {
                        ;  return addr;
                        ;}
                        ;
                        ;/**
                        ; * Write value to large array
                        ; * example: LargeArrayWrite(&myArray[0],index,value);
                        ; *
                        ; * @param base Physical ram address (>=0x10) of array element 0
                        ; * @param index Logical index into array
                        ; * @param value Value to write
                        ; */
                        ;#define LargeArrayWrite(base,index,value) \
                        ;  _MainTemp = value; \
                        ;  W = index; \
                        ;  FSR = W; \
                        ;  W = base; \
                        ;  FSR += W; \
                        ;  INDF = _MainTemp;
                        ;  
                        ;/**
                        ; * Read value from large array
                        ; * example: LargeArrayRead(&myArray[0],index);
                        ; *                 value = W;
                        ; *
                        ; * @param base Physical ram address (>= 0x10) of array element 0
                        ; * @param index Logical index into array
                        ; * @return value
                        ; */
                        ;#define LargeArrayRead(base,index) \
                        ;  W = index; \
                        ;  FSR = W; \
                        ;  W = base; \
                        ;  FSR += W; \
                        ;  W = INDF;
                        ;  
                        ;/**
                        ; * Calculate physical ram address from array index
                        ; *
                        ; * @param base Physical ram address of array element 0
                        ; * @param index Logical index into array
                        ; * @return Physical ram address of array element index
                        ; */
                        ;char LargeArrayAddress(char base, char index) {
                        ;  return base + index;
                        ;}
                        ;
                        ;/**
                        ; * Calculate array index from physical ram address
                        ; *
                        ; * @param base Physical ram address of array element 0
                        ; * @param addr Physical ram address of array element index
                        ; * @return Logical index into array
                        ; */
                        ;char LargeArrayIndex(char base, char addr) {
                        ;  return addr - base;
                        ;}
                        ;
                        ;#endif
                        ;
                        ;/**
                        ; * Read word from codepage (rom)
                        ; *
                        ; * @param addr Physical rom address
                        ; *                        SX18/20/28: 0x000-0x7FF
                        ; *                        SX48/52: 0x000-0xFFF
                        ; * @return Word (12bits) at addr
                        ; */
                        ;long RomWord(long addr) {
RomWord
                        ;  MODE = addr.high8;
                        ;  W = addr.low8;
                        ;  iread();
                        ;  addr.low8 = W;
                        ;  addr.high8 = MODE;
                        ;  return addr;
                        ;}
                        ;
                        ;/**
                        ; * Read char from codepage (rom)
                        ; *
                        ; * @param addr Physical rom address
                        ; *                        SX18/20/28: 0x000-0x7FF
                        ; *                        SX48/52: 0x000-0xFFF
                        ; * @return Char (8bits) at addr
                        ; */
                        ;char RomChar(long addr) {
RomChar
                        ;  MODE = addr.high8;
        MOV   W,addr_4+1
        MOV   M,W
                        ;  W = addr.low8;
        MOV   W,addr_4
                        ;  iread();
        IREAD
                        ;  return W;
        RET  
                        ;}
                        ;
                        ;/**
                        ; * Copy chars from codepage (rom) to ram
                        ; *
                        ; * @param dest Physical ram address
                        ; * @param source Physical rom address
                        ; *                            SX18/20/28: 0x000-0x7FF
                        ; *                            SX48/52: 0x000-0xFFF
                        ; * @param len Number of bytes to copy
                        ; */
                        ;void RomCopy(char dest, long source, char len) {
RomCopy
        MOV   len,W
                        ;  char i = 0;
        CLR   i
                        ;  while (len > 0) {
m003    TEST  len
        SNB   3.Zero_
        JMP   m004
                        ;    _MainTemp = RomChar(source);
        MOV   W,source
        MOV   addr_4,W
        MOV   W,source+1
        MOV   addr_4+1,W
        CALL  RomChar
        MOV   _MainTemp,W
                        ;	char k = LargeArrayAddress(dest,i);
        MOV   W,dest
        MOV   base,W
        MOV   W,i
        CALL  LargeArrayAddress
        MOV   k,W
                        ;	_SystemRam[k] = _MainTemp;
        MOV   FSR,W
        MOV   W,_MainTemp
        MOV   INDF,W
                        ;    FSR = &source; //select correct bank
        MOV   W,#20
        MOV   FSR,W
                        ;	source++;
        CLR   FSR
        INC   source
        SNB   3.Zero_
        INC   source+1
                        ;	i++;
        INC   i
                        ;	len--;
        DEC   len
                        ;  }
        JMP   m003
                        ;}
m004    RET  

  ; FILE C:\Program Files\bknd\CC1B\Task.h
                        ;// File Task.h for CC1B
                        ;#ifndef _TASK_H_
                        ;#define _TASK_H_
                        ;//include other libraries here
                        ;#pragma library 1
                        ;
                        ;/*
                        ;The taskswitching mechanism is NOT multitasking. It allows subroutines to run automatically at specified intervals.
                        ;The interrupt routine monitors when it is time to run a task. Then a contextswitch is done, so that after the interrupt
                        ;routine exits, a task routine is executed, rather than the mainlevel code that was interrupted. The interrupts remain
                        ;active while the task runs. Only after the task exits, will the mainlevel code continue (after another contextswitch).
                        ;It is therefore imperative that a task never blocks or waits for an event. If it must wait, let it set a flag and return.
                        ;The next time it runs, it can then check the flag.
                        ;The SX secret instructions are used to perform the contextswitch.
                        ;
                        ;For clarity, I defined TASK which equals void. It allows you to use
                        ;TASK myTask() {
                        ;}
                        ;Note that task routines appear to CC1B as part of the interrupt call tree, although the task routines
                        ;themselves execute as mainlevel code. You should never call a TASK subroutine from normal mainlevel code.
                        ;
                        ;Here are some guide 'rules' to determine the correct tasks parameters.
                        ;The goal is to LET EACH TASK START ON TIME.
                        ;In the following, T is the number of tasks, K is the taskfactor.
                        ;
                        ;Since there are T tasks running, the task interval value must be a multiple of T: interval = K*T where K>=1.
                        ;The task that has the longest runtime, sets K. For example, if the longest runtime is 2.4 taskticks then K = 3.
                        ;If you increase TASKTICK, then you can decrease TASKFACTOR (eg. K). Note that due to the number
                        ;of cycles used for task entry and exit code, TASKTICKS*TASKFACTOR must be 2 or greater.
                        ;
                        ;   All tasks must have an interval equal to N*K*T and must return within K taskticks and should
                        ;   start K taskticks apart. This ensures tasks do not overlap and are evenly spaced in time and start on time.
                        ;
                        ;You are not bound by the above rule. You can set the interval to any non-zero value (1-255) but then it may
                        ;happen that tasks are at some point scheduled for the same time and then the tasks are not deterministic.
                        ;Tasks scheduled for the same time are simply executed after each other without returning to the mainlevel
                        ;code inbetween.
                        ;
                        ;If N>1 then a task does not use all its timeslices. Unused timeslices are not available to other
                        ;tasks but are used for mainloopcode. If taskcode takes too much time, a statemachine can be
                        ;used to make a task return in time. If a task should run at a lower rate than set by its interval
                        ;parameter, the task can decrement a counter and return immediately if it has not reached 0.
                        ;
                        ;Since the interval parameter is an 8bit values, the highest K calculates from 1+(4*K) = 255. This yields Kmax = 63
                        ;INTPERIOD determines the interrupt tick: isrtick = INTPERIOD/CPUFREQ
                        ;TASKTICK determines the tasktick: tasktick = TASKTICK*isrtick
                        ;The maximum value for TASKTICK is 65279
                        ;
                        ;Example: suppose you want to run a task 1000 times per second, with given CPUFREQ=20_000_000 and INTPERIOD=217
                        ;isrtick = INTPERIOD/CPUFREQ = 217/20_000_000 = 10.85 uSec
                        ;tasktick = TASKTICK * 10.85 uSec = (1/1000) Sec = 1000 uSec
                        ;TASKTICK = 1000/10.85 = 92 with interval = 1
                        ;But you can also select TASKTICK = 46 with interval = 2
                        ;or TASKTICK = 23 with interval = 4
                        ;Normally the task that must run at the highest rate determines TASKTICK
                        ;and interval is adjusted for tasks that run at a lower rate.
                        ;*/
                        ;
                        ;#ifndef TASKS
                        ;#error TASKS (1-5) needs to be defined before including <Task.h>
                        ;#else
                        ;#if (TASKS < 1) || (TASKS > 5)
                        ;#error TASKS needs to be in range 1 to 5
                        ;#endif
                        ;#endif
                        ;
                        ;#ifndef TASKFACTOR
                        ;#define TASKFACTOR 1
                        ;#endif
                        ;
                        ;#ifndef TASKLIST
                        ;#error TASKLIST needs to be defined before including <Task.h>
                        ;#else
                        ;#if (16 - (TASKLIST & 0x0F)) < (3*TASKS+1)
                        ;#error TASKLIST value too high, TaskList array not in single bank
                        ;#endif
                        ;#endif
                        ; 
                        ;#define TASK void
                        ;#define TASKSTART 0x80
                        ;#define TASKRUNONCE 0x40
                        ;
                        ;char TaskList[3*TASKS+1] @TASKLIST;
                        ;  char TaskId[TASKS] @TaskList[0]; //b7=0: task suspended, b7=1:task enabled, b6-b0: taskid
                        ;  char TaskInterval[TASKS] @TaskList[TASKS];
                        ;  char TaskRunTick[TASKS] @TaskList[2*TASKS];
                        ;  char TaskToRun @TaskList[3*TASKS];
                        ;
                        ;#define TASKKERNEL 0xF0
                        ;char TaskCountLSB @0xF0; //16bits divisor to keep track when contextswitch must occur
                        ;char TaskCountMSB @0xF1;
                        ;char TaskDelay @0xF2; //number of taskticks to wait before task runs, or number of taskticks passed while task runs
                        ;char TaskSuspendTicks @0xF3; //number of taskticks that a task can suspend itself
                        ;char TaskFlags @0xF4; //lownib increments every tasktick until b4 set
                        ;  bit TaskPauseDone @TaskFlags.4; //set if TaskPause completed
                        ;  bit TaskRunning @TaskFlags.5; //set if task code is currently running
                        ;  bit TaskFinished @TaskFlags.6; //set if task code finished
                        ;  bit TaskEnabled @TaskFlags.7; //set if tasks are allowed to run
                        ;char Task_W @0xFA; //mainlevel code save area
                        ;char Task_M @0xFB;
                        ;char Task_ST @0xFC;
                        ;char Task_FSR @0xFD;
                        ;char Task_PCL @0xFE; // mainlevel code return address
                        ;char Task_PCH @0xFF;
                        ;
                        ;#pragma cdata.TASKDATA
                        ;#if TASKS == 1
                        ;#pragma cdata[] = 0
                        ;#pragma cdata[] = TASKS*TASKFACTOR
                        ;#pragma cdata[] = 1+(0*TASKFACTOR)
                        ;#elif TASKS == 2
                        ;#pragma cdata[] = 0,1
                        ;#pragma cdata[] = TASKS*TASKFACTOR,TASKS*TASKFACTOR
                        ;#pragma cdata[] = 1+(0*TASKFACTOR),1+(1*TASKFACTOR)
                        ;#elif TASKS == 3
                        ;#pragma cdata[] = 0,1,2
                        ;#pragma cdata[] = TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR
                        ;#pragma cdata[] = 1+(0*TASKFACTOR),1+(1*TASKFACTOR),1+(2*TASKFACTOR)
                        ;#elif TASKS == 4
                        ;#pragma cdata[] = 0,1,2,3
                        ;#pragma cdata[] = TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR
                        ;#pragma cdata[] = 1+(0*TASKFACTOR),1+(1*TASKFACTOR),1+(2*TASKFACTOR),1+(3*TASKFACTOR)
                        ;#elif TASKS == 5
                        ;#pragma cdata[] = 0,1,2,3,4
                        ;#pragma cdata[] = TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR,TASKS*TASKFACTOR
                        ;#pragma cdata[] = 1+(0*TASKFACTOR),1+(1*TASKFACTOR),1+(2*TASKFACTOR),1+(3*TASKFACTOR),1+(4*TASKFACTOR)
                        ;#endif
                        ;#pragma cdata[] = 0
                        ;#pragma cdata[] = 1,1,0,0,0x10
                        ;
                        ;/**
                        ; * Initialize task scheduler.
                        ; * This function is to be called from mainlevel code.
                        ; * It must be called before any of the other task functions.
                        ; */
                        ;void TaskInit() {
TaskInit
                        ;  RomCopy(TaskList,TASKDATA,3*TASKS+1);
        MOV   W,#48
        MOV   dest,W
        CLR   source
        MOV   W,#7
        MOV   source+1,W
        MOV   W,#16
        CALL  RomCopy
                        ;  RomCopy(TASKKERNEL,TASKDATA+(3*TASKS+1),5);
        MOV   W,#240
        MOV   dest,W
        MOV   W,#16
        MOV   source,W
        MOV   W,#7
        MOV   source+1,W
        MOV   W,#5
        JMP   RomCopy
                        ;}
                        ;
                        ;/**
                        ; * Enable the task scheduler.
                        ; * Tasks are allowed to run.
                        ; */
                        ;void TaskEnable() {
TaskEnable
                        ;  TaskEnabled = 1;
        SETB  244.TaskEnabled
                        ;}
        RET  
                        ;
                        ;/**
                        ; * Disable the task scheduler.
                        ; * No tasks will run.
                        ; */
                        ;void TaskDisable() {
TaskDisable
                        ;  TaskEnabled = 0;
                        ;}
                        ;
                        ;/**
                        ; * Set task from mainlevel code.
                        ; * The task parameters are setup in the TaskList.
                        ; * This function is to be called from mainlevel code.
                        ; *
                        ; * @param slot Index in TaskList
                        ; * @param taskid Identifier for task subroutine (may be ORed with TASKSTART and/or TASKRUNONCE)
                        ; * @param interval Specifies in taskticks (>=1) how frequently the task runs
                        ; *                               Do not use value 0
                        ; */
                        ;void TaskSet(char slot, char taskid, char interval) {
TaskSet
        MOV   interval,W
                        ;  char flags = TaskFlags & 0x80; //get current enable state
        MOV   W,#128
        BANK  224
        AND   W,TaskFlags
        BANK  0
        MOV   flags,W
                        ;  TaskEnabled = 0; //disable tasks
        BANK  224
        CLRB  244.TaskEnabled
                        ;  _MainTemp = interval;
        BANK  0
        MOV   W,interval
        MOV   _MainTemp,W
                        ;  TaskInterval[slot] = _MainTemp;
        MOV   W,#53
        ADD   W,slot
        MOV   FSR,W
        MOV   W,_MainTemp
        MOV   INDF,W
                        ;  _MainTemp = taskid;
        BANK  0
        MOV   W,taskid
        MOV   _MainTemp,W
                        ;  TaskId[slot] = _MainTemp;
        MOV   W,#48
        ADD   W,slot
        MOV   FSR,W
        MOV   W,_MainTemp
        MOV   INDF,W
                        ;  TaskFlags |= flags; //restore enable state
        BANK  0
        MOV   W,flags
        BANK  224
        OR    TaskFlags,W
                        ;}
        BANK  0
        RET  
                        ;
                        ;/**
                        ; * Set task from task subroutine.
                        ; * The task parameters are setup in the TaskList.
                        ; * This function is to be called from a task subroutine.
                        ; *
                        ; * @param slot Index in TaskList
                        ; * @param taskid Identifier for task subroutine (may be ORed with TASKSTART and/or TASKRUNONCE)
                        ; * @param interval Specifies in taskticks (>=1) how frequently the task runs
                        ; *                               Do not use value 0
                        ; */
                        ;void TaskChange(char slot, char taskid, char interval) {
TaskChange
                        ;  _TaskTemp = interval;
                        ;  TaskInterval[slot] = _TaskTemp;
                        ;  _TaskTemp = taskid;
                        ;  TaskId[slot] = _TaskTemp;
                        ;}
                        ;
                        ;/**
                        ; * Get slot for running task.
                        ; * This function is to be called from a task subroutine.
                        ; *
                        ; * @return slot for running task
                        ; */
                        ;char TaskRun() {
TaskRun
                        ;  return TaskToRun;
                        ;}
                        ;
                        ;/**
                        ; * Get slot for specified task.
                        ; * This function is to be called from mainlevel code.
                        ; *
                        ; * @param taskid Task identifier for task subroutine
                        ; * @return slot for specified task, or 0xFF if slot not found (eg. task not set)
                        ; */
                        ;char TaskSlot(char taskid) {
TaskSlot
                        ;  char i;
                        ;  taskid &= 0x3F; //mask off TASKSTART and TASKRUNONCE bits
                        ;  for (i=0; i<TASKS; i++) {
                        ;    if ((TaskId[i] & 0x3F) == taskid) return i;
                        ;  }
                        ;  return 0xFF;
                        ;}
                        ;
                        ;/**
                        ; * Get the current task timer value.
                        ; *
                        ; * @return Current task timer value
                        ; */
                        ;char TaskTimer() {
TaskTimer
                        ;#pragma update_FSR 0
                        ;  FSR = TaskRunTick;
                        ;  W = TaskToRun;
                        ;  FSR += W;
                        ;  W = INDF;         // W = TaskRunTick[TaskToRun]
                        ;  FSR = 0;
                        ;  FSR = ~FSR;       // FSR =0xFF (eg. bank TASKKERNEL)
                        ;  if (TaskRunning) {
                        ;    // TaskDelay holds passed ticks for current task -> runtime = TaskRunTick[TaskToRun] + TaskDelay
                        ;    W += TaskDelay; // W = TaskRunTick[TaskTorun] + TaskDelay
                        ;  }
                        ;  else {
                        ;    // TaskDelay holds ticks that must pass before next task runs -> runtime= TaskRunTick[TaskToRun] - TaskDelay
                        ;	W = TaskDelay-W; // W = TaskDelay - TaskRunTick[TaskToRun]
                        ;    W = ~W;          // W = ~(TaskDelay - TaskRunTick[TaskToRun])
                        ;    FSR = 0;
                        ;    FSR++;           // FSR = 1
                        ;    W += FSR;        // W = ~(TaskDelay - TaskRunTick[TaskToRun]) + 1 = TaskRunTick[TaskToRun] - TaskDelay
                        ;  }
                        ;#pragma update_FSR 1
                        ;  return W;
                        ;}
                        ;
                        ;/**
                        ; * Pause for a number of taskticks.
                        ; * This function is to be called from mainlevel code.
                        ; *
                        ; * @param W Number of taskticks to pause (1-255)
                        ; */
                        ;void TaskPauseLong(char ticks) {
TaskPauseLong
                        ;  char start = TaskTimer();
                        ;  char stop = start+ticks;
                        ;  bit p1,p2;
                        ;  do {
                        ;    char current = TaskTimer();
                        ;    p1 = (current >= stop);
                        ;    p2 = (current < start);
                        ;    if (start < stop) {
                        ;      p1 |= p2;
                        ;    }
                        ;    else {
                        ;      p1 &= p2;
                        ;    }
                        ;  } while (!p1);
                        ;}
                        ;
                        ;/**
                        ; * Pause for a number of taskticks.
                        ; *
                        ; * @param W Number of taskticks to pause (1-15)
                        ; */
                        ;void TaskPause(char W) {
TaskPause
                        ;  if (W) {
                        ;    W = ~W;
                        ;    W &= 0x0F;
                        ;    TaskFlags |= W;
                        ;	TaskFlags++;
                        ;    TaskPauseDone = 0;
                        ;    while (!TaskPauseDone) ;
                        ;  }
                        ;}
                        ;
                        ;/**
                        ; * Start a task.
                        ; *
                        ; * @param W Index in TaskList
                        ; */
                        ;void TaskStart(char W) {
TaskStart
                        ;  FSR = W;
        MOV   FSR,W
                        ;  FSR += TaskId;
        MOV   W,#48
        ADD   FSR,W
                        ;  INDF.7 = 1;
        SETB  INDF.7
                        ;}
        RET  
                        ;
                        ;/**
                        ; * Stop a task.
                        ; *
                        ; * @param W Index in TaskList
                        ; */
                        ;void TaskStop(char W) {
TaskStop
                        ;  FSR = W;
                        ;  FSR += TaskId;
                        ;  INDF.7 = 0;
                        ;}
                        ;
                        ;/**
                        ; * Reschedule a task.
                        ; * This function is to be called from a task subroutine.
                        ; *
                        ; * @param W Number of taskticks after which the task runs again once it is finished
                        ; *                     A non-zero value will reschedule the task using that value instead of the task interval value.
                        ; *                     The interval value however is not overwritten. This allows a task to run more often if required.
                        ; */
                        ;void TaskReschedule(char W) {
TaskReschedule
                        ;  TaskSuspendTicks = W;
                        ;}
                        ;
                        ;/**
                        ; * Get task status.
                        ; *
                        ; * @param W Index in TaskList
                        ; * @return Status byte
                        ; *                b7 set if task enabled
                        ; *                b6 set if task set as runonce
                        ; *                b5-b0: task id (0-63)
                        ; */
                        ;char TaskStatus(char W) {
TaskStatus
                        ;  FSR = W;
                        ;  FSR += TaskId;
                        ;  W = INDF;
                        ;  return W;
                        ;}
                        ;
                        ;#pragma library 0
                        ;extern void TaskSwitch(char W); //application supplied function
                        ;
                        ;/**
                        ; * The mainlevel part of the task scheduler
                        ; * It executes outside the isr, but is only called from the isr.
                        ; * This routine calls the function TaskSwitch that must be supplied by the application.
                        ; * It is important to realize that this function in effect interrupts the mainlevel code.
                        ; */
                        ;void TaskSchedule() {
TaskSchedule
                        ;  char index,runtime,delay;
                        ;  // start task code (interrupts mainlevel code, executes outside isr)
                        ;  nop(); //in case interrupt occured in middle of skip instruction
        NOP  
                        ;  FSR = TASKKERNEL;
        MOV   W,#240
        MOV   FSR,W
                        ;  W = MODE; //save m
        MOV   W,M
                        ;  Task_M = W;
        BANK  224
        ;CLRB  4.FSR_7
        MOV   Task_M,W
                        ;Task_run:
                        ;  TaskSuspendTicks = 0; //may be set by task using TaskSuspend()
m005    CLR   TaskSuspendTicks
                        ;  index = TaskId[TaskToRun];
        MOV   W,#48
        BANK  32
        ADD   W,TaskToRun
        MOV   FSR,W
        MOV   W,INDF
        BANK  0
        MOV   index_3,W
                        ;  if (index.7 == 1) { //only run task if enabled
        SB    index_3.7
        JMP   m006
                        ;    TaskSwitch(index);
        PAGE  0
        CALL  TaskSwitch
                        ;	if (index.6) TaskId[TaskToRun].7 = 0; //if runonce, stop task after run
        SB    index_3.6
        JMP   m006
        MOV   W,#48
        BANK  32
        ADD   W,TaskToRun
        MOV   FSR,W
        CLRB  INDF.7
                        ;  }
                        ;  //reschedule current task
                        ;  runtime = TaskRunTick[TaskToRun]; //current time
m006    MOV   W,#58
        BANK  32
        ADD   W,TaskToRun
        MOV   FSR,W
        MOV   W,INDF
        BANK  0
        MOV   runtime,W
                        ;  W = TaskSuspendTicks;
        BANK  224
        MOV   W,TaskSuspendTicks
                        ;  if (!W) {
        SB    3.Zero_
        JMP   m007
                        ;	W = TaskInterval[TaskToRun];
        MOV   W,#53
        BANK  32
        ADD   W,TaskToRun
        MOV   FSR,W
        MOV   W,INDF
                        ;  }
                        ;  _TaskTemp = W + runtime;
m007    BANK  0
        ADD   W,runtime
        MOV   _TaskTemp,W
                        ;  TaskRunTick[TaskToRun] = _TaskTemp;
        MOV   W,#58
        BANK  32
        ADD   W,TaskToRun
        MOV   FSR,W
        MOV   W,_TaskTemp
        MOV   INDF,W
                        ;  //find next task to run
                        ;  delay = 255;
        MOV   W,#255
        BANK  0
        MOV   delay,W
                        ;  for (index=0; index<TASKS; index++) {
        CLR   index_3
m008    MOV   W,#5
        MOV   W,index_3-W
        SNB   3.Carry
        JMP   m010
                        ;	W = TaskRunTick[index];
        MOV   W,#58
        ADD   W,index_3
        MOV   FSR,W
        MOV   W,INDF
                        ;    _TaskTemp = W - runtime;
        MOV   _TaskTemp,W
        BANK  0
        MOV   W,runtime
        SUB   _TaskTemp,W
                        ;    if (_TaskTemp < delay) {
        MOV   W,delay
        MOV   W,_TaskTemp-W
        SNB   3.Carry
        JMP   m009
                        ;      delay = _TaskTemp;
        MOV   W,_TaskTemp
        MOV   delay,W
                        ;      TaskToRun = index; //next time that TaskSchedule runs, the task in TaskToRun is called
        MOV   W,index_3
        BANK  32
        MOV   TaskToRun,W
                        ;	}
                        ;  }
        BANK  0
m009    INC   index_3
        JMP   m008
                        ;  if (delay <= TaskDelay) { //next task was scheduled for same time, or for later time but taskticks are missed so it must run now
m010    MOV   W,delay
        BANK  224
        MOV   W,TaskDelay-W
        SB    3.Carry
        JMP   m011
                        ;    TaskDelay = TaskDelay - delay;
        BANK  0
        MOV   W,delay
        BANK  224
        SUB   TaskDelay,W
                        ;    goto Task_run; //no contextswitch, but immediately run the next task
        JMP   m005
                        ;  }
                        ;  else {
                        ;    TaskDelay = delay - TaskDelay; //next task scheduled for later time
m011    MOV   W,TaskDelay
        BANK  0
        MOV   W,delay-W
        BANK  224
        MOV   TaskDelay,W
                        ;  }
                        ;  // exit task code (contextswitch to run mainlevel code)
                        ;#pragma update_FSR 0
                        ;  FSR = TASKKERNEL;
        MOV   W,#240
        MOV   FSR,W
                        ;  W = Task_M; //restore m
        MOV   W,Task_M
                        ;  MODE = W;
        MOV   M,W
                        ;  W = 25; //check if rtcc will overrun when doing contextswitch directly
        MOV   W,#25
                        ;  //-------------------------
                        ;  W += RTCC; // 21+4 cycles are required to do context switch now (leaves new rtcc @mainlevel <= $FC)
        ADD   W,RTCC
                        ;  TaskFinished = 1;
        SETB  244.TaskFinished
                        ;  if (Carry) { //carry clear if enough cycles left to do context switch now
        SNB   3.Carry
                        ;Task_exit_wait:
                        ;    goto Task_exit_wait; //wait for interrupt to do contextswitch
m012    JMP   m012
                        ;  }
                        ;  //task has finished, restore registers from outside isr
                        ;  W = Task_W; //restore w
        MOV   W,Task_W
                        ;#asm
                        ;  DW 0x48 ;PUSHW ;1
        DW    $48
                        ;#endasm
                        ;  W = Task_ST; //restore st
        MOV   W,Task_ST
                        ;#asm
                        ;  DW 0x49 ;PUSHST ;1
        DW    $49
                        ;#endasm
                        ;  W = Task_FSR; //restore fsr
        MOV   W,Task_FSR
                        ;#asm
                        ;  DW 0x4A ;PUSHFSR ;1
        DW    $4A
                        ;#endasm
                        ;  W = Task_PCH; //restore pc
        MOV   W,Task_PCH
                        ;  MODE = W;
        MOV   M,W
                        ;  W = Task_PCL;
        MOV   W,Task_PCL
                        ;#asm
                        ;  DW 0x4B ;PUSHPC ;1
        DW    $4B
                        ;#endasm
                        ;  W = Task_M; //restore m
        MOV   W,Task_M
                        ;  MODE = W;
        MOV   M,W
                        ;  TaskRunning = 0;
        CLRB  244.TaskRunning
                        ;  TaskFinished = 0;
        CLRB  244.TaskFinished
                        ;#asm
                        ;  DW 0x0E ;RETI ;3 //continue to execute mainlevel code (leaving rtcc <= $FC)
        DW    $E
                        ;#endasm
                        ;#pragma update_FSR 1
                        ;}
        RET  
                        ;
                        ;/**
                        ; * The interrupt part of the task scheduler
                        ; * It must run at the end of the interrupt routine.
                        ; * If a task must run, it redirects program control to TaskSchedule().
                        ; *
                        ; * @param W physical ram bank address
                        ; */
                        ;void Task_isr(char W) {
Task_isr
                        ;  if (0) TaskSchedule(); //this line is required to make cc1b allocate the locals for TaskSchedule(), generates cc1b warning
        JMP   m013
        CALL  TaskSchedule
                        ;#pragma update_FSR 0
        BANK  224
        ;CLRB  4.FSR_7
                        ;  FSR = W;
m013    MOV   FSR,W
                        ;  TaskCountLSB--;
        DECSZ TaskCountLSB
                        ;  if (!Zero_) goto Task_contextswitch;
        JMP   m014
                        ;#if TASKTICK > 255
                        ;  TaskCountMSB--;
                        ;  if (!Zero_) goto Task_contextswitch;
                        ;  TaskCountMSB = (TASKTICK+256) >> 8;
                        ;#endif
                        ;  TaskCountLSB = (TASKTICK+256) & 0xFF;
        MOV   W,#2
        MOV   TaskCountLSB,W
                        ;  if (!TaskFlags.4) TaskFlags++;
        SB    TaskFlags.4
        INC   TaskFlags
                        ;  TaskDelay++; //if task running then increment taskdelay, else taskdelay decrements and remains 0 if it is time to do taskswitch
        INC   TaskDelay
                        ;  if (TaskRunning) goto Task_contextswitch; //task already running
        SNB   244.TaskRunning
        JMP   m014
                        ;  TaskDelay--;
        DECSZ TaskDelay
                        ;  if (!Zero_) {
                        ;    TaskDelay--;
        DEC   TaskDelay
                        ;  }
                        ;  if (TaskDelay) goto Task_contextswitch; // not time to run next task
        TEST  TaskDelay
        SB    3.Zero_
        JMP   m014
                        ;  if (!TaskEnabled) goto Task_contextswitch; //task scheduler disabled
        SB    244.TaskEnabled
        JMP   m014
                        ;  TaskRunning = 1;
        SETB  244.TaskRunning
                        ;#asm
                        ;  DW 0x4C ;POPW
        DW    $4C
                        ;#endasm
                        ;  Task_W = W;
        MOV   Task_W,W
                        ;#asm
                        ;  DW 0x4D ;POPST
        DW    $4D
                        ;#endasm
                        ;  Task_ST = W;
        MOV   Task_ST,W
                        ;#asm
                        ;  DW 0x4E ;POPFSR
        DW    $4E
                        ;#endasm
                        ;  Task_FSR = W;
        MOV   Task_FSR,W
                        ;#asm
                        ;  DW 0x4F ;POPPC
        DW    $4F
                        ;#endasm
                        ;  Task_PCL = W;
        MOV   Task_PCL,W
                        ;  W = MODE;
        MOV   W,M
                        ;  Task_PCH = W;
        MOV   Task_PCH,W
                        ;  W = 0; //high byte of target address (0x002: JMP @TaskSchedule)
        CLR   W
                        ;  MODE = W;
        MOV   M,W
                        ;  W = 0x02; //low byte of target address
        MOV   W,#2
                        ;#asm
                        ;  DW 0x4B ;PUSHPC ;retiw will force contextswitch to run TaskSchedule()
        DW    $4B
                        ;#endasm
                        ;  goto Task_exit;
        JMP   m015
                        ;Task_contextswitch:
                        ;  if (!TaskFinished) goto Task_exit;
m014    SB    244.TaskFinished
        JMP   m015
                        ;  W = Task_W;
        MOV   W,Task_W
                        ;#asm
                        ;  DW 0x48 ;PUSHW
        DW    $48
                        ;#endasm
                        ;  W = Task_ST;
        MOV   W,Task_ST
                        ;#asm
                        ;  DW 0x49 ;PUSHST
        DW    $49
                        ;#endasm
                        ;  W = Task_FSR;
        MOV   W,Task_FSR
                        ;#asm
                        ;  DW 0x4A ;PUSHFSR
        DW    $4A
                        ;#endasm
                        ;  W = Task_PCH;
        MOV   W,Task_PCH
                        ;  MODE = W;
        MOV   M,W
                        ;  W = Task_PCL;
        MOV   W,Task_PCL
                        ;#asm
                        ;  DW 0x4B ;PUSHPC ;retiw will force contextswitch to run mainlevel code
        DW    $4B
                        ;#endasm
                        ;  TaskRunning = 0;
        CLRB  244.TaskRunning
                        ;  TaskFinished = 0;
        CLRB  244.TaskFinished
                        ;Task_exit:
                        ;#pragma update_FSR 1
                        ;}
m015    RETP 

  ; FILE task_test.c
                        ;#include <memory.h>
                        ;
                        ;#define TASKS 5        //number of tasks that can run concurrently
                        ;#define TASKFACTOR 1   //number of taskticks for each task to finish
                        ;#define TASKTICK 2     //number of ISR ticks that make up a tasktick
                        ;#define TASKLIST 0x30  //locates TaskList at 0x30
                        ;#include <Task.h>
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// VIRTUAL PERIPHERAL AND OBJECT DECLARATIONS
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// INTERRUPT SUPPORT
                        ;extern interrupt iServer(void);
                        ;
                        ;//include global variables for interrupt here
                        ;#pragma unlockISR
                        ;#pragma origin 0   // without #pragma unlockISR, interrupt must be located first on codepage 0
        ORG $0000
        JMP   @iServer
        JMP   @TaskSchedule
                        ;#pragma origin 4
        ORG $0004
                        ;interrupt iServer(void) {
iServer
                        ;  /* NOTE: W, STATUS and FSR are automatically saved to and restored from shadow registers */
                        ;  //insert other isr code here
                        ;  Task_isr(TASKKERNEL); //taskswitching
        MOV   W,#240
        PAGE  512
        BANK  224
        ;CLRB  4.FSR_7
        CALL  Task_isr
                        ;  W = -INTPERIOD;
        MOV   W,#39
                        ;  retiw();
        RETIW
                        ;}
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// TASK FUNCTIONS
                        ;
                        ;char cnt0 @0x50;
                        ;char cnt1 @0x51;
                        ;char cnt2 @0x52;
                        ;char cnt3 @0x53;
                        ;char cnt4 @0x54;
                        ;char cnt5 @0x70;
                        ;
                        ;TASK inc0() {
inc0
                        ;  cnt0 += 1;
        INC   cnt0
                        ;  cnt5.0 = !cnt5.0;
        MOV   W,#1
        BANK  96
        XOR   cnt5,W
                        ;}
        RET  
                        ;TASK inc1() {
inc1
                        ;  cnt1 += 1;
        INC   cnt1
                        ;  cnt5.1 = !cnt5.1;
        MOV   W,#2
        BANK  96
        XOR   cnt5,W
                        ;}
        RET  
                        ;TASK inc2() {
inc2
                        ;  cnt2 += 1;
        INC   cnt2
                        ;  cnt5.2 = !cnt5.2;
        MOV   W,#4
        BANK  96
        XOR   cnt5,W
                        ;}
        RET  
                        ;TASK inc3() {
inc3
                        ;  cnt3 += 1;
        INC   cnt3
                        ;  cnt5.3 = !cnt5.3;
        MOV   W,#8
        BANK  96
        XOR   cnt5,W
                        ;}
        RET  
                        ;TASK inc4() {
inc4
                        ;  cnt4 += 1;
        INC   cnt4
                        ;  cnt5.4 = !cnt5.4;
        MOV   W,#16
        BANK  96
        XOR   cnt5,W
                        ;}
        RET  
                        ;
                        ;/**
                        ; * Call task (called from TaskSchedule)
                        ; *
                        ; * @param W taskid
                        ; */
                        ;void TaskSwitch(char W) {
TaskSwitch
                        ;  switch (W & 0x3F) { //call task for passed taskid
        AND   W,#63
        SNB   3.Zero_
        JMP   m016
        XOR   W,#1
        SNB   3.Zero_
        JMP   m017
        XOR   W,#3
        SNB   3.Zero_
        JMP   m018
        XOR   W,#1
        SNB   3.Zero_
        JMP   m019
        XOR   W,#7
        SNB   3.Zero_
        JMP   m020
        JMP   m021
                        ;	  case 0: inc0(); break;
m016    BANK  64
        CALL  inc0
        BANK  0
        JMP   m021
                        ;	  case 1: inc1(); break;
m017    BANK  64
        CALL  inc1
        BANK  0
        JMP   m021
                        ;	  case 2: inc2(); break;
m018    BANK  64
        CALL  inc2
        BANK  0
        JMP   m021
                        ;	  case 3: inc3(); break;
m019    BANK  64
        CALL  inc3
        BANK  0
        JMP   m021
                        ;	  case 4: inc4(); break;
m020    BANK  64
        CALL  inc4
        BANK  0
                        ;	  //more tasks can be added
                        ;  }
                        ;}
m021    RETP 
                        ;
                        ;// ************************************************
                        ;// ************************************************
                        ;// C FUNCTIONS
                        ;
                        ;void main() {
main
                        ;  cnt0 = 0;
        BANK  64
        ;CLRB  4.FSR_7
        CLR   cnt0
                        ;  cnt1 = 0;
        CLR   cnt1
                        ;  cnt2 = 0;
        CLR   cnt2
                        ;  cnt3 = 0;
        CLR   cnt3
                        ;  cnt4 = 0;
        CLR   cnt4
                        ;  cnt5 = 0;
        BANK  96
        CLR   cnt5
                        ;  TaskInit(); //initialize task scheduler
        PAGE  512
        BANK  0
        CALL  TaskInit
                        ;  TaskSet(0,0,5); //slot,taskid,interval
        CLR   slot
        CLR   taskid
        MOV   W,#5
        CALL  TaskSet
                        ;  TaskSet(1,1,5); //slot,taskid,interval
        MOV   W,#1
        MOV   slot,W
        MOV   taskid,W
        MOV   W,#5
        CALL  TaskSet
                        ;  TaskSet(2,2,5); //slot,taskid,interval
        MOV   W,#2
        MOV   slot,W
        MOV   taskid,W
        MOV   W,#5
        CALL  TaskSet
                        ;  TaskSet(3,3,5); //slot,taskid,interval
        MOV   W,#3
        MOV   slot,W
        MOV   taskid,W
        MOV   W,#5
        CALL  TaskSet
                        ;  TaskSet(4,4,5); //slot,taskid,interval
        MOV   W,#4
        MOV   slot,W
        MOV   taskid,W
        MOV   W,#5
        CALL  TaskSet
                        ;  TaskStart(0); //we could have used TaskSet(0,0+TASKSTART,5) and omit TaskStart(0)
        MOV   W,#0
        CALL  TaskStart
                        ;  TaskStart(1);
        MOV   W,#1
        CLR   FSR
        CALL  TaskStart
                        ;  TaskStart(2);
        MOV   W,#2
        CLR   FSR
        CALL  TaskStart
                        ;  TaskStart(3);
        MOV   W,#3
        CLR   FSR
        CALL  TaskStart
                        ;  TaskStart(4);
        MOV   W,#4
        CLR   FSR
        CALL  TaskStart
                        ;  TaskEnable(); //tasks will run after interrupts are enabled
        BANK  224
        ;CLRB  4.FSR_7
        CALL  TaskEnable
        PAGE  0
                        ;  OPTION = RTCC_ON + RTCC_PS_OFF; //rtcc interrupts on, rtcc prescaler off
        MOV   W,#136
        MOV   !option,W
                        ;  while (1) {
                        ;  }
        BANK  0
m022    JMP   m022

        ;--- ORG $07FF
        ;-- JMP main
        ORG $0700
        DW $000
        DW $001
        DW $002
        DW $003
        DW $004
        DW $005
        DW $005
        DW $005
        DW $005
        DW $005
        DW $001
        DW $002
        DW $003
        DW $004
        DW $005
        DW $000
        DW $001
        DW $001
        DW $000
        DW $000
        DW $010
        END


; *** KEY INFO ***

; 0x0024 P0   36 word(s)  7 % : TaskSwitch
; 0x0004 P0    7 word(s)  1 % : iServer
; 0x000B P0    5 word(s)  0 % : inc0
; 0x0010 P0    5 word(s)  0 % : inc1
; 0x0015 P0    5 word(s)  0 % : inc2
; 0x001A P0    5 word(s)  0 % : inc3
; 0x001F P0    5 word(s)  0 % : inc4
; 0x0048 P0   58 word(s) 11 % : main

; 0x0200 P1    9 word(s)  1 % : _SystemRamAddress
; 0x0209 P1   13 word(s)  2 % : _SystemRamIndex
; 0x0216 P1    5 word(s)  0 % : LargeArrayAddress
; 0x021B P1    5 word(s)  0 % : RomChar
; 0x0220 P1   29 word(s)  5 % : RomCopy
; 0x023D P1   15 word(s)  2 % : TaskInit
; 0x024C P1    2 word(s)  0 % : TaskEnable
; 0x024E P1   30 word(s)  5 % : TaskSet
; 0x026C P1    5 word(s)  0 % : TaskStart
; 0x0271 P1  119 word(s) 23 % : TaskSchedule
; 0x02E8 P1   52 word(s) 10 % : Task_isr

; RAM usage: 52 bytes (12 local), 84 bytes free
; Maximum call level: 4 (+5 for interrupt)
;  Codepage 0 has  126 word(s) :  24 %
;  Codepage 1 has  284 word(s) :  55 %
;  Codepage 2 has    0 word(s) :   0 %
;  Codepage 3 has   22 word(s) :   4 %
; Total of 432 code words (21 %)
