In Assembly language

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”

; ****************************************************************************