avrx_tasking.s

来自「血凝仪检测系统,硬件电路部分由正弦波产生模块、前级放大与滤波模块、检测线圈、锁相」· S 代码 · 共 462 行

S
462
字号
#include        "avrx.inc"
/*
Copyright 1999-2001, Larry Barello
larry@barello.net

        Revision History
20010515- Added support for IAR c_stack (initialize Y in task def)
20010403- Ported to avrx-ctoasm.inc

20010307- Moved AvrXStack into RAM variable so it can be set from main().
        - Added function AvrXSetKernelStack

*/
        _MODULE(avrx_tasking)
;+
;----------------------------------------------------
; Critical Data Structures
;
; - RunQueue is the head of the run queue.
; - Running is current running task - once inside a Prolog/Epilog
;       section, the RunQueue might get re-arranged so we need to track
;       who we are.
; - Syslevel is a counter tracking how many times we have reentered
; the kernel, or, how many interrupts are nested (same thing).
;-
        _DATASECTION
        _GLOBAL(RunQueue, 2)     ; Head of the run queue
        _GLOBAL(Running, 2)      ; Current running task
        _GLOBAL(AvrXStack, 2)    ; Pointer to the kernel stack
#ifdef  __IAR_SYSTEMS_ASM__
        _GLOBAL(AvrXCStack, 2)   ; Pointer to C parameter stack
#endif
        _GLOBAL(SysLevel, 1)

        _CODESECTION
;+
; --------------------------------------------------
; void IntProlog(void)
;
; Pushes entire register context onto the stack, returning a frame pointer
; to the saved context.  If running in user mode upon entry (SysLevel == 0xFF)
; then switches the stack to the kernel and stores the frame pointer in the
; current processes PID.
;
; PASSED:       Nothing
; RETURN:       Y = Frame Pointer (See AvrX.inc)
;               R0 = pid.state (Single Step Flag) or 0
; ASSUMES:      
; USES:         R14-R18, R26-R32, SysLevel
; CALLS:                
; NOTES:        Does not muck with R16-R25 so passed parameters from C to
;               AvrX functions can be accessed directly after a call to Prolog.
;
;-
	_FUNCTION(IntProlog)
	
IntProlog:                      ; 3 cycles
	push	R29
	push	R28
	push	R27
	push	R26
        push    R25
        push    R24
        push    R23
        push    R22
        push    R21
        push    R20
        push    R19
        push    R18
        push    R17
        push    R16
        push    R15
        push    R14
        push    R13
        push    R12
        push    R11
        push    R10
        push    R9
        push    R8
        push    R7
        push    R6
        push    R5
        push    R4
        push    R3
        push    R2
        push    R1
        push    R0
        in      R0, SREG
        push    R0

	in	Yl, SPL
	in	Yh, SPH         ; Grab frame pointer

	ldd	Xl, Y+_RetLo
	ldd	Xh, Y+_RetHi
        std     Y+_R30, Zl
        std     Y+_R31, Zh      ; Swap return address with Z

#ifdef SINGLESTEPSUPPORT
        clr     R0
#endif /* SINGLESTEPSUPPORT */
	
	lds	R1, SysLevel
	inc	R1
	sts	SysLevel, R1  ; (80)
	brne	AlreadyInKernel ; Check if first time in kernel

	lds	Zh, Running+NextH    ; Point to running task
	lds	Zl, Running+NextL
	std	Z+PidSP+NextH, Yh
	std	Z+PidSP+NextL, Yl

#ifdef  SINGLESTEPSUPPORT
        ldd     tmp0, Z+PidState ; Return to caller SingleStepFlag.
        sbrs    tmp0, SingleStep
        rjmp    SkipDequeue
        
        ldd     R1, Z+PidNext+NextH  ; Dequeue current task
        sts     RunQueue+NextH, R1
        ldd     R1, Z+PidNext+NextL
        sts     RunQueue+NextL, R1
        mov     R0, tmp0        ; Return to caller SingleStepFlag.
        cbr     tmp0, BV(SingleStep)
        std     Z+PidState, tmp0 ; Clear flag in PID
SkipDequeue:
#endif /* SINGLESTEPSUPPORT */

        lds	tmp0, AvrXStack+NextL
	out	SPL, tmp0
	lds	tmp0, AvrXStack+NextH
	out	SPH, tmp0         ; Swap to kernel stack
AlreadyInKernel:                ; (85/102)
	clr     R1              ; R1 = __Zero__ for Avr-gcc
        mov     Zl, Xl          ; scratch in IARICC
        mov     Zh, Xh
	ijmp			; Return to caller (89/106)
        _ENDFUNC

/*+
; --------------------------------------------------
; _Epilog
;
; Restore previous context (kernel or user).
; If task has SingleStep flag set, then generate an interrupt
; before returning to the task.
;
; PASSED:       
; RETURN:       
; ASSUMES:      SysLevel >= 0 (running on kernel stack)
; USES:         SysLevel
; CALLS:                
-*/
        _EXTERN(GenerateInterrupt)
        _FUNCTION(Epilog)
Epilog:
        pop     R30
        pop     R30
        _ENDFUNC
	_FUNCTION(_Epilog)
_Epilog:
        BeginCritical
        lds     R16, SysLevel    ; Interrupts off..
        dec     R16
        brge    SkipTaskSwap

        lds     Zh, RunQueue+NextH
        lds     Zl, RunQueue+NextL
        adiw    Zl, 0
        breq    _IdleTask

        sts     Running+NextH, Zh
        sts     Running+NextL, Zl   ; Update current running task

        ldd     Yh, Z+PidSP+NextH
        ldd     Yl, Z+PidSP+NextL
        out     SPL, Yl
        out     SPH, Yh         ; 20 cycles
#ifdef  SINGLESTEPSUPPORT     
        ldd     R0, Z+PidState  ; X, Y and R0 available
        sbrc    R0, SingleStep
        rcall   GenerateInterrupt
#endif
SkipTaskSwap:                   ; 20/6
        sts     SysLevel, R16
        pop     R0
        out     SREG, R0
        pop     R0
        pop     R1
        pop     R2
        pop     R3
        pop     R4
        pop     R5
        pop     R6
        pop     R7
        pop     R8
        pop     R9
        pop     R10
        pop     R11
        pop     R12
        pop     R13
        pop     R14
        pop     R15
        pop     R16
        pop     R17
        pop     R18
        pop     R19
        pop     R20
        pop     R21
        pop     R22
        pop     R23
        pop     R24
        pop     R25
        pop     R26
        pop     R27
        pop     R28
        pop     R29
        pop     R30
        pop     R31
        EndCriticalReturn       ; 97/83 cycles with interrupts off

; Jump here if there are no entries in the _RunQueue.

_IdleTask:
        BeginIdle               ; Any interrupt will EndIdle
        EndCritical             ; Enable interrupts
        sleep                   ; Power Down..
        EndIdle
        rjmp    _Epilog
        _ENDFUNC

/*+
;-------------------------------------------------
; void * AvrXSetKernelStack(char * newstack);
;
; PASSED: Pointer to end of new stack or NULL
; RETURN: pointer to end of stack
;
-*/
        _FUNCTION(AvrXSetKernelStack)
        
AvrXSetKernelStack:
        pop     R31
        pop     R30
        subi    p1l, 0
        sbci    p1h, 0
        brne    sks1
        in      p1l, SPL
        in      p1h, SPH
sks1:
        sts     AvrXStack+NextL, p1l
        sts     AvrXStack+NextH, p1h
        ijmp

        _ENDFUNC
/*+
; --------------------------------------------------
; *PID AvrXInitProcess(*TaskControlBlock)
; *PID AvrXRunTask(*TaskControlBlock)
;
; Initialize the PID based upon the TCB table.  Run Task
; "resumes" the process as well
; 
; PASSED:       R25:R24 = TCB
; RETURNS:      R25:R24 = PID
; USES:         X, Z and R0
; CALLS:
; NOTES:        TCB, Layout, below: 
;               2 PID
;               1 Flags
;               2 STACK
;               2 Entry
-*/
        _FUNCTION(AvrXRunTask)

AvrXRunTask:
        rcall   AvrXInitTask
        rjmp    AvrXResume
        _ENDFUNC
        
        _FUNCTION(AvrXInitTask)

AvrXInitTask:
#ifdef __IAR_SYSTEMS_ASM__
        mov     tmp0, Xl
        mov     tmp1, Xh
#endif
        mov     Zl, p1l
        mov     Zh, p1h
        
        rcall   lpm_inc         ; Get Stack pointer
        mov     Xl, R0
        rcall   lpm_inc         
        mov     Xh, R0
        
#ifdef  TASKEXIT
        _EXTERN(AvrXTaskExit)
        ldi     tmp2, lo8_pm(AvrXTaskExit)
        st      X, tmp2          ; !Pre-dec is not a PUSH!
        ldi     tmp2, hi8_pm(AvrXTaskExit)
        st      -X, tmp2          ; Push task exit onto stack

        rcall   lpm_inc
        st      -X, R0
        rcall   lpm_inc
        st      -X, R0          ; Push task entry point
#else
        rcall   lpm_inc
        st      X, R0
        rcall   lpm_inc
        st      -X, R0          ; Push task entry point
#endif /* TASKEXIT */

        ldi     tmp2, 0
#ifdef __IAR_SYSTEMS_ASM__
        st      -x, tmp2        ; R31
        st      -x, tmp2        ; R30
        rcall   lpm_inc
        mov     tmp3, R0
        rcall   lpm_inc
        st      -x, R0          ; Frame is stored in reverse order
        st      -x, tmp3        ; lo8(c_stack)
        ldi     tmp3, 29        ; R0-R27 and SREG get "pushed"
#else
        ldi     tmp2, 0
        ldi     tmp3, 33        ; R0-R31 and SREG get "pushed"
#endif
PushRegisters:
        st      -X, tmp2
        dec     tmp3            ; Fill rest of context with Zeros
        brne    PushRegisters   ; Decrement stack pointer
        
        sbiw    Xl, 1           ; because pre dec is not a push!

        rcall   lpm_inc
        mov     r1l, R0          ; Get PID -> R23:22
        rcall   lpm_inc
        mov     r1h, R0
        rcall   lpm_inc         ; Priority -> R0
        mov     Zl, r1l
        mov     Zh, r1h

        std     Z+PidSP+NextL, Xl
        std     Z+PidSP+NextH, Xh     ; Stash context frame pointer

        std     Z+PidPriority, R0
        ldi     Xl, BV(SuspendedBit) | BV(SuspendBit)
        std     Z+PidState, Xl  ; Mark state so we can "resume" pid
        std     Z+PidNext+NextH, tmp2
        std     Z+PidNext+NextL, tmp2; Clear pid.next
#ifdef __IAR_SYSTEMS_ASM__
        mov     Xh, tmp1
        mov     Xl, tmp0
#endif
        ret

lpm_inc:                        ; Tiny helper...
        lpm
        adiw    Zl, 1
        ret
        _ENDFUNC
/*+
; --------------------------------------------------
; AvrXResume
;
; Takes a PID and inserts it into the run queue.
;
; Uses two flags, Suspended and Suspend to determine if a task is
; actually suspended, as opposed to just being marked for suspension.
; Mark _SUSP to make QueuePid not queue, Mark _SUSPENDED when attempt
; to queue is made (e.g. not blocked on a semaphore).  This is because
; we can't "resume" something that is blocked on a semaphore, even though
; it is marked for suspension.
;
; PASSED:       R25:24 = PID to queue
; RETURNS:      R24 : 0 = Ok, 1 = Already queued (error)
; USES:         
; ASSUMES:
; NOTES:        
-*/
        _FUNCTION(AvrXResume)

AvrXResume:                     ; User Entry Point
        AVRX_Prolog

        mov     Zl, p1l
        mov     Zh, p1h
        
        ldd     tmp0, Z+PidState
        cbr     tmp0, BV(SuspendBit)
        std     Z+PidState, tmp0  ; clear Suspend flag

        sbrs    tmp0, SuspendedBit
        rjmp    ar00
        
        cbr     tmp0, BV(SuspendedBit)  ; If removed from run queue,
        std     Z+PidState, tmp0  ; Clear flag and queue it up.
        
        rcall   _QueuePid       ; If found, then add to run queue
ar00:
        rjmp    _Epilog
        _ENDFUNC
/*+
; --------------------------------------------------
; _QueuePid
;
; Takes a PID and inserts it into the run queue.  The run queue is sorted
; by priority.  Lower numbers go first.  If there are multiple tasks of equal 
; priority, then the new task is appended to the list of equals (round robin)
;
; PASSED:       p1h:p1l = PID to queue
; RETURNS:      
; USES:         Z, tmp0-3 and SREG, RunQueue
; ASSUMES:
; NOTES:        Returns with interrupts on.
;
-*/
        _FUNCTION(_QueuePid)
        
_QueuePid:                              ; Kernel entry point only
        mov     Zl, p1l
        mov     Zh, p1h

        ldd     tmp0, Z+PidState          ; Xh = Priority & Flags
        andi    tmp0, (BV(SuspendBit) | BV(IdleBit)) ; if marked Suspended or idle
        brne    _qpSUSPEND
        
        ldd     tmp2, Z+PidPriority
        mov     tmp0, Yl
        mov     tmp1, Yh                  ; Preserve Y through this routine
        ldi     Yl, lo8(RunQueue)
        ldi     Yh, hi8(RunQueue)
        BeginCritical
_qp00:
        mov     Zl, Yl
        mov     Zh, Yh
        ldd     Yl, Z+PidNext+NextL
        ldd     Yh, Z+PidNext+NextH     ; Z = current, X = Next
        adiw    Yl, 0
        breq    _qp01                   ; End of queue, continue
        ldd     tmp3, Y+PidPriority
        cp      tmp2, tmp3
        brsh    _qp00                   ; Loop until pri > PID to queue
_qp01:
        std     Z+NextH, p1h
        std     Z+NextL, p1l    ; Prev->Next = Object
        mov     Zh, p1h
        mov     Zl, p1l
        std     Z+NextH, Yh     ; Object->Next = Next
        std     Z+NextL, Yl
        mov     Yl, tmp0
        mov     Yh, tmp1
        EndCriticalReturn

_qpSUSPEND:
        sbr     tmp0, BV(SuspendedBit)  ; Mark suspended and return
        std     Z+PidState, tmp0
        EndCriticalReturn
        
        _ENDFUNC
        _END

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?