📄 os_cpu_a.asm
字号:
; AX
; OFFSET of task code address
; SEGMENT of task code address
; Flags to load in PSW (High memory)
;*********************************************************************************************************
_OSIntCtxSw PROC FAR
;
CALL FAR PTR _OSTaskSwHook ; Call user defined task switch hook;OSIntCtxSw()做的第1件事是,调用可
;由用户定义的任务接口函数OSTaskSwHook()。注意:当调用OSTaskSwHook()时,OSTCBCur指向当前任务的任务控制块OS_TCB,而
;OSTCBHighRdy指向新任务的任务控制块OS_TCB。这样可以从OSTaskSwHook()中访问这2个任务的任务控制块OS_TCB中的任何一个。
;同样,如果不想使用OSTaskSwHook(),则可以注释掉这条语句。这样在任务切换时,可以节省几个时钟周期。
;
MOV AX, SEG _OSTCBCur ; Reload DS in case it was altered报警
MOV DS, AX ;
;
MOV AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy当从OSTaskSwHook()返回时,OSTCBHighRdy复制给
;OSTCBCur,这是因为此时新任务应该是当前任务。
MOV DX, WORD PTR DS:_OSTCBHighRdy ;
MOV WORD PTR DS:_OSTCBCur+2, AX ;
MOV WORD PTR DS:_OSTCBCur, DX ;
;
MOV AL, BYTE PTR DS:_OSPrioHighRdy ; OSPrioCur = OSPrioHighRdy;OSPrioHighRdy也复制给OSPrioCur,这也是因为
;此时新任务应该是当前任务。
MOV BYTE PTR DS:_OSPrioCur, AL
;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr;至此,OSCtxSw()应该把新任务的运行
;环境装入到处理器的寄存器中。这是通过从新任务的任务控制块OS_TCB中获取寄存器SS和寄存器SP的值来实现的。
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX] ;
;
POP DS ; Load new task's context从堆栈中弹出和恢复CPU的其余寄存器。
POP ES ;
POPA ;
;
IRET ; Return to new task执行IRET指令,设置新任务的程序计数器和状态字。执行
;了这一指令后,处理器恢复执行新任务。注意:在执行OSIntCtxSw()时,中断是关掉的;在执行用户定义的OSTaskSwHook()接口函数时,
;中断也是关掉的。
;
_OSIntCtxSw ENDP
PAGE ; /*$PAGE*/
;*********************************************************************************************************
; HANDLE TICK ISR
;
; Description: This function is called 199.99 times per second or, 11 times faster than the normal DOS
; tick rate of 18.20648 Hz. Thus every 11th time, the normal DOS tick handler is called.
; This is called chaining. 10 times out of 11, however, the interrupt controller on the PC
; must be cleared to allow for the next interrupt.
在OS_CPU.H,时钟节拍频率中已经提到实时操作系统的时钟节拍频率应为10~100Hz。在PC中,时钟节拍由硬件定时器产生,硬件定时器会中
断CPU,间隔是54.93ms(18.206 48HZ)。笔者将时钟节拍频率设为200HZ。PC机时钟节拍的中断向量为0x08,UCOS将此向量截取,使之指向UC
OS的时钟节拍中断服务子程序OSTickISR(),而原先的中断向量保存在中断向量129(0x81)中(参见PC.C文件中的PC_DOSSaveReturn()函数)。
为满足DOS的需要,原先的中断服务还是每隔54.93ms(实际上还要短些)调用1次。在UCOS中,当调用OSStart()启动多任务环境后,时钟中断
的作用是非常重要的但是PC环境下,启动UCOS之前就已经有时钟中断发生了。实际上希望在UCOS初始化完成之后,再发生时钟中断,调用
OSTickISR()。为了防止中断服务子程序ISR在UCOS准备好之前调用OSTickISR(),需要作以下的工作:
Main()
调用OSInit()初始化UCOS;
调用PC_DOSSaveReturn()见PC.C
调用PC_VectSet把OSCtxSw放在向量0x80处;
建立至少1个应用任务
调用OSStart(),以运行多任务。
第一个任务要作的工作有:
把OSTickISR()的起始地址放在向量0x08处
时钟节拍从18.206 48HZ改变为200HZ。
PC上时钟处理很巧妙,
;
; Arguments : none
;
; Returns : none
;
; Note(s) : The following C-like pseudo-code describe the operation being performed in the code below.
;
; Save all registers on the current task's stack;如同所有UCOS的中断服务子程序一样,所有的寄存器须保存在当前
任务的堆栈中。
; OSIntNesting++;当进入中断服务子程序ISR时,须告诉UCOS,进入中断服务子程序了。可以通过OSIntEnter()实现,或以
直接给中断嵌套OSIntNesting加1的方式实现。直接给OSIntNesting加1会更快一些。OSIntEnter()实现,或以直接给中断嵌套层数
OSIntNesting加1的方式实现。直接给OSIntNesting加1会更快一些。OSIntEnter()会检查OSIntNesting是否超过了255,这样,中断嵌套会更
安全些
; if (OSIntNesting == 1) {
; OSTCBCur->OSTCBStkPtr = SS:SP;如果中断服务子程序是中断的第一层,即没有中断嵌套,则需要把堆栈指针保存在
当前任务的任务控制块OS_TCB中。
; }
; OSTickDOSCtr--;
; if (OSTickDOSCtr == 0) {
; OSTickDOSCtr = 11; 计数器OSTickDOSCtr减一,当OSTickDOSCtr为0时,调用DOS的时钟节拍处理函数。这种情况每隔
54.93ms发生一次
; INT 81H; Chain into DOS every 54.925 mS
; (Interrupt will be cleared by DOS)清中断
; } else {
; Send EOI to PIC; Clear tick interrupt by sending an End-Of-Interrupt to the 8259
; PIC (Priority Interrupt Controller)
11次中有10次,中断优先级控制器PIC会收到一个清除中断的指令。注意:当调用DOS的时钟节拍处理函数时,这一操作不是必须的,因为
DOS的时钟节拍处理函数直接清除中断源。
; }
; OSTimeTick(); Notify uC/OS-II that a tick has occured;OSTickISR()调用OSTimeTick(),这样UCOS就
给所有延迟任务的等待时间以及有时限的等待某事件发生的任务的等待时间的节拍参数减1.
; OSIntExit(); Notify uC/OS-II about end of ISR当所有中断服务子程序完成时,调用OSIntExit()。如
果中断服务子程序(或者其他嵌套的中断服务子程序)使一个更高优先级的任务进入了就绪态,并且当前中断服务子程序已经脱离了中断嵌套
,那么OSIntExit()不再返回OSTickISR()。OSIntCtxSw()恢复新任务的所有寄存器,并执行一个IRET指令。如果中断服务子程序没有脱离
中断嵌套,或者中断服务子程序没有使更高优先级的任务进入就绪态,OSIntExit()就返回OSTickISR()。
; Restore all registers that were save on the current task's stack;
; Return from Interrupt; 如果OSIntExit()返回,则是因为OSIntExit()没有发现更高优先级的任务。这样被中断任务
的寄存器会被恢复。当执行IRET指令时,中断服务子程序返回,让被中断了的任务继续运行。
;*********************************************************************************************************
;
_OSTickISR PROC FAR
;
PUSHA ; Save interrupted task's context此段注释同上文
PUSH ES
PUSH DS
;
MOV AX, SEG(_OSIntNesting) ; Reload DS
MOV DS, AX
INC BYTE PTR DS:_OSIntNesting ; Notify uC/OS-II of ISR
;
CMP BYTE PTR DS:_OSIntNesting, 1 ; if (OSIntNesting == 1)
JNE SHORT _OSTickISR1
MOV AX, SEG(_OSTCBCur) ; Reload DS
MOV DS, AX
LES BX, DWORD PTR DS:_OSTCBCur ; OSTCBCur->OSTCBStkPtr = SS:SP
MOV ES:[BX+2], SS ;
MOV ES:[BX+0], SP ;
;
_OSTickISR1:
MOV AX, SEG(_OSTickDOSCtr) ; Reload DS
MOV DS, AX
DEC BYTE PTR DS:_OSTickDOSCtr
CMP BYTE PTR DS:_OSTickDOSCtr, 0
JNE SHORT _OSTickISR2 ; Every 11 ticks (~199.99 Hz), chain into DOS
;
MOV BYTE PTR DS:_OSTickDOSCtr, 11
INT 081H ; Chain into DOS's tick ISR
JMP SHORT _OSTickISR3
_OSTickISR2:
MOV AL, 20H ; Move EOI code into AL.
MOV DX, 20H ; Address of 8259 PIC in DX.
OUT DX, AL ; Send EOI to PIC if not processing DOS timer.
;
_OSTickISR3:
CALL FAR PTR _OSTimeTick ; Process system tick
;
CALL FAR PTR _OSIntExit ; Notify uC/OS-II of end of ISR
;
POP DS ; Restore interrupted task's context
POP ES
POPA
;
IRET ; Return to interrupted task
;
_OSTickISR ENDP
;
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -