Assembly language RTOS files
; PaulOSV5C.A51 FOR C USE
; Version 5C
; with PERIODIC INTERVAL (Variables in XDATA)
; ****************************************************
;
; STORES ALL BANK 0 TASK REGISTERS
;
; NOTE THAT MAIN_STACK WOULD HAVE TO BE VERIFIED
; WITH FILE *.M51
;
; HANDLES MANY TASKS, DEPENDING ON
; EXTERNAL MEMORY AND INTERNAL STACK SPACE
; CAN BE USED WITH ASSEMBLY LANGUAGE MAIN PROGRAM
;
; Written by Paul P. Debono – FEBRUARY 2005
; University of Malta
; Faculty of Engineering ; Department of Communications and Computer Engineering
; MSIDA MSD 06
; MALTA.
;
;
;/******************************************************************************/
;//
;// N O T E
;// USE the following settings in Options for Target 1
;// Memory Model: LARGE: VARIABLES IN XDATA
;// Code Model: LARGE: 64K Program
;// START SIZE (32K RAM)
;// CODE: 0X8100 0X5D00
;// RAM: 0XDE00 0X2000
;// or say
;// CODE: 0X8100 0X4000
;// RAM: 0XC100 0X3D00
;//
;// CODE: 0X8100 0X1B00 (8K RAM)
;// RAM: 0X9C00 0X400
;//
;// Code Model: LARGE: 64K Program
;// START SIZE (32K EPROM)
;// CODE: 0X0000 0X8000
;// RAM: 0X8000 0X7E00
;//
;/******************************************************************************/
; STACK MOVING VERSION – MOVES WORKING STACK IN AND OUT OF
; EXTERNAL MEMORY
; SLOWS DOWN RTOS, BUT DOES NOT RESTRICT TASK CALLS
;
; Uses timer 2, in 16-bit auto-reload mode as the time scheduler (time-ticker)
; FOR 8051, TIMER 0 CAN BE USED.
; All tasks run in bank 0, RTOS kernel runs in bank 1
; All tasks must be written as an endless loop.
;
; Waiting time range for WAITT system calls is 1-65535.
; A zero waiting time parameter is set to 1 by the RTOS itself,
; since a ZERO effectively effectively kills the task,
; actually putting it in permanent sleep in the waiting queue!!
;
; Waiting time range for WAITS system call is 0-65535. 0 means wait for the signal forever
;
; IDLE TASK (ENDLESS MAIN PROGRAM – TASK NUMBER = NOOFTASKS)
;
; COMMANDS AVAILABLE FOR THE C APPLICATION PROGRAM ARE:
; (valid parameter values are shown in parenthesis, assuming 20 tasks maximum)
; THE TOP FIVE COMMANDS ARE USED ONLY IN THE MAIN (IDLE TASK) PROGRAM.
; THE OTHERS ARE ONLY USED IN THE TASKS.
;
; THE FOLLOWING COMMANDS, DO NOT CAUSE A CHANGE OF TASK:
; INIT_RTOS(IEMASK) Initialise variables, SPs and enable required interrupts.
; ***** THIS MUST BE THE FIRST RTOS COMMAND TO BE EXECUTED *****
; VALID INTERRUPT NUMBERS USING MONITOR ARE 0, 2, 3 AND 4
; VALID INTERRUPT NUMBERS USING USER EEPROM ARE 0, 1, 2, 3 AND 4
; NOTE THAT IF TIMER 1 IS BEING USED TO GENERATE BAUD RATE,
; THEN YOU CANNOT USE 3 AND 4 SIMULTANEOUSLY
; VALID ; 0 EXTERNAL INT 0 (IEMASK = 00000001 = 01H)
; EEPROM ; 1 TIMER COUNTER 0 (IEMASK = 00000010 = 02H)
; VALID ; 2 EXTERNAL INT 1 (IEMASK = 00000100 = 04H)
; VALID ; 3 TIMER COUNTER 1 (IEMASK = 00001000 = 08H)
; VALID ; 4 SERIAL PORT (IEMASK = 00010000 = 10H)
; 5 TIMER COUNTER 2 (IEMASK = 00010000 = 10H)
; CREATE(TSK#,TSKADDR) Create a new task (0-[n-1]),placing it in the Ready Queue,
; and set up correct task address on its stack.
; RTOSGOMSEC(TICKTIME,PRIORITY)
; Start RTOS going, interrupt every TICKTIME (1-255) msecs.
; PRIORITY = 1 implies Q Priority sorting is required.
; PRIORITY = 0 implies FIFO queue function.
;
; SET_IDLE_MODE() Puts the micro-controller in Idle mode (IDL, bit 0 in PCON)
; SET_POWER_DOWN() Puts the micro-controller in Power Down mode (PD, bit 1 in PCON)
;
; PERIODIC(TIME) Repeat task every TIME msecs.
; SCHEK() Check if current task has its signal set (Returns 1 or 0).
; Signal is cleared if it was found to be set.
; SIGNL(TASKNUMBER) Set signal bit of specified task (0-[n-1]).
; RUNNING_TASK_ID() Returns the number of the currently executing task
;
; THE FOLLOWING COMMANDS WILL CAUSE A CHANGE IN TASK ONLY
; WHEN THE SIGNAL IS NOT ALREADY PRESENT.
; WAITS(TIMEOUT) Wait for signal within TIMEOUT ticks (TIMEOUT = 1 – 65535).
; Or wait for signal indefinitely (TIMEOUT = 0).
; If signal already present, proceed with current task.
; WAITV() Wait for interval to pass.
; If interval already passed, proceed with current task.
;
; THE FOLLOWING COMMANDS, ALWAYS CAUSE A CHANGE IN TASK:
; WAITT(TIMEOUT) Wait for timeout ticks (1 – 65535).
; WAITI(INTNUM) Wait for the given interrupt to occur.
; DEFER() Stop current task and place it at end of ready Q.
; KILL() Kill current task by putting it permanently waiting,(TIMEOUT = 0).
; Clears any waiting signals.
;
;
; THIS IS STILL A SMALL TEST VERSION RTOS. IT IS JUST USED FOR
; SHOWING WHAT IS NEEDED TO MAKE A SIMPLE RTOS.
; IT MIGHT STILL NEED SOME MORE FINE TUNING.
; IT HAS NOT BEEN THOROUGHLY TESTED YET !!!!
; BUT WORKS FINE SO FAR.
; NO RESPONSABILITY IS TAKEN.
;
; CHECK YOUR OWN CORRECT FILE NAME INCLUDING CORRECT PATH IF NECESSARY.
;
; NOTE: Functions which receive parameters when
; called from within C must have their name
; start with an underscore in the A51 source file.
;
; These 2 parameters (set in TaskStkV5C.A51) are used to save
; code and data memory space and increase rtos performance if
; these functions are not used in the application program.
IF (PERIODIC_CMD = 1)
PUBLIC WAITV, _PERIODIC
ENDIF
IF (USING_INT = 1)
PUBLIC _WAITI
ENDIF
PUBLIC DEFER, KILL, SCHEK ; no parameters
PUBLIC SET_IDLE_MODE, SET_POWER_DOWN ; no parameters
PUBLIC RUNNING_TASK_ID ; no parameters
PUBLIC _INIT_RTOS, _CREATE
PUBLIC _WAITT, _WAITS
PUBLIC _RTOSGOMSEC, _SIGNL
;#include “RTMACROSV5C.A51”
; CHECK YOUR OWN CORRECT FILE NAME INCLUDING CORRECT PATH IF NECESSARY.
RTOSVAR1 SEGMENT DATA
RSEG RTOSVAR1 ; VARIABLE DATA AREA VAR1,
; range 0x10-0xFF, since we are using Banks 0,1
READYQTOP: DS 1 ; ADDRESS OF LAST READY TASK
RUNNING: DS 1 ; NUMBER OF CURRENT TASK
TMPSTORE0: DS 1 ; USED IN FETCHSTACK
XINTMASK: DS 1 ; MASK SET BY EXTERNAL INTERRUPT TO INDICATE TYPE
TICKCOUNT: DS 1 ; USED FOR RTOSGO…..
GOPARAM: DS 1 ; USED FOR RTOSGO…..
MYRTOSBITS SEGMENT BIT
RSEG MYRTOSBITS
IF (HALFMSEC == 1)
MSECFLAG: DBIT 1 ; FLAG INDICATING 1 MSEC PASSED
ENDIF
INTFLAG: DBIT 1 ; MARKER INDICATING FOUND TASK WAITING FOR SOME INTERUPT
TINQFLAG: DBIT 1 ; TASK TIMED OUT MARKER
PRIORITY: DBIT 1 ; PRIORITY BIT SET BY RTOSGO….
RSEG RTOSVAR1 ; DIRECTLY ADDRESSABLE AREA
READYQ: DS (NOOFTSKS + 2) ; QUEUE STACK FOR TASKS READY TO RUN
; THE FOLLOWING VARIABLES CAN BE IN THE INDIRECTLY ADDRESSABLE RAM (EVEN > 80H)
RTOSVAR2 SEGMENT IDATA
RSEG RTOSVAR2
SPTS: DS (NOOFTSKS + 1) ; SP FOR EACH TASK AND 1 FOR THE IDLE (MAIN) TASK
TTS: DS 2*NOOFTSKS ; REMAINING TIMEOUT TIME FOR TASKS, 2 BYTES PER TASK
; 0 = NOT TIMING
TSKFLAGS: DS (NOOFTSKS + 1) ; BYTES STORING FLAGS FOR EACH TASK (AND MAIN)
; MAIN_STACK AREA STARTS HERE, NEXT LOCATION AFTER TSKFLAGS.
; CHECK STACK LOCATION IN THE .M51 FILE AFTER COMPILING
; TO CONFIRM THE VALUE OF “MAIN_STACK”
;XSEG AT (XTRAMTOP – (STACKSIZE * (NOOFTSKS + 1)) – (4*NOOFTSKS) + 1)
EXTERNDATA SEGMENT XDATA
RSEG EXTERNDATA
IF (PERIODIC_CMD = 1)
INTVALCNT: DS 2*NOOFTSKS ; 0 = NOT TIMING
INTVALRLD: DS 2*NOOFTSKS ; 0 = NOT TIMING
ENDIF
EXT_STK_AREA: DS (NOOFTSKS + 1) * STACKSIZE ; THIS IS THE ACTUAL SIZE OF STACK AREA
; ======================================================================
;
CSEG AT EXT0_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA
MOV XINTMASK,#EXT0W ; EXTERNAL 0
LJMP XTRA_INT
IF (TICK_TIMER = 0)
CSEG AT TIM0_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA ; TIMER 0
LJMP RTOS_TIMER_INT ; USED FOR THE RTOS SCHEDULER
ELSE
CSEG AT TIM0_INT_VECTOR
CLR EA
MOV XINTMASK,#TIM0W
LJMP XTRA_INT
ENDIF
CSEG AT EXT1_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA
MOV XINTMASK,#EXT1W ; EXTERNAL 1
LJMP XTRA_INT
IF (TICK_TIMER = 1)
CSEG AT TIM1_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA ; TIMER 1
LJMP RTOS_TIMER_INT ; USED FOR THE RTOS SCHEDULER
ELSE
CSEG AT TIM1_INT_VECTOR
CLR EA
MOV XINTMASK,#TIM1W
LJMP XTRA_INT
ENDIF
CSEG AT SER0_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA
MOV XINTMASK,#SER0W ; SERIAL
LJMP XTRA_INT
IF (TICK_TIMER = 2)
CSEG AT TIM2_INT_VECTOR ; INTERRUPT VECTOR ADDRESS FOR
CLR EA ; TIMER 2
CLR TF2 ; Clear Timer 2 interrupt flag (not done automatically)
LJMP RTOS_TIMER_INT
ELSE
CSEG AT TIM2_INT_VECTOR
CLR EA
MOV XINTMASK,#TIM2W
LJMP XTRA_INT
ENDIF
MyRTOS_CODE SEGMENT CODE ; STARTS AT 8100H FOR THE FLIGHT32 BOARD
RSEG MyRTOS_CODE
;==========================================================================
; START OF RTOS SYSTEM
; PREFIX NAME FOR FUNC WITH REG-PASSED PARAMS MUST START WITH AN UNDERSCORE _
SET_IDLE_MODE: ; SETS THE MICRO-CONTROLLER IN IDLE MODE
ORL PCON,#0x01 ; SETS BIT 0 OF PCON SFR
RET
SET_POWER_DOWN: ; SETS THE MICRO-CONTROLLER IN PWER DOWN MODE
ORL PCON,#0x02 ; SETS BIT 1 OF PCON SFR
RET
_INIT_RTOS: ; SYS CALL TO SET UP VARIABLES
; R7 HOLDS THE IE MASK
MOV A,R7
ANL A,#01111111B ; ENSURE EA = 0 (ENABLED LATER FROM RTOSGO…)
IF (TICK_TIMER = 0)
ORL A,#00000010B ; AND ET0 = 1 (USED FOR RTOS TICK TIME)
MOV IP,#02H ; Timer 0 High Priority, PT0=1, OTHERS ALL LOW
ELSEIF (TICK_TIMER = 1)
ORL A,#00001000B ; AND ET1 = 1 (USED FOR RTOS TICK TIME)
MOV IP,#08H ; Timer 1 High Priority, PT1=1, OTHERS ALL LOW
ELSEIF (TICK_TIMER = 2)
ORL A,#00100000B ; AND ET2 = 1 (USED FOR RTOS TICK TIME)
MOV IP,#20H ; Timer 2 High Priority, PT2=1, OTHERS ALL LOW
ENDIF
MOV IE,A
; IN THE C ENVIRONMENT, THE KEIL SOFTWARE CLEARS THE INTERNAL RAM FROM 0 TO FFH
; PROVIDED THAT THE C51\LIB FILE STARTUP.A51 IS INCLUDED WITH THE SOURCE GROUP,
; AND WITH THE CORRECT IDATALEN VARIABLE SETTING TO REFLECT 8051 FAMILY TYPE.
;
; IN ASM OR A51 (NOT IN C), ALL THE INTERNAL RAM (0-FFH) IS
; CLEARED BY MEANS OF THE CLR_8051_RAM MACRO.
;
; CLEAR PERIODIC INTERVAL TABLE IF BEING USED
IF (PERIODIC_CMD = 1)
MOV DPTR,#INTVALCNT
MOV A,#NOOFTSKS
RL A ; DOUBLE THE NUMBER
MOV R0,A ; R0 CONTAINS NUMBER OF BYTES TO CLEAR
CLR A
DPTR_A0: ; POINT DPTR TO CORRECT LOCATION
MOVX @DPTR,A
INC DPTR
DJNZ R0,DPTR_A0
ENDIF
MOV DPTR,#EXT_STK_AREA ; CLEAR ALL EXTERNAL RAM STACKS
MOV R0,#(NOOFTSKS + 1)
CLR A
NEXT_STACK:
MOV R1,#STACKSIZE
CLR_STACK:
MOVX @DPTR,A
INC DPTR
DJNZ R1,CLR_STACK
DJNZ R0,NEXT_STACK
MOV RUNNING,#IDLE_TASK ; IDLE TASK RUNNING (Main program endless loop)
MOV R7,#NOOFTSKS
MOV R0,#TTS
MOV R1,#READYQ
LOAD_VARS:
MOV @R0,#LOW(NOT_TIMING) ; NO TIMER ACTION
INC R0
MOV @R0,#HIGH(NOT_TIMING)
MOV @R1,#IDLE_TASK ; IDLE TASK IN ALL OF READYQ (Main program endless loop)
INC R0
INC R1
DJNZ R7,LOAD_VARS ; SET UP ALL TASKS
MOV @R1,#IDLE_TASK ; FILL TWO ADDITIONAL LOCATIONS, USED
INC R1 ; DURING THE Q SHIFTING ROUTINE, WITH IDLE TASK.
MOV @R1,#IDLE_TASK ; THIS ENSURES IDLE TASK WILL ALWAYS BE IN Q IF
MOV READYQTOP,#READYQ ; THERE ARE NO OTHER TAKS READY TO EXECUTE.
; SET UP SP
MOV R7,#(NOOFTSKS + 1) ; COUNTER
MOV R0,#SPTS ; INITIALIZE ALL STACK POINTERS
MOV A, #(MAIN_STACK – 1)
ADD A,#(NOOFPUSHES + 2) ; SIMULATE Push_Bank0_Reg PLUS
; SAVING OF RETURN ADDRESS BY INTERRUPT
SET_UP:
MOV @R0,A
INC R0
DJNZ R7,SET_UP
RET
_CREATE:
; SYS CALL ENTRY TO CREATE A TASK
; TASK NUMBER (0 to 19) PASSED IN BANK0 R7
; TASK START ADDR PASSED IN BANK0 R1,R2,R3
; LSB in R1, MSB in R2, R3 contains type
INC READYQTOP ; POINT TO TOP OF READY READYQ
MOV R0,READYQTOP
MOV @R0,07H ; PUT TASK# (R7 bank 0 = 07H) IN READY QUEUE
MOV A,R7
CALL FETCH_STACK
MOV A,R1
MOVX @DPTR,A ; COPY LOW BYTE R1 INTO LOW STACK AREA
INC DPTR
MOV A,R2
MOVX @DPTR,A ; NOW SAVE THE HIGH ORDER BYTE (R2)
SETB TINQFLAG ; SIGNAL NEW TASK IN Q, USED TO START QSHIFT
RET
_RTOSGOMSEC: ; SYS CALL TO START RTOS FOR R7 MILLISECOND TICKS
CLR PRIORITY
CJNE R5,#1,PRIORITY_OK ; IF SECOND PARAMETER = 1, THEN
SETB PRIORITY ; SET PRIORITY SORTING IS REQUIRED
PRIORITY_OK:
IF (TICK_TIMER = 0)
MOV TH0,#HIGH(BASIC_TICK) ; LOAD TH0 AND TL0 WITH BASIC TICK COUNT
MOV TL0,#LOW(BASIC_TICK) ; SAVE THEM IN THE AUTO RE-LOAD REGISTERS
ELSEIF (TICK_TIMER = 1)
MOV TH1,#HIGH(BASIC_TICK) ; LOAD TH0 AND TL0 WITH BASIC TICK COUNT
MOV TL1,#LOW(BASIC_TICK) ; SAVE THEM IN THE AUTO RE-LOAD REGISTERS
ELSEIF (TICK_TIMER = 2)
MOV RCAP2H,#HIGH(BASIC_TICK) ; LOAD RCAPS WITH 1 MILLISECOND COUNT
MOV RCAP2L,#LOW(BASIC_TICK) ; SAVE THEM IN THE AUTO RE-LOAD REGISTERS
ENDIF ; OF TIMER 2 (FOR FLT-32)
MOV GOPARAM,07 ; LOAD TICKS PARAMETER, PASSED IN R7 BANK 0
MOV TICKCOUNT,07
IF (TICK_TIMER = 0)
ANL TMOD,#0F0H
ORL TMOD,#01H ; START TIMER 0 IN 16-BIT MODE 1.
SETB TF0 ; SIMULATE TIMER 0 INTERRUPT.
ELSEIF (TICK_TIMER = 1)
ANL TMOD,#0FH
ORL TMOD,#10H ; START TIMER 1 IN 16-BIT MODE 1.
SETB TF1 ; SIMULATE TIMER 1 INTERRUPT.
ELSE
MOV T2CON,#04H ; START TIMER 2 IN 16-BIT AUTO RE-LOAD MODE.
; TIMER 1 CAN BE USED FOR SERIAL BAUD RATE
SETB TF2 ; SIMULATE TIMER 2 INTERRUPT IMMEDIATELY
ENDIF
SETB EA ; ENABLE GLOBAL INTERRUPT SIGNAL
RET ; EFFECTIVELY STARTING THE RTOS.
RUNNING_TASK_ID:
MOV R7,RUNNING
RET
SCHEK: ; SYS CALL ENTRY CHECK SIGNAL BIT FOR TASK
; RETURN 0 IF BIT CLEAR OR 1 IF BIT SET IN R7.
; SIG. BIT IS CLEARED IF FOUND TO BE SET
; NO NEED FOR BANK SWITCHING
CLR EA ; IMMEDIATE RETURN – NO CONTEXT SWITCHING
Push_HalfB0_Reg
MOV A,RUNNING
MOV B,#SIGS
CALL CHK_CLR_FLAG ; SIG IS CLEARED IF IT WAS FOUND TO BE SET
MOV R7,#1
JC SIGNAL_SET ; SIG SET, HENCE RETURN WITH R7=1
DEC R7 ; SIG NOT YET SET, HENCE RETURN WITH R7=0
SIGNAL_SET:
Pop_HalfB0_Reg
SETB EA
RET
_SIGNL: ; SYS CALL ENTRY-SET SIGNAL BIT FOR SPECIFIED TASK
CLR EA ; NO NEED FOR BANK SWITCHING – NO CONTEXT SWITCHING
Push_Bank0_Reg
MOV A,R7 ; TASK NUMBER PASSED IN R7 bank 0
MOV B,#SIGW
CALL CHK_CLR_FLAG
JNC NOT_WAITING ; IF TASK NOT ALREADY WAITING, SET SIGNAL BIT
MOV A,R7 ; OTHERWISE PLACE IT ON READY Q
MOV B,#SIGS ; ENSURE CLEARED SIGNAL BIT
CALL CLR_FLAG
MOV A,#TTS ; AND MARK TASK AS NOT TIMING
ADD A,R7
ADD A,R7 ; ADD OFFSET TWICE SINCE 2 TIME-OUT BYTES PER TASK
MOV R0,A
MOV @R0,#LOW(NOT_TIMING)
INC R0
MOV @R0,#HIGH(NOT_TIMING)
INC READYQTOP
MOV R0,READYQTOP
MOV @R0,07 ; PLACE SIGNALLED TASK ON READY Q
SETB TINQFLAG ; INDICATE, NEW TASK IN Q, BUT
DONT_GIVE_UP:
Pop_Bank0_Reg
SETB EA ; DON’T GIVE UP RUNNING CURRENT TASK.
RET ; (MUST DEFER IF REQUIRED TO DO SO)
NOT_WAITING:
MOV A,R7
MOV B,#SIGS ; SET SIGNAL BIT OF SIGNALLED TASK
CALL SET_FLAG
Pop_Bank0_Reg
SETB EA
RET ; AND CONTINUE RUNNING CURRENT TASK
IF (USING_INT = 1)
_WAITI: ; SYS CALL ENTRY POINT – WAIT FOR INTERRUPT
; VALID INTERRUPT NUMBERS USING MONITOR ARE 0, 2, 3 AND 4
; VALID INTERRUPT NUMBERS USING USER EEPROM ARE 0, 1, 2, 3 AND 4
; NOTE THAT IF TIMER 1 IS BEING USED TO GENERATE BAUD RATE,
; THEN YOU CANNOT USE 3 AND 4 SIMULTANEOUSLY
; INT 5 IS COMPULSORY (IEMASK = 00100000 = 20H)
; VALID ; 0 EXTERNAL INT 0 (IEMASK = 00000001 = 01H)
; EEPROM ; 1 TIMER COUNTER 0 (02H, VECTOR NOT AVAILABLE ON FLT-32)
; VALID ; 2 EXTERNAL INT 1 (IEMASK = 00000100 = 04H)
; VALID ; 3 TIMER COUNTER 1 (IEMASK = 00001000 = 08H)
; VALID ; 4 SERIAL PORT (IEMASK = 00010000 = 10H)
; 5 TIMER COUNTER 2 (ALWAYS USED BY RTOS, ENABLED AUTOMATICALLY)
CLR EA
Push_Bank0_Reg
INC R7 ; INTERRUPT NUMBER (0 TO 4) PARAMETER PASSED IN R7 bank 0
CLR A
SETB C
SHIFT_LEFT: ; CONVERT TO INTERRUPT MASK (1,2,4,8,16) BY ROTATING LEFT
RLC A
DJNZ R7, SHIFT_LEFT
MOV B,A ; B NOW CONTAINS CORRECT INTERRUPT MASK
MOV A,RUNNING
CALL SET_FLAG
LJMP QSHFT ; STOP CURRENT TASK AND RUN NEXT TASK IN READY Q
ENDIF
IF (PERIODIC_CMD = 1)
WAITV:
CLR EA ; UNTIL TIMEOUT PASSED IN R7(LOW),R6(HIGH)
Push_Bank0_Reg
MOV A,RUNNING
MOV B,#SIGV ; TEST IF SIGNAL ALREADY THERE
CALL CHK_CLR_FLAG
JNC NO_INTVAL ; NO SIGNAL YET, SO TASK MUST WAIT
; ELSE, SIGNAL WAS PRESENT, (NOW CLEARED)
LJMP DONT_GIVE_UP ; OR RETURN TO SAME TASK
NO_INTVAL:
MOV A,RUNNING ; RELOAD TASK NUMBER
MOV B,#SIGV
CALL SET_FLAG ; SET SIG WAITING BIT, AND
LJMP QSHFT ; RUN NEXT TASK IN READY Q
ENDIF
_WAITS: ; SYSTEM CALL – WAIT SIGNAL ARRIVAL
CLR EA ; UNTIL TIMEOUT PASSED IN R7(LOW),R6(HIGH)
Push_Bank0_Reg
MOV A,RUNNING
MOV B,#SIGS ; TEST IF SIGNAL ALREADY THERE
CALL CHK_CLR_FLAG
JNC NO_SIGNAL ; NO SIGNAL YET, SO TASK MUST WAIT
; ELSE, SIGNAL WAS PRESENT, (NOW CLEARED)
LJMP DONT_GIVE_UP ; OR RETURN TO SAME TASK
NO_SIGNAL:
MOV A,RUNNING ; RELOAD TASK NUMBER
MOV B,#SIGW
CALL SET_FLAG ; SET SIG WAITING BIT, AND CONTINUE WITH WAITT
; TO WAIT FOR TIMEOUT
CJNE R7,#LOW(NOT_TIMING),SET_TIMEOUT ; ACCEPT A WAIT TIME OF 0
CJNE R6,#HIGH(NOT_TIMING),SET_TIMEOUT ; ACCEPT A WAIT TIME OF 0 IN ORDER TO BE ABLE
; TO WAIT FOR SIGNAL INDEFINETELY
SJMP SET_TIMEOUT_0
_WAITT: ; SYS CALL ENTRY POINT – WAIT FOR TIME OUT
CLR EA
Push_Bank0_Reg
CJNE R7,#LOW(NOT_TIMING),SET_TIMEOUT ; TIME OUT PARAMETER PASSED IN R6 (HIGH)
CJNE R6,#HIGH(NOT_TIMING),SET_TIMEOUT ; AND R7 (LOW) BANK 0
MOV R7,#1 ; RANGE 1-65535 (0 = PERMANENT SLEEP)
MOV R6,#0 ; IF BOTH ARE ZERO, REPLACE WITH A ONE
SET_TIMEOUT:
CLR C ; PERFORM 65536 – TIME OUT VALUE
CLR A ; SO THAT IN RTOS_TIMER_INT WE CAN
SUBB A,R7 ; USE ‘INC DPTR’ EASILY TO UPDATE TIMEOUT
MOV R7,A
CLR A
SUBB A,R6
MOV R6,A
SET_TIMEOUT_0:
MOV A,#TTS
ADD A,RUNNING ; ADD OFFSET TWICE SINCE TIMEOUTS ARE
ADD A,RUNNING ; TWO BYTES PER TASK
MOV R0,A
MOV @R0,07 ; BANK 0 R7,R6 – TIMEOUT PUT IN TABLE (WAITING Q)
INC R0
MOV @R0,06
LJMP QSHFT ; STOP CURRENT TASK AND RUN NEXT TASK IN READY Q
KILL: ; SYS CALL ENTRY (NO PARAMETERS)
; CLEARS ALL WAITING SIGNALS FLAGS
CLR EA
Push_Bank0_Reg
MOV A,RUNNING
MOV R0,#TSKFLAGS
ADD A,R0
MOV R0,A
CLR A
MOV @R0,A ; TO CLEAR AND STORE
MOV R7,#LOW(NOT_TIMING) ; KILL PRESENT TASK (PUT IN PERMANENT WAIT)
MOV R6,#HIGH(NOT_TIMING)
SJMP SET_TIMEOUT_0
DEFER: ; SYS CALL ENTRY (NO PARAMETERS)
CLR EA
Push_Bank0_Reg
MOV A, #TTS
ADD A, RUNNING
ADD A, RUNNING
MOV R0,A
MOV @R0,#LOW(NOT_TIMING) ; SET NO WAITING TIME FOR DEFERRED TASK
INC R0
MOV @R0,#HIGH(NOT_TIMING)
INC READYQTOP ; BUT PUT IN READY Q AGAIN.
MOV R0,READYQTOP
MOV @R0,RUNNING ; PUT RUNNING AT END OF READYQ
; AND THEN SHIFT Q BELOW
QSHFT: ; SAVE PRESENT RUNNING TASK STACK PTR
CLR TINQFLAG ; CLR TINQFLAG AND SHIFT READYQ BY ONE,
; GET NEW RUNNING TASK FROM READYQ
SetBank 1 ; USE BANK 1 – MAY HAVE ENTERED FROM INTERRUPT
MOV A, #SPTS ; SAVE SP
ADD A, RUNNING
MOV R0,A
MOV @R0, SP ; STORE PRESENT STACK POINTER OF TASK
MOV A,RUNNING
CALL FETCH_STACK
Int2Ext ; SAVE STACK IN EXTERNAL, READY FOR SWAP
MOV R1,#(READYQ + 1) ; NOW SHIFT Q DOWN 1
SHIFT_DOWN:
MOV A,@R1
DEC R1
MOV @R1,A
MOV A,R1
INC R1
INC R1
CJNE A,READYQTOP,SHIFT_DOWN
DEC READYQTOP ; THEY ALL MOVED DOWN BY 1, HENCE DECREMENT RQTOP
CJNE A,#READYQ,RUN_NEW_TASK ; BUT READYQTOP SHOULD NEVER GO BELOW READYQ
INC READYQTOP ; SO READYQTOP = READYQ AGAIN, IF IT WAS BELOW
RUN_NEW_TASK: ; RUN NEW TASK
JNB PRIORITY,DONT_SORT ; DO NOT SORT Q IF PRIORITY OPTION IS OFF
LCALL TASK_SORT
DONT_SORT:
MOV A,READYQ
MOV RUNNING,A ; SET NEW TASK AS RUNNING
CALL FETCH_STACK
Ext2Int ; GET NEW STACK IMAGE
MOV A,#SPTS
ADD A,RUNNING
MOV R0,A
MOV SP,@R0 ; SET SP TO NEW TASK STACK AREA
Pop_Bank0_Reg
SetBank 0
SETB EA ; MAY HAVE ENTERED FROM TIMER INTERRUPT
RETI ; OTHERWISE NO HARM ANYWAY
IF (PERIODIC_CMD = 1)
_PERIODIC: ; SYSTEM CALL
CLR EA
Push_Bank0_Reg
MOV DPTR,#INTVALCNT
MOV A,RUNNING
RL A ; DOUBLE THE NUMBER
DPTRPLUSA
MOV A,07 ; SAVE LOW BYTE, HELD IN R7
MOVX @DPTR,A
MOV A,06
INC DPTR
MOVX @DPTR,A ; SAVE HIGH BYTE, HELD IN R6
MOV DPTR,#INTVALRLD
MOV A,RUNNING
RL A ; DOUBLE THE NUMBER
DPTRPLUSA
MOV A,07 ; SAVE LOW BYTE, HELD IN R7
MOVX @DPTR,A
MOV A,06 ; SAVE HIGH BYTE, HELD IN R6
INC DPTR
MOVX @DPTR,A
Pop_Bank0_Reg
SETB EA
RET
ENDIF
TSKRDY_CHK2: ; JUST A STEPPING STONE
LJMP TSKRDY_CHK
RTOS_TIMER_INT: ; INTERRUPT ENTRY ONLY FROM TIMER2 OVERFLOW INTERRUPT
; USES ACC,PSW, (R0,R1 AND R2 FROM BANK 1)
Push_Bank0_Reg
SetBank 1 ; SET TO REGISTERBANK 1
IF (TICK_TIMER = 0)
CLR TR0 ; STOP, RELOAD
MOV TH0,#HIGH(BASIC_TICK)
MOV TL0,#LOW(BASIC_TICK)
SETB TR0 ; AND RESTART TIMER 0
ELSEIF (TICK_TIMER = 1)
CLR TR1 ; STOP, RELOAD
MOV TH1,#HIGH(BASIC_TICK)
MOV TL1,#LOW(BASIC_TICK)
SETB TR1 ; AND RESTART TIMER 1
ENDIF
IF (HALFMSEC = 1)
JBC MSECFLAG, TSKRDY_CHK2 ; ONLY HALF A MILLISECOND PASSED, HENCE CHK FOR
; EXTERNAL INTERRUPT TASKS ONLY
SETB MSECFLAG ; USED TO DOUBLE 1/2 MSEC TICKCOUNT DELAY
ENDIF
DJNZ TICKCOUNT, TSKRDY_CHK2 ; CHECK IF REQUIRED TICK TIME HAS PASSED
MOV TICKCOUNT, GOPARAM
IF (PERIODIC_CMD = 1)
; FIRST CHECK THE PERIODIC INTERVALS, IF USED
CHK_PERIODICS:
MOV R0,#0 ; DO ALL TASKS, STARTING WITH TASK 0, HELD IN R0, BANK 1
MOV DPTR,#INTVALCNT ; DPTR POINTS TO FIRST TASK INTERVAL IN TABLE
CHECK_VALS:
PUSH DPL ; SAVE PTR
PUSH DPH
LOADREGSXDATA R2,R3 ; R2 = LOW, R3 = HIGH VALUE OF INTVALCNT
MOV A,R2
ORL A,R3
JZ CHECK_NEXTV ; 0=TASK NOT USING PERIODIC INTERVAL, HENCE SKIP,
; DO NOT UPDATE INTERVAL COUNT.
COUNT_DOWNV:
DEC2REGS R2,R3 ; DECREMENT INTERVAL COUNT
POP DPH ; GET POINTER
POP DPL
PUSH DPL ; AND SAVE POINTER AGAIN
PUSH DPH
LOADXDATAREGS R2,R3 ; AND STORE NEW DECREMENTED VALUE
MOV A,R2
ORL A,R3
JNZ CHECK_NEXTV ; TASK NOT TIMED OUT YET, CHECK NEXT TASK
VAL_OUT: ; NEW TASK INTERVAL TIMED OUT, HENCE RELOAD INTERVAL
; RELOAD VALUE IS NOOFTSKS*2 – 1 AWAY FROM
; PRESENT DPTR VALUE
MOV A, #NOOFTSKS
RL A
DEC A
DPTRPLUSA
LOADREGSXDATA R4,R5
POP DPH
POP DPL
PUSH DPL ; SAVE PTR
PUSH DPH
LOADXDATAREGS R4,R5
; TEST INTERVAL FLAG
MOV A,R0
MOV B,#SIGV ; TEST IF SIGNAL ALREADY THERE
CALL CHK_CLR_FLAG
JNC SET_VFLAG ; NO SIGNAL YET, SO JUST SET FLAG
;
; IF TASK ALREADY IN Q, DO NOT DUPLICATE
; THIS COULD HAPPEN IN CASE OF BAD TASK PROGRAMMING, WHERE
; THE TASK DURATION IS LONGER THAN THE PERIODIC TIME
; IF TASK PROGRAMMING IS OK, THEN THIS CHECK CAN BE ELIMINATED TO REDUCE OVERHEADS.
MOV R1,#READYQ
CHK_NXT_IN_Q:
MOV A,@R1
XRL A,R0
JZ CHECK_NEXTV
MOV A,R1
INC R1
CJNE A,READYQTOP,CHK_NXT_IN_Q
;
;
;
INC READYQTOP ; POINT TO TOP OF READY READYQ
MOV R1,READYQTOP
MOV @R1,08 ; PUT TASK# (R0 bank 1 = 08H) IN READYQ
SETB TINQFLAG ; MARK FLAG INDICATING THAT A TASK FINISHED WAITING INTERVAL
SJMP CHECK_NEXTV ; AND PLACED IN READYQ
SET_VFLAG:
MOV A,R0
MOV B,#SIGV
CALL SET_FLAG ; SET INTERVAL READY BIT
CHECK_NEXTV:
POP DPH
POP DPL
INC DPTR ; MOVE UP 1 TASK IN PERIODIC INTERVAL TABLE
INC DPTR
INC R0 ; INCREMENT TASK NUMBER COUNTER
CJNE R0,#NOOFTSKS,CHECK_VALS ; END OF ALL TASKS YET?
ENDIF
SJMP CHK_FOR_TOUTS ; NOW CHECK FOR TIME OUTS
TSKRDY_CHK1: ; JUST A STEPPING STONE
SJMP TSKRDY_CHK
; NOW CHECK FOR TIME OUTS
CHK_FOR_TOUTS:
MOV R0,#TTS ; R0 POINTS TO FIRST TASK TIMEOUT IN TTS TABLE
MOV R2,#0 ; CHECK ALL TASKS, STARTING WITH TASK 0
CHECK_TIMEOUTS:
MOV A,@R0 ; GET TIME FOR TASK
MOV DPL,A
MOV R1,08 ; SAVE POINTER TO LOW BYTE IN R1
INC R0 ; SAVE POINTER TO HIGH BYTE IN R0
MOV A,@R0
MOV DPH,A
ORL A,DPL
JZ CHECK_NEXT ; 0=TASK NOT TIMING, HENCE SKIP, DO NOT DECREMENT TIMEOUT
COUNT_UP: ; DPTR NOW CONTAINS TIMEOUT VALUE
INC DPTR ; NOW WE CAN INCREMENT IT
MOV @R0,DPH ; SAVE NEW TIME OUT VALUE, EVEN IF ZERO
MOV @R1,DPL
MOV A,DPH ; AND CHECK IF TIMED UP (ROLL OVER TO ZERO)
ORL A,DPL ; ACCUMULATOR EQUALS ZERO IF TIMED OUT
JNZ CHECK_NEXT ; TASK NOT TIMED OUT YET, CHECK NEXT TASK
TIMED_OUT: ; NEW TASK TIMED OUT, HENCE PLACE IN READYQ
INC READYQTOP ; POINT TO TOP OF READY READYQ
MOV R1,READYQTOP
MOV @R1,0AH ; PUT TASK# (R2 bank 1 = 0AH) IN READYQ
SETB TINQFLAG ; MARK FLAG INDICATING THAT A TASK FINISHED WAITING
MOV A,R2
MOV B,#SIGW
CALL CLR_FLAG ; CLEAR SIGNAL WAITING BIT (IF SET)
CHECK_NEXT:
INC R0 ; MOVE UP 1 IN TTS TABLE
INC R2 ; INCREMENT TASK NUMBER COUNTER
CJNE R2,#NOOFTSKS,CHECK_TIMEOUTS ; END OF ALL TASKS YET?
TSKRDY_CHK:
JNB TINQFLAG, EXIT ; NO TASK ADDED, HENCE EXIT
; NOTE THAT TINQFLAG CAN BE SET BY 4 ROUTINES,
; CREATE, SIGNL, RTOS_TIMER_INT AND XTRA_INT
; IF CLR (FLAG = 0) => NO NEW TASK PUT IN READYQ
; IF SET (FLAG = 1) => A NEW TASK HAS BEEN PLACED IN READYQ.
CAN_CHANGE:
MOV A,RUNNING ; CHECK CURRENT TASK
CJNE A,#IDLE_TASK,EXIT ; NOT IN IDLE STATE, SO DO NOT INTERRUPT YET
; BUT LEAVE TINQFLAG SET FOR THE NEXT RTOS INT. CHECK
; WHEN THE IDLE_TASK MIGHT BE RUNNING.
LJMP QSHFT ; IDLE AND NEW TASK TIMED OUT, HENCE CHANGE TASK
EXIT:
Pop_Bank0_Reg
SetBank 0
SETB EA
RETI
XTRA_INT:
IF (USING_INT = 1)
; EXTRA INTERRUPT SERVICE ROUTINE
; USED DURING EXTERNAL, TIMER AND SERIAL INTERRUPTS
; CHECKS BITS SET BY WAITI CALL AND PUTS TASK
; WAITING FOR ONE RTOS INTERRUPT IF IT
Push_Bank0_Reg ; WAS WAITING FOR THIS EXTERNAL INTERRUPT
; USES ACC, B, PSW,( R0, R2 AND R3 BANK 1 )
SetBank 1
MOV B,XINTMASK ; GET EXTERNAL INTERRUPT MASK
CLR A ; NOW CHECK IF ANY TASKS WERE WAITING
CLR INTFLAG ; FOR THIS INTERRUPT, STARTING WITH TASK 0
TRY_NXT:
MOV R2,A ; STORE TASK NUMBER IN R2 BANK 1
CALL CHK_CLR_FLAG
JNC NOT_YET
SETB INTFLAG ; SET MARKER SHOWING THAT AT LEAST ONE
MOV A,#TTS ; TASK WAS WAITING FOR THIS INTERRUPT
ADD A,R2
ADD A,R2
MOV R0,A ; HENCE
MOV @R0,#LOW(NOT_TIMING) ; MARK TASK AS NOT WAITING
INC R0
MOV @R0,#HIGH(NOT_TIMING) ; MARK TASK AS NOT WAITING
INC READYQTOP ; AND
MOV R0,READYQTOP ; PUT FOUND TASK ON READYQ
MOV @R0,0AH ; QSHFT WILL DO THE REST LATER.
NOT_YET:
MOV A,R2
INC A ; CHECK NEXT TASK
CJNE A,#NOOFTSKS,TRY_NXT
JNB INTFLAG, EXIT_INT ; NO TASK FOUND WAITING INT, HENCE EXIT
EXIT_INT_SHFT:
SETB TINQFLAG ; INDICATE THAT A NEW TASK HAS BEEN PUT IN READY Q
MOV A,RUNNING ; CHECK CURRENT TASK
CJNE A,#IDLE_TASK,EXIT_INT ; NOT IN IDLE STATE, SO DO NOT SHIFT TASKS
; BUT TINQFLAG WILL STILL REMAIN SET SO THAT THE
; RTOS_TIMER_INT ROUTINE CAN HANDLE IT LATER.
OK2SHFT:
LJMP QSHFT
EXIT_INT:
Pop_Bank0_Reg
SetBank 0
SETB EA
ENDIF
RETI
; SUB ROUTINES USED IN THE RTOS
; ***********************************************
SET_FLAG:
; ENTRY A = TASK NUMBER
; B = BIT MASK
; EXIT REQUIRED BIT IN TASK FLAG BYTE SET
PUSH 00
PUSH 08
MOV R0,#TSKFLAGS
ADD A,R0
MOV R0,A
MOV A,@R0
ORL A,B ; SET REQUIRED BIT TO 1
MOV @R0,A
POP 08
POP 00
RET
; ***********************************************
CHK_FLAG:
; ENTRY A = TASK NUMBER
; B = BIT MASK
; EXIT CARRY SET IF FLAG WAS FOUND TO BE SET
PUSH 00
PUSH 08
MOV R0,#TSKFLAGS
ADD A,R0
MOV R0,A
MOV A,@R0
CLR C
ANL A,B
JZ EXIT1 ; BIT WAS CLEARED, CARRY = 0
SETB C ; BIT WAS SET, CARRY =1
EXIT1:
POP 08
POP 00
RET
; ***********************************************
CLR_FLAG:
CHK_CLR_FLAG:
; BOTH NAMES CORRESPOND TO THE SAME ROUTINE
; ENTRY A = TASK NUMBER
; B = BIT MASK
; EXIT CARRY SET IF FLAG WAS FOUND TO BE SET
; AND THEN CLEARS FLAG BEFORE EXITING ROUTINE
; CARRY BIT = 0 IF BIT WAS ZERO
PUSH 00
PUSH 08
MOV R0,#TSKFLAGS
ADD A,R0
MOV R0,A
MOV A,@R0
CLR C
ANL A,B
JZ EXIT2 ; BIT WAS CLEAR, HENCE EXIT, CARRY = 0
MOV A,@R0
XRL A,B ; SINCE IT WAS SET, THEN SIMPLY XOR WITH MASK
MOV @R0,A ; TO CLEAR AND STORE
SETB C ; CARRY = 1 SINCE BIT WAS INITIALLY SET
EXIT2:
POP 08
POP 00
RET
; ***********************************************
FETCH_STACK:
; ENTRY A = TASK NUMBER, USES ACC, DPTR, AND R0
; EXIT DPTR POINT TO START OF STACK AREA FOR TASK
MOV TMPSTORE0,A
MOV DPTR,#EXT_STK_AREA
MOV R0,#0
LOOP1:
MOV A,R0
CJNE A,TMPSTORE0,CONT1
RET
CONT1:
MOV A,#STACKSIZE
ADD A,DPL
MOV DPL,A
MOV A,DPH
ADDC A,#0
MOV DPH,A
INC R0
SJMP LOOP1
; ***********************************************
; SORT THE READY Q, LOW TASK NUMBER IS THE HIGHEST
; PRIORITY, AND THEREFORE AFTER ONE Q SORT PASS,
; THE LOWEST NUMBERED TASK ENDS UP AT BOTTOM OF Q,
; NEXT IN LINE TO EXECUTE.
; IT IS CALLED FROM QSHFT, WHEN REGISTER BANK 1 IS BEING USED.
;
TASK_SORT:
PUSH ACC
PUSH 08
PUSH B
MOV R0,READYQTOP ; R0 POINTS IN READY Q AREA
MOV A,R0
CJNE A,#READYQ,NEXT_PAIR
SJMP EXIT_QSORT ; ONLY ONE TASK, HENCE EXIT
NEXT_PAIR:
MOV A,@R0
MOV B,@R0
DEC R0
CLR C
SUBB A,@R0
JNC NO_SWAP ; ENSURE LOWEST TASK NUMBER (HIGHEST PRIORITY)
; TRICKLES DOWN TO READYQ BOTTOM, READY TO RUN
SWAP_NOW:
MOV A,@R0
MOV @R0,B
INC R0
MOV @R0,A
DEC R0
NO_SWAP:
CJNE R0,#READYQ,NEXT_PAIR ; ONE PASS DONE, HENCE EXIT
EXIT_QSORT:
POP B
POP 08
POP ACC
RET
; ***********************************************
END
/* PaulosV5C.h */
/* for use with PaulosV5C.a51 RTOS program */
/* written by Paul P. Debono – FEBRUARY 2005 */
#define uchar unsigned char
#define uint unsigned int
// The following calls do not receive parameters, hence are not
// declared with an underscore prefix in the a51 file
void SET_IDLE_MODE(void);
void SET_POWER_DOWN(void);
void DEFER(void);
void KILL(void);
uchar SCHEK(void);
uchar RUNNING_TASK_ID(void);
void WAITV(void);
// The following calls do receive parameters, hence are declared
// with an underscore prefix in the a51 file
void INIT_RTOS(uchar IEMASK);
void RTOSGOMSEC(uchar msecs,uchar prior);
void SIGNL(uchar task);
void WAITI(uchar intnum);
void WAITT(uint ticks);
void WAITS(uint ticks);
void CREATE(uchar task,uint *taskadd);
void PERIODIC(uint ticks);
/* ==================================================== */
/* ADD-ON MACROS */
/* ================================================= */
/* Macro ‘WAITM’ Used to wait for a max of 1 MINUTE */
/* Use ONLY with an RTOSGOMSEC(1,0) – 1 milli seconds tick time */
#define WAITM(s, ms) WAITT((uint)(##s*1000 +##ms))
/* Macro ‘WAITH’ Used to wait for a max of 18h 12m */
/* 66535 * 4 * 250 MILLISECONDS */
/* Use ONLY with an RTOSGOMSEC(250,0) – 250 milli seconds tick time */
#define WAITH(H, M, S) {WAITT((uint)(3600*##H + 60*##M + ##S)); \
WAITT((uint)(3600*##H + 60*##M + ##S)); \
WAITT((uint)(3600*##H + 60*##M + ##S)); \
WAITT((uint)(3600*##H + 60*##M + ##S)); \
}
/* Macro ‘WAITD’ Used to wait for a max of 7D 14h 2m */
/* 66535 * 40 * 250 MILLISECONDS */
/* Use ONLY with an RTOSGOMSEC(250,0) – 250 milli seconds tick time */
#define WAITD(D, H, M) {WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
WAITT((uint)(8640*##D + 360*##H + 6*##M)); \
}
/* Macro ‘PERIODICA’ Used to wait for a max of 4h 33m */
/* Use with an RTOSGOMSEC(250,0) – 250 milli seconds tick time */
#define PERIODICA(H, M, S) PERIODIC((uint)(14000*##H + 240*##M + 4*##S))
/* Macro ‘PERIODICM’ Used to wait for a max of 1 MINUTE */
/* Use with an RTOSGOMSEC(1,0) – 1 milli seconds tick time */
#define PERIODICM(s, ms) PERIODIC((uint)(##s*1000 +##ms))
/* ======================================================== */
;
; RTMACROSV5C.A51
;
; RTOS EQUATES
; FOR USE WITH PAULOSV5C.A51 RTOS.
;
EXT0_INT_VECTOR EQU (INT_VECTOR_BASE + 03H)
;TIM0_INT_VECTOR IS AVAILABLE ONLY IF USING EPROM VERSION 2 ON THE FLITE 32
TIM0_INT_VECTOR EQU (INT_VECTOR_BASE + 0BH)
EXT1_INT_VECTOR EQU (INT_VECTOR_BASE + 13H)
TIM1_INT_VECTOR EQU (INT_VECTOR_BASE + 1BH)
SER0_INT_VECTOR EQU (INT_VECTOR_BASE + 23H)
TIM2_INT_VECTOR EQU (INT_VECTOR_BASE + 2BH)
; RTOS EQUATES
;
IF (HALFMSEC = 1)
CLOCK EQU 430 ; timer clock (11059/12 = 922) counts for 1 msec
ELSE
CLOCK EQU 860
ENDIF ; assuming 11.0592 MHz crystal
; (Used 760 for 1 msec to compensate for overheads)
; You can experiment with other values depending on accuracy required.
BASIC_TICK EQU (65535 – CLOCK + 1)
NOOFPUSHES EQU 13 ; NUMBER OF PUSHES AT BEGINNING OF TASK CHANGE
; I.E. PUSHES IN PushBank0.
IDLE_TASK EQU NOOFTSKS ; main endless loop in C application given
; a task number equal to NOOFTSKS
NOT_TIMING EQU 0H
; TASK FLAG MASKS
SIGS EQU 10000000B ; 128
SIGW EQU 01000000B ; 64
SIGV EQU 00100000B ; 32
SER0W EQU 00010000B ; 16
EXT1W EQU 00000100B ; 4
EXT0W EQU 00000001B ; 1
IF (TICK_TIMER = 2)
TIM0W EQU 00000010B ; 2
TIM1W EQU 00001000B ; 8
ELSEIF (TICK_TIMER = 1)
TIM0W EQU 00000010B ; 2
TIM2W EQU 00001000B ; 8
ELSEIF (TICK_TIMER = 0)
TIM1W EQU 00000010B ; 2
TIM2W EQU 00001000B ; 8
ENDIF
; ========================================================
; ========================================================
; RTOS MACROS
;
SetBank MACRO BankNumber
IF BankNumber = 0
CLR RS0
CLR RS1
ELSEIF BankNumber = 1
SETB RS0
CLR RS1
ELSEIF BankNumber = 2
SETB RS1
CLR RS0
ELSEIF BankNumber = 3
SETB RS1
SETB RS0
ENDIF
ENDM
Ext2Int MACRO ; MOVES R0 DATA FROM EXT DPTR POINTER TO INTERNAL R1 POINTER
MOV R1,#MAIN_STACK
MOV R0,#STACKSIZE
NEXT11:
MOVX A,@DPTR
MOV @R1,A
INC DPTR
INC R1
DJNZ R0,NEXT11
ENDM
Int2Ext MACRO ; MOVES R0 DATA FROM INTERNAL R1 POINTER TO EXT DPTR PONTER
; USES R0, R1, ACC AND DPTR
MOV R1,#MAIN_STACK
MOV R0,#STACKSIZE
NEXT12:
MOV A,@R1
MOVX @DPTR,A
INC DPTR
INC R1
DJNZ R0,NEXT12
ENDM
Push_Bank0_Reg MACRO ; 13 PUSHES
PUSH ACC
PUSH B
PUSH PSW
PUSH DPL
PUSH DPH
PUSH 00
PUSH 01
PUSH 02
PUSH 03
PUSH 04
PUSH 05
PUSH 06
PUSH 07
ENDM
Pop_Bank0_Reg MACRO
POP 07
POP 06
POP 05
POP 04
POP 03
POP 02
POP 01
POP 00
POP DPH
POP DPL
POP PSW
POP B
POP ACC
ENDM
Push_HalfB0_Reg MACRO ; R7 NOT PUSHED, USED FOR PASSING PARAMETER
PUSH ACC ; BACK TO MAIN CALLING PROGRAM
PUSH B
PUSH PSW
PUSH DPL
PUSH DPH
PUSH 00
PUSH 01
PUSH 02
PUSH 03
ENDM
Pop_HalfB0_Reg MACRO ; R7 NOT POPPED, USED FOR PASSING PARAMETER
POP 03 ; BACK TO MAIN CALLING PROGRAM
POP 02
POP 01
POP 00
POP DPH
POP DPL
POP PSW
POP B
POP ACC
ENDM
DEC2REGS MACRO LowReg, HighReg
LOCAL HIGHOK
CLR C ; Clear For SUBB
MOV A,LowReg ; Move Low Of DPTR To A
SUBB A,#1 ; Subtract 1
MOV LowReg,A ; Store Back
JNC HIGHOK
MOV A,HighReg ; Get High Of DPTR
SUBB A,#0 ; Subtract CY If Set
MOV HighReg,A ; Move Back
HIGHOK:
ENDM
LOADREGSXDATA MACRO LowReg, HighReg
MOVX A,@DPTR
MOV LowReg,A
INC DPTR
MOVX A,@DPTR
MOV HighReg,A
ENDM
LOADXDATAREGS MACRO LowReg, HighReg
MOV A,LowReg
MOVX @DPTR,A
INC DPTR
MOV A,HighReg
MOVX @DPTR,A
ENDM
DPTRPLUSA MACRO
ADD A,DPL ; Add ‘A’ To DPL
MOV DPL,A ; Move Result To DPL
MOV A,DPH ; Get DPH
ADDC A,#0 ; If Carry Set, This Will Increment
MOV DPH,A ; Move Bck To DPH
ENDM
; ***********************************************
; ***********************************************
;TASKSTKV5C.a51
$NOMOD51
#include “..\Headers\reg52.h”
; check your own correct path
USING 1 ; SET ASIDE BANK 1
;
; ****************************************************************************
; I M P O R T A N T
INT_VECTOR_BASE EQU 7FFDH ; INTERRUPT VECTOR TABLE BASE ADDRESS
TICK_TIMER EQU 2 ; TIMER USED FOR RTOS SCHEDULER
USING_INT EQU 0 ; SET TO 1 IF USING INTERRUPTS (WAITI)
PERIODIC_CMD EQU 1 ; IF NOT USING PERIODIC COMMAND SET TO ZER0
; TO CLEAR UP SOME INTERNAL IDATA MEMORY
HALFMSEC EQU 0 ; SET TO 1 TO CHECK INTERRUPTS EVERY 1/2 MSEC
; ELSE RTOS WOULD CHECK EVERY 1 MSEC
NOOFTSKS EQU 5 ; CAN BE MORE, SAY 20 TASKS (numbered 0 to 19)
MAIN_STACK EQU 44H ; CONFIRM LOCATION WITH KEIL FILE *.M51
; see variable ?STACK in IDATA
;
STACKSIZE EQU 25H ; 20H MINIMUM
;
; NOTE *************************************************************************
; MODIFY ABOVE TO REFLECT YOUR APPLICATION PROGRAM AND HARDWARE
; *****************************************************************************
;
; THESE ARE THE FOUR MAIN PARAMETERS WHICH YOU MIGHT NEED TO ADJUST,
; DEPENDING ON YOUR APPLICATION.
;
; A STACK SIZE OF 20H SHOULD BE ADEQUATE FOR MOST APPLICATIONS.
;
; ****************************************************************************
#include “..\PaulosRTOS\RTMACROSV5C.a51”
#include “..\PaulosRTOS\PaulosV5C.a51”
; ****************************************************************************