|
一、OS_CPU_A.ASM 文件包含 4 个移植相关的函数:
OSStartHighRdy();
OSCtxSw();
OSIntCtxSw();
OSTickISR();
二、OSStartHighRdy()
OSStartHighRdy() 仅由 OSStart() 启动多任务时调用一次,其意为启动优先级最高的任务。先看 OSStart() 代码:
void OSStart (void)
{
INT8U y;
INT8U x;
if (OSRunning == FALSE) {
y = OSUnMapTbl[OSRdyGrp]; /* 找到最高优先级任务优先级号 */
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* 指向已就绪的最高优先级任务TCB */
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy(); /* 启动任务 */
}
} 再看 OSStartHighRdy() 代码:
_OSStartHighRdy PROC FAR
MOV AX, SEG _OSTCBHighRdy ; 重装载 DS
MOV DS, AX ;
;
CALL FAR PTR _OSTaskSwHook ; 调用用户自定义函数
;
MOV AL, 1 ; OSRunning = TRUE
MOV BYTE PTR DS:_OSRunning, AL ;多任务运行已经要开始了
;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX+0] ;
;
POP DS ; Load task's context
POP ES
POPA
;
IRET ; Run task
_OSStartHighRdy ENDP 说明:
1、OSTCBHighRdy 是指向已就绪的最高优先级任务 TCB 的指针,是一个 32位的远指针--指针的段地址和偏移量都在指针内。因为 uC/OS-II是在大模式下编译的,所以缺省状态下产生的指针为远指针。每次使用远指针时,都要重装段寄存器,远指针的可选址目标不超过64K,因为远指针增减运算时,段地址不参与运算。所以,在 OSStartHighRdy 函数开头中有:
MOV AX, SEG _OSTCBHighRdy
MOV DS, AX ;重装段寄存器
2、OSTaskSwHook
该函数是用户自定义函数,调用该函数时,中断必须禁止。
3、LES BX, DWORD PTR DS:_OSTCBHighRdy
_OSTCBHighRdy 指针段地址(DS)存往 ES 中,偏移量存往 BX 中。
MOV SS, ES:[BX+2] ;待运行任务 TCB 结构中第一个元素为其堆栈指针,此处取该指针的段地址,
;然后存往 SS 中
MOV SP, ES:[BX+0] ;取该指针的偏移量存往 SP
三、_OSCtxSw
OSCtxSw 是任务切换的底层操作函数,其代码如下:
_OSCtxSw PROC FAR
;
PUSHA ;当前任务相关寄存器保存
PUSH ES ;
PUSH DS ;
;
MOV AX, SEG _OSTCBCur ;重装在当起任务TCB的指针以防被改变
MOV DS, AX ;
;
LES BX, DWORD PTR DS:_OSTCBCur ; OSTCBCur->OSTCBStkPtr = SS:SP
MOV ES:[BX+2], SS ;当前任务堆栈指针存往 TCB 中
MOV ES:[BX+0], SP ;
;
CALL FAR PTR _OSTaskSwHook ; Call user defined task switch hook
;
MOV AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy
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
MOV BYTE PTR DS:_OSPrioCur, AL ;
;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SS, ES:[BX+2] ;取出就绪即运行任务堆栈指针改写到SS:SP中
MOV SP, ES:[BX] ;
;
POP DS ;恢复新任务的上下文
POP ES ;
POPA ;
;
IRET ; Return to new task
;
_OSCtxSw ENDP 四、OSIntCtxSw
在中断服务子程序最后,OSIntExit() 调用 OSIntCtxSw 做任务切换,故OSIntCtxSw 是中断级的任务切换。在使用 OSIntCtxSw 之前,中断已经发生,所以无需保存 CPU寄存器,堆栈指针也被服务子程序保存在任务控制块 OS_TCB 中。OSIntCtxSw 代码和 OSCtxSw 基本相同。
在代码中有一句,MOV AX, SEG _OSTCBCur
MOV DS, AX
说是重装 DS,以防被改变了。我认为像在 C 中的这些变量,至少是远指针变量,它们的段地址应该都是一样的。所以才有下面的 MOV AX,WORD PTR DS: _OSTCBHighRdy + 2。这里可以看到,OSTCBHighRdy 指针的段地址也是 DS 里的地址。
五、OSTickISR
PC 机的始终节拍中断向量为 0x08 处,但 uC/OS-II 则会让 0x80 这里的中断向量指向它的中断服务--OSTickISR,而原来的中断服务则将其安排到 0x81 处暂存起来。OSTickISR 的汇编代码如下:
_OSTickISR PROC FAR
;
PUSHA ;中断进,保存CPU相关寄存器
PUSH ES
PUSH DS
;
MOV AX, SEG(_OSIntNesting) ;取得OSIntNesting变量的段地址
MOV DS, AX
INC BYTE PTR DS:_OSIntNesting ; OSIntNesting++
CMP BYTE PTR DS:_OSIntNesting, 1 ; if (OSIntNesting == 1)
JNE SHORT _OSTickISR1
MOV AX, SEG(_OSTCBCur) ;第一层中断则保存好当前任务堆栈指针
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 ;若达到DOS的中断则调到0x81进行中断处理
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.
;EOI-->end of interrupt,清中断
_OSTickISR3:
CALL FAR PTR _OSTimeTick ; Process system tick
;
CALL FAR PTR _OSIntExit ;中断结束,退出
;
POP DS ; Restore interrupted task's context
POP ES
POPA
;
IRET ; Return to interrupted task
;
_OSTickISR ENDP
;
END
|
|