📄 cxsk53s.s90
字号:
;******************************************************
;
; Copyright (C) 2002
; CMX Systems, Inc.
; 12276 San Jose Blvd, #119
; Jacksonville, FL 32223
;
; All Rights Reserved
;
; CONFIDENTIAL
;*******************************************************
#include "macros.m90"
; for ATMEL AVR processor
; Note: for 2 byte shorts
; ASSUMES compiled C code with switches -v1 or -v3
; ASSUMES SMALL memory model
; IAR uses the Y register for parameters passing and locals
; CMX uses the Z register for pointer to index through cmx_tcb array
; WORDS (INTS) are store low byte, high byte
; For this K_I_Scheduler, we are going to set up 2 stacks
; one for the system stack and one for the user stack (Using Y reg)
RSEG CODE(0)
CASEON
fwlink equ H'00 ;forward wait link
bwlink equ H'02 ;backward wait link
ftlink equ H'04 ;forward time link
btlink equ H'06 ;backward time link
tcbstate_low equ H'08 ;task state
tcbstate_high equ H'09 ;task state
trig equ H'0a ;the number of triggers (starts) for task
priority equ H'0b ;task priority, 0 is highest
tcbtimer equ H'0c ;task countdown delay timer
nxttcb equ H'0e ;ptr to next TCB (task control block)
task_addr equ H'10 ;address of where task's code begins
stk_start equ H'12 ;the task's stack address
stk_save equ H'14 ;the task's stack save address
system_stk_start equ H'16 ;the task's stack address
system_stk_save equ H'18 ;the task's stack save address
; tcbstate high byte, 1'st byte of tcbstate
IDLE equ H'01 ;task not able to run, no triggers
READY equ H'02 ;the task ready to run
RESUME equ H'04 ;the task ready to run, resume where it left off
RUNNING equ H'08 ;the task is the running task
TIME equ H'10
; tcbstate low byte, 2'nd byte of tcbstate
RESOURCE equ H'01 ;waiting on time
WAIT equ H'02 ;waiting
SEND_MESG equ H'08 ;waiting for task that recieved message,to wake me
WAIT_MESG equ H'10 ;waiting for message
FLAGS equ H'20 ;waiting on flag
TIME_EXPIRED equ H'40 ;the time period specified has ellapsed
SEMAPHORE equ H'80 ;the time period specified has ellapsed
LOW_POWER_ACTIVE equ 0 ;set to 1, to enable K_OS_Low_Power_Func function
SLICE_ENABLE equ 1 ;set to 1, to enable time slicing code testing
CMXTRACKER_ENABLE EQU 0 ;SET TO 1 IF USING CMXTRACKER, otherwise 0
IF (CMXTRACKER_ENABLE)
EXTERN previoustcb
EXTERN cmxtracker_in_task
ENDIF
SAVE_RAMPZ equ 0 ;Set to 1 (one) if you are using a derivative that has this register
EXTERN stack_holder
EXTERN interrupt_stack
EXTERN system_interrupt_stack
EXTERN activetcb
EXTERN active_priority
EXTERN cmx_tcb
EXTERN K_I_Intrp_Pipe_Out
EXTERN K_I_Timer_Task
EXTERN K_OS_Low_Power_Func
EXTERN TSLICE_SCALE
EXTERN SLICE_ON
EXTERN tslice_count
EXTERN int_count
EXTERN locked_out
EXTERN cmx_flag1
EXTERN ie_holder
PUBLIC K_OS_Intrp_Entry
PUBLIC K_OS_Intrp_Exit
PUBLIC K_I_Sched
PUBLIC K_I_Scheduler
PUBLIC K_OS_Enable_Interrupts
PUBLIC K_OS_Disable_Interrupts
PUBLIC K_OS_Save_Interrupts
PUBLIC K_OS_Restore_Interrupts
RSEG CODE
preempted equ H'01 ;preemption flag position
do_timer_tsk equ H'02 ;timer task flag position
do_time_slice equ H'04 ;do time slice, next slice task if possible
slice_enable equ H'08 ;time slice enabled
do_coop_sched equ H'10 ;cooperative scheduling flag position
do_int_pipe equ H'20 ;interrupt pipe needs processing
idle_flag equ H'40 ;used by CMX to see if all tasks cannot run
cmx_active equ H'80 ;shows that the CMX OS entered
bit_preempted equ 0
bit_do_timer_tsk equ 1
bit_do_time_slice equ 2
bit_slice_enable equ 3
bit_do_coop_sched equ 4
bit_do_int_pipe equ 5
bit_idle_flag equ 6
bit_cmx_active equ 7
K_I_Sched:
ST -Y,R16 ;save R16
IN R16,LOW(63) ;get SREG
ST -Y,R16 ;save SREG
sched_x:
ST -Y,R31 ;save rest of registers
ST -Y,R30
; ST -Y,R29 ;do not save Y
; ST -Y,R28
ST -Y,R27
ST -Y,R26
ST -Y,R25
ST -Y,R24
ST -Y,R23
ST -Y,R22
ST -Y,R21
ST -Y,R20
ST -Y,R19
ST -Y,R18
ST -Y,R17
ST -Y,R15
ST -Y,R14
ST -Y,R13
ST -Y,R12
ST -Y,R11
ST -Y,R10
ST -Y,R9
ST -Y,R8
ST -Y,R7
ST -Y,R6
ST -Y,R5
ST -Y,R4
ST -Y,R3
ST -Y,R2
ST -Y,R1
ST -Y,R0
IF SAVE_RAMPZ > 0
IN R16,LOW(RAMPZ) ;get RAMPZ
ST -Y,R16 ;save RAMPZ
ENDIF
sched_int:
; Z REG is used for activetcb pointer
LDS R30,LWRD(activetcb) ;get current active task tcb
LDS R31,LWRD((activetcb+1))
LDD R16,Z+tcbstate_high
CPI R16,RUNNING ;see if task was running
BREQ set_resume ;yes, so set flag indicating the task should
;finish its code, because the interrupt
;preempted it, during a task locked out
LDI R16,H'FF
STS LWRD(active_priority),R16 ;set priority to lowest.
RJMP sched_cont
set_resume:
LDI R16,RESUME ;save task's new state
STD Z+tcbstate_high,R16
sched_cont:
STD Z+stk_save,R28 ;save task's current user stack address
STD Z+stk_save+1,R29
IN R20,0x3D ;GET SYSTEM STACK
IN R21,0x3E
STD Z+system_stk_save,R20 ;save task's current system stack address
STD Z+system_stk_save+1,R21
K_I_Scheduler:
CLI ;turn interrupts off
LDS R28,LWRD(interrupt_stack) ;load in interrupt and K_I_Scheduler user stack
LDS R29,LWRD((interrupt_stack+1))
LDS R20,LWRD(system_interrupt_stack) ;load in interrupt and K_I_Scheduler system stack
LDS R21,LWRD((system_interrupt_stack+1))
OUT 0x3D,R20
OUT 0x3E,R21
LDI R16,H'01
STS LWRD(int_count),R16 ;set int count to 1, for using stack
STS LWRD(locked_out),R16 ;set locked out to 1, critical region
sched_again:
;
SEI ;enable interrupts
rescan1:
LDS R16,LWRD(cmx_flag1) ;Get CMX flags
SBRS R16,LOW(bit_do_timer_tsk) ;see if K_I_Timer_Task needs to be called
RJMP rescan2 ;No, jump
CLI ;turn interrupts off ;yes, turn interrupts off
LDS R16,LWRD(cmx_flag1) ;get flags again
CBR R16,do_timer_tsk ;clear respective bit
STS LWRD(cmx_flag1),R16 ;store it
SEI ;enable interrupts
PUSH R30
PUSH R31
CALL K_I_Timer_Task ;call CMX timer func. that will dec. timer
POP R31
POP R30
rescan2:
LDS R16,LWRD(cmx_flag1) ;Get CMX flags
SBRS R16,LOW(bit_do_int_pipe) ;see if K_I_Intrp_Pipe_Out needs to be called
RJMP rescan4 ;No, jump
PUSH R30 ;yes
PUSH R31
CALL K_I_Intrp_Pipe_Out ;go process interrupt pipe contents
POP R31
POP R30
rescan4:
LDS R16,LWRD(cmx_flag1) ;Get CMX flags
SBRS R16,LOW(bit_preempted) ;see if preempted flag set
RJMP rescan5 ;No, jump
CLI ;yes, turn interrupts off
LDS R16,LWRD(cmx_flag1) ;get flags again
CBR R16,(preempted | do_coop_sched | do_time_slice | slice_enable)
SBR R16,idle_flag
STS LWRD(cmx_flag1),R16
SEI ;enable interrupts
LDS R30,LWRD(cmx_tcb+nxttcb) ;load in highest priority user task
LDS R31,LWRD(cmx_tcb+nxttcb+1)
RJMP midpaus2 ;now go see if ready
rescan5:
LDS R16,LWRD(cmx_flag1) ;Get CMX flags
SBRS R16,LOW(bit_do_coop_sched) ;see if preempted flag set
RJMP rescan6 ;No, jump
CLI ;yes, turn interrupts off
LDS R16,LWRD(cmx_flag1) ;get flags again
CBR R16,(do_coop_sched | do_time_slice | slice_enable)
STS LWRD(cmx_flag1),R16
SEI ;enable interrupts
RJMP findready ;get next task in line
rescan6:
IF SLICE_ENABLE > 0
LDS R16,LWRD(cmx_flag1) ;Get CMX flags
SBRS R16,LOW(bit_do_time_slice) ;see if time slice flag set
RJMP midpaus2 ;No, jump
CLI ;turn interrupts off
LDS R16,LWRD(cmx_flag1)
CBR R16,do_time_slice ;clear bit
STS LWRD(cmx_flag1),R16
SEI ;enable interrupts
RJMP findready ;get next task in line
ENDIF
midpaus2:
LDD R18,Z+tcbstate_high ;get task state
ANDI R18,(RESUME | READY) ;see if capable of running.
BRNE task_resume ;yes, let task run
findready:
LDD R16,Z+nxttcb ;get task state
LDD R17,Z+nxttcb+1 ;get task state
MOV R30,R16
MOV R31,R17
CPI R16,LOW(cmx_tcb) ;see if at end of linked list
BRNE midpaus2 ;no, go test this task to see if runnable
CPI R17,HIGH(cmx_tcb) ;see if at end of linked list
BRNE midpaus2 ;no, go test this task to see if runnable
IF LOW_POWER_ACTIVE > 0
LDS R16,LWRD(cmx_flag1) ;get flags
SBRS R16,LOW(bit_idle_flag) ;have we traveled down complete link list?
RJMP no_power_down ;no, then no power down
power_down:
CALL @K_OS_Low_Power_Func ;go to user written low power mode
no_power_down:
ENDIF
LDS R30,LWRD(cmx_tcb+nxttcb) ;load in highest priority user task
LDS R31,LWRD(cmx_tcb+nxttcb+1)
RJMP rescan1 ;go test flags again
task_resume:
LDD R0,Z+priority ;get task's priority
STS LWRD(active_priority),R0 ;load it into global variable.
IF SLICE_ENABLE > 0
LDS R16,LWRD(SLICE_ON) ;see if time slicing enabled
CPI R16,0
BREQ no_slice ;no, exit
LDS R16,LWRD(cmx_flag1)
SBRC R16,LOW(bit_slice_enable) ;test slice bit
RJMP no_slice ;already set, good
CLI ;turn interrupts off
LDS R16,LWRD(cmx_flag1)
SBR R16,slice_enable ;set it
STS LWRD(cmx_flag1),R16
SEI ;enable interrupts
LDS R16,LWRD(TSLICE_SCALE) ;now load time slice count
STS LWRD(tslice_count),R16
no_slice:
ENDIF
STS LWRD(activetcb),R30 ;store active task tcb
STS LWRD((activetcb+1)),R31
IF CMXTRACKER_ENABLE > 0
LDS R20,LWRD(previoustcb) ;get previous active task tcb
LDS R21,LWRD((previoustcb+1))
CP R30,R20
BRNE update
CP R31,R21
BREQ same1
update:
STS LWRD(previoustcb),R30 ;store active task tcb
STS LWRD((previoustcb+1)),R31
PUSH R30 ;yes
PUSH R31
CALL cmxtracker_in_task ;go inform tracker
POP R31
POP R30
LDD R18,Z+tcbstate_high ;get task state
same1:
ENDIF
LDI R16,RUNNING ;show that task is now executing
STD Z+tcbstate_high,R16 ;store task new state
ANDI R18,READY ;now see if task should start or resume
BREQ resume_good
RJMP task_ready ;task should start at beginning brace
resume_good:
CLI
LDD R28,Z+stk_save ;task is resuming, load saved stack address
LDD R29,Z+stk_save+1
LDD R20,Z+system_stk_save ;task is resuming, load saved system stack address
LDD R21,Z+system_stk_save+1
OUT 0x3D,R20
OUT 0x3E,R21
SEI
IF SAVE_RAMPZ > 0
LD R16,Y+ ;GET RAMPZ
OUT LOW(RAMPZ),R16
ENDIF
LD R0,Y+ ;restore registers
LD R1,Y+
LD R2,Y+
LD R3,Y+
LD R4,Y+
LD R5,Y+
LD R6,Y+
LD R7,Y+
LD R8,Y+
LD R9,Y+
LD R10,Y+
LD R11,Y+
LD R12,Y+
LD R13,Y+
LD R14,Y+
LD R15,Y+
LD R17,Y+
LD R18,Y+
LD R19,Y+
LD R20,Y+
LD R21,Y+
LD R22,Y+
LD R23,Y+
LD R24,Y+
LD R25,Y+
LD R26,Y+
LD R27,Y+
; LD R28,Y+ ;do not restore Y
; LD R29,Y+
LD R30,Y+
LD R31,Y+
CLI ;turn interrupts off
LDS R16,LWRD(cmx_flag1) ;get flags
ANDI R16,~(idle_flag | do_time_slice) ;reset these 2 flags
STS LWRD(cmx_flag1),R16
ANDI R16,(do_int_pipe | do_timer_tsk) ;test to see if bit(s) set
BREQ resume_good1 ;no then go let task run
SEI
RJMP sched_x ;yes, then process bits first
resume_good1:
LDI R16,H'00
STS LWRD(int_count),R16 ;set int count to 1, for using stack
STS LWRD(locked_out),R16 ;set locked out to 1, critical region
LD R16,Y+ ;GET SREG
OUT LOW(63),R16
LD R16,Y+
; SEI ;enable interrupts
RET ;return to task
task_ready:
LDD R0,Z+task_addr ;it's location in ROM
LDD R1,Z+task_addr+1
LDD R20,Z+system_stk_start ;task starting, load system stack start address
LDD R21,Z+system_stk_start+1
CLI ;turn interrupts off
LDS R16,LWRD(cmx_flag1)
ANDI R16,~(idle_flag | do_time_slice)
STS LWRD(cmx_flag1),R16
ANDI R16,(do_int_pipe | do_timer_tsk) ;test to see if bit(s) set
BREQ task_ready1 ;no then go let task run
LDI R16,READY ;put task back to ready
STD Z+tcbstate_high,R16 ;store task new state
;No sense to do much with this task, because it wants to start at beginning address
RJMP sched_again ;yes, then process bits first
task_ready1:
LDD R28,Z+stk_start ;get task user stack start address
LDD R29,Z+stk_start+1
OUT 0x3D,R20
OUT 0x3E,R21
LDI R16,H'00
STS LWRD(int_count),R16 ;set int count to 0
STS LWRD(locked_out),R16 ;set locked out to 0
MOV R30,R0
MOV R31,R1
SEI ;enable interrupts
IJMP ;jump to task's beginning address
RSEG CODE(0)
K_OS_Intrp_Entry:
; for devices with up to 16 bit PC (128K ROM), return PC size is 2 bytes
; for devices with up to 22 bit PC (8 Meg ROM), return PC size is 3 bytes
; This way, when interrupt issues a RETI, it will return to K_OS_Intrp_Exit
; and not the PC of interrupted code
ST -Y,R19 ;save up 4 registers (6 for 22 bit PC)
ST -Y,R18 ;2 are used for INTERRUPT return address, 2 for K_OS_Intrp_Exit address
ST -Y,R17
ST -Y,R16
LDS R16,LWRD(cmx_flag1) ;we might not have to test for active?
SBRS R16,LOW(bit_cmx_active)
RJMP cxint_in1
LDS R16,LWRD(int_count) ;increment int_count (nested counter)
INC R16
STS LWRD(int_count),R16
cxint_in1:
LDI R16,LOW(K_OS_Intrp_Exit/2) ;place K_OS_Intrp_Exit address on stack, so
LDI R17,((K_OS_Intrp_Exit/2) >> 8) ;interrupt returns to K_OS_Intrp_Exit
POP R18 ;get return address of interrupt that called K_OS_Intrp_Entry
POP R19
PUSH R16
PUSH R17 ;push K_OS_Intrp_Exit on system stack, so interrupt returns to here
PUSH R19 ;push original return back on now
PUSH R18
LD R16,Y+ ;restore registers
LD R17,Y+
LD R18,Y+
LD R19,Y+
RET ;return to caller
K_OS_Intrp_Exit: ;when interrupt issues the RETI, it will now come here
ST -Y,R16 ;save reg 16
IN R16,LOW(63) ;get SREG
ST -Y,R16 ;save that as well
CLI ;turn interrupts off
LDS R16,LWRD(cmx_flag1) ;we might not have to test for active?
SBRS R16,LOW(bit_cmx_active)
RJMP cxint_ex1
LDS R16,LWRD(int_count) ;get nested int_count
DEC R16 ;decrement it
STS LWRD(int_count),R16 ;write it back
BRNE cxint_ex1 ;yes, exit. Not zero
LDS R16,LWRD(locked_out)
CPI R16,0 ;see if we are locked out
BRNE cxint_ex1 ;yes, exit
LDS R16,LWRD(cmx_flag1) ;we might not have to test for active?
ANDI R16,(do_int_pipe | preempted | do_timer_tsk | do_time_slice | do_coop_sched)
BREQ cxint_ex1 ;yes, exit
LDI R16,H'01
STS LWRD(locked_out),R16 ;set locked out to 1, critical region
SEI
JMP sched_x ;otherwise go save task context.
cxint_ex1:
LD R16,Y+ ;GET SREG
OUT LOW(63),R16 ;restore it
LD R16,Y+ ;restore reg 16
RET ;return to where interrupt had interrupted
K_OS_Disable_Interrupts:
CLI ;disable interrupts
RET
K_OS_Enable_Interrupts:
SEI ;enable interrupts
RET
K_OS_Save_Interrupts:
ST -Y,R16 ;save reg 16
IN R16,LOW(63) ;get SREG
CLI ;turn interrupts off
STS LWRD(ie_holder),R16 ;save SREG
LD R16,Y+ ;restore r16
RET
K_OS_Restore_Interrupts:
ST -Y,R16 ;save reg 16
LDS R16,LWRD(ie_holder) ;get saved SREG
OUT LOW(63),R16 ;put back to original value
LD R16,Y+ ;restore reg 16
RET
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -