发布我学习uC/OS-II的过程(学习最后,自己改编了作者的例子一,运行OK)
学习uCOS-II已经一年多了,还没有完整的阅读一遍书上的例子,下面就以作者第一个例子展开学习吧!////////////////////////////////////////////////////////////////////////////////////////////////////////
//学校:成都理工大学
//系:应用数学系
//专业:应用数字专业。
//日期:2009年3月
//说明:本简要合成程序是uCOS-II提供的EX1_X86
////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "includes.h"
/*
*********************************************************************************************************
* CONSTANTS(宏定义)
*********************************************************************************************************
*/
#defineTASK_STK_SIZE 512 /* 任务堆栈尺寸=512 (单位=WORD) */
#defineN_TASKS 10 /* 任务数量=10(不包括TaskStart()任务) */
/*
*********************************************************************************************************
* VARIABLES(变量)
*********************************************************************************************************
*/
OS_STK TaskStk; /* 10个任务的堆栈区域 */
OS_STK TaskStartStk; /* 任务TaskStart()的堆栈 */
char TaskData; /* 每个任务显示字符存储区域 */
OS_EVENT *RandomSem; /* 信号量 */
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
voidTask(void *data); /* 其余10个任务,这10个任务共用一个函数名称 */
voidTaskStart(void *data); /* 起始任务(负责uCOS-II初始化,修改中断向量*/
/* 调用TaskStartCreateTasks()创建其余10个任务*/
/* 然后若检测“ESC”键,则返回MSDOS */
staticvoidTaskStartCreateTasks(void); /* 创建其余10个任务的任务 */
staticvoidTaskStartDispInit(void); /* 界面显示初始化 */
staticvoidTaskStartDisp(void); /* 多任务显示子程序 */
voidmain (void)
{
PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK); /* 清屏幕 */
OSInit(); /* uC/OS-II初始化 */
/* 包括(1)、初始化任务就绪表 */
/* (2)、初始化任务控制块链表 */
/* (3)、初始化事件表 */
/* (4)、初始化内存管理 */
/* (5)、初始化消息队列 */
/* (6)、初始化空闲任务 */
/* (5)、初始化统计任务 */
PC_DOSSaveReturn(); /* 保存MSDOS环境 */
/* 包括(1)、保存BIOS硬件定时中断向量(0x08)*/
/* (2)、将保留中断号0x81指向硬件定时*/
/* 中断 */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
RandomSem = OSSemCreate(1); /* Random number semaphore */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk, 0);
OSStart(); /* Start multitasking */
} 连载二:
////////////////////////////////////////////////////////////////////////////////////////////////////////
//学校:成都理工大学
//系:应用数学系
//专业:应用数字专业。
//日期:2009年3月
//说明:本简要合成程序是uCOS-II提供的EX1_X86
////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "includes.h"
/*
*********************************************************************************************************
* CONSTANTS(宏定义)
*********************************************************************************************************
*/
#defineTASK_STK_SIZE 512 /* 任务堆栈尺寸=512 (单位=WORD) */
#defineN_TASKS 10 /* 任务数量=10(不包括TaskStart()任务) */
/*
*********************************************************************************************************
* VARIABLES(变量)
*********************************************************************************************************
*/
OS_STK TaskStk; /* 10个任务的堆栈区域 */
OS_STK TaskStartStk; /* 任务TaskStart()的堆栈 */
char TaskData; /* 每个任务显示字符存储区域 */
OS_EVENT *RandomSem; /* 信号量 */
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
voidTask(void *data); /* 其余10个任务,这10个任务共用一个函数名称 */
voidTaskStart(void *data); /* 起始任务(负责uCOS-II初始化,修改中断向量*/
/* 调用TaskStartCreateTasks()创建其余10个任务*/
/* 然后若检测“ESC”键,则返回MSDOS */
staticvoidTaskStartCreateTasks(void); /* 创建其余10个任务的任务 */
staticvoidTaskStartDispInit(void); /* 界面显示初始化 */
staticvoidTaskStartDisp(void); /* 多任务显示子程序 */
voidmain (void)
{
PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK); /* 清屏幕 */
OSInit(); /* uC/OS-II初始化 */
/* 包括(1)、初始化任务就绪表 */
/* (2)、初始化任务控制块链表 */
/* (3)、初始化事件表 */
/* (4)、初始化内存管理 */
/* (5)、初始化消息队列 */
/* (6)、初始化空闲任务 */
/* (5)、初始化统计任务 */
PC_DOSSaveReturn(); /* 保存MSDOS环境 */
/* 包括(1)、保存BIOS硬件定时中断向量(0x08)*/
/* (2)、将保留中断号0x81指向硬件定时*/
/* 中断 */
//------------------------------------------------------------------------------------------------
//#defineuCOS 0x80 /* Interrupt vector # used for context switch*/
//宏定义uCOS的路径=\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU.H(文件中)
//------------------------------------------------------------------------------------------------
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
/* 设置中断号=0x80自定义中断服务程序的向量 */
/* 地址指向OSCtxSw,即指向普通任务切换子程序*/
/* 说明:任务切换包括: */
/* (1)、普通任务切换 */
/* (2)、中断任务切换 */
RandomSem = OSSemCreate(1); /* Random number semaphore */
/* 创建一个信号量,保护Borland C++产生的随机*/
/* 数。信号量的作用是防止多个任务同时访问同 */
/* 一个资源 */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk, 0);
/* 创建任务TaskStart */
OSStart(); /* 在就绪表中查找最高优先级的任务号 */
} /* 然后调用OSStartHighRdy()执行最高 */
/* 优先级任务 */
/*
*********************************************************************************************************
* START MULTITASKING(启动多任务操作系统)
*
* Description: This function is used to start the multitasking process which lets uC/OS-II manages the
* task that you have created.Before you can call OSStart(), you MUST have called OSInit()
* and you MUST have created at least one task.
* 描述 : 当已经调用了OSStart(),则这个函数可以启动多任务操作系统,让uC/OS-II管理你已经创建的任务。
* 前提是你已经调用了OSInit(),并且已经至少创建了一个任务(只需要创建系统提供的空闲任务即可)。
* Arguments: none
* 入口参数: 无
* Returns : none
* 出口参数: 无
* Note : OSStartHighRdy() MUST:
* a) Call OSTaskSwHook() then,
* b) Set OSRunning to TRUE.
* c) Load the context of the task pointed to by OSTCBHighRdy.
* d_ Execute the task.
* 注释: OSStartHighRdy()函数执行的动作是:
* a) 调用Call OSTaskSwHook(),然后
* b) 设置OSRunning=TRUE
* c) 根据任务控制块OSTCBHighRdy,作任务切换动作。
* 实际上任务切换的实质:
* (1)、从当前任务的私栈中恢复SS:SP
* (2)、从当前任务的私栈中恢复AX,BX,CX,DX,SI,DI,BP
* (3)、执行IRET,从堆栈中弹出CS:IP
* d) 自动跳转到CS:IP处,执行这个最高优先级任务
* OSStartHighRdy()函数的存储路径=\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_A.ASM
*********************************************************************************************************
*/
voidOSStart (void)
{
INT8U y;
INT8U x;
if (OSRunning == FALSE) /* 如果多任务操作系统还没有运行 */
{
y = OSUnMapTbl; /* Find highest priority's task priority number */
x = OSUnMapTbl]; /* 查找优先级最高的任务号 */
OSPrioHighRdy = (INT8U)((y << 3) + x); /* 优先级最高的任务号=(y << 3) + x) */
OSPrioCur = OSPrioHighRdy; /* OSPrioCur中保存优先级最高的任务号 */
OSTCBHighRdy= OSTCBPrioTbl;/* Point to highest priority task ready to run */
/* 得到优先级最高任务的任务控制块OSTCB */
OSTCBCur = OSTCBHighRdy; /* OSTCBCur中保存优先级最高任务的任务控制块 */
OSStartHighRdy(); /* Execute target specific code to start task*/
/* 调用OSStartHighRdy(),执行这个最高优先级任务*/
}
}
/*
*********************************************************************************************************
* CREATE A SEMAPHORE(创建一个信号量)
*
* Description: This function creates a semaphore.
*
* Arguments: cnt is the initial value for the semaphore.If the value is 0, no resource is
* available (or no event has occurred).You initialize the semaphore to a
* non-zero value to specify how many resources are available (e.g. if you have
* 10 resources, you would initialize the semaphore to 10).
*
* Returns : != (void *)0is a pointer to the event control clock (OS_EVENT) associated with the
* created semaphore
* == (void *)0if no event control blocks were available
*********************************************************************************************************
*/
OS_EVENT*OSSemCreate (INT16U cnt)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
OS_EVENT*pevent;
if (OSIntNesting > 0) /* See if called from ISR ... */
{ /* 如果当前正在执行中断服务程序 */
return ((OS_EVENT *)0); /* ... can't CREATE from an ISR */
} /* 创建失败,返回(OS_EVENT *)0 */
/* 注:中断服务子程序中不能调用创建信号量 */
OS_ENTER_CRITICAL(); /* 关全局中断 */
pevent = OSEventFreeList; /* Get next free event control block */
/* 从空闲事件控制块链表中获得一个事件控制块 */
if (OSEventFreeList != (OS_EVENT *)0) /* See if pool of free ECB pool was empty */
{ /* 如果获得一个事件控制块成功 */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
} /* 修正空闲事件控制块链表头指针 ,使它指向*/
/* 下一个空闲控制块 */
OS_EXIT_CRITICAL(); /* 开放全局中断 */
if (pevent != (OS_EVENT *)0) { /* 如果获得的事件控制块可用 */
pevent->OSEventType = OS_EVENT_TYPE_SEM; /* 登记事件控制块的类型=信号量 */
pevent->OSEventCnt= cnt; /* 信号量的初始值存入申请的事件控制块中 */
pevent->OSEventPtr= (void *)0; /* Unlink from ECB free list */
OS_EventWaitListInit(pevent); /* Initialize to 'nobody waiting' on sem. */
}
return (pevent);
} 连载三、
/*
*********************************************************************************************************
* INITIALIZE A TASK'S STACK
*
* Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the
* stack frame of the task being created.This function is highly processor specific.
*
* Arguments: task is a pointer to the task code
*
* pdata is a pointer to a user supplied data area that will be passed to the task
* when the task first executes.
*
* ptos is a pointer to the top of stack.It is assumed that 'ptos' points to
* a 'free' entry on the task stack.If OS_STK_GROWTH is set to 1 then
* 'ptos' will contain the HIGHEST valid address of the stack.Similarly, if
* OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address
* of the stack.
* (指向任务堆栈栈顶的指针,它是指向一个任务堆栈空间
*
* opt specifies options that can be used to alter the behavior of OSTaskStkInit().
* (see uCOS_II.H for OS_TASK_OPT_???).
*
* Returns : Always returns the location of the new top-of-stack' once the processor registers have
* been placed on the stack in the proper order.
*
* Note(s) : Interrupts are enabled when your task starts executing. You can change this by setting the
* PSW to 0x0002 instead.In this case, interrupts would be disabled upon task startup.The
* application code would be responsible for enabling interrupts at the beginning of the task
* code.You will need to modify OSTaskIdle() and OSTaskStat() so that they enable
* interrupts.Failure to do this will make your system crash!
* 初始化任务堆栈
//入口参数:
// void (*task)(void *pd)=当前任务代码的起始地址
// *pdata =参数指针
// *ptos =当前任务堆栈的栈顶地址
// opt =当前任务的优先级
//出口参数:
// 任务堆栈初始化后,新堆栈指针的位置。
*********************************************************************************************************
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////
//存储路径=C:\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_C.C
// 预备知识:MSDOS操作系统方面的知识:
//(1)、MSDOS操作系统堆栈的生长方向:MSDOS堆栈是从下往上(从高端地址往低端地址)生长的。
//
// 低端(0x0000) ---------------------
// | |
// --------------------
// | |
// --------------------
// | |
// (0xFFFD) --------------------
// (0xFFFE) | | <---SP
// 高端(0xFFFF) --------------------
// 系统刚上电时,堆栈指针SP位置
//
// 低端(0x0000) ---------------------
// | |
// --------------------
// | |
// --------------------
// | AX |<---SP
// (0xFFFD) --------------------
// (0xFFFE) | |
// 高端(0xFFFF) --------------------
// 堆栈中压入AX寄存器后,堆栈指针SP位置
//
//(2)、MSDOS操作系统存储器中的中断向量区
//
// 低端 ----------------------------------------
// (0x0000) | INT(0)中断服务程序的IP |
// ----------------------------------------
// | INT(0)中断服务程序的CS |
// ----------------------------------------
// (0x0004) | INT(1)中断服务程序的IP |
// ----------------------------------------
// | INT(1)中断服务程序的CS |
// -----------------------------------------
// (4*N) | INT(N)中断服务程序的IP |
// ----------------------------------------
// | INT(N)中断服务程序的CS |
// 高端(0xFFFF) -----------------------------------------
//
//
//(3)、CALL DEST(段间直接调用指令)的堆栈过程如下:
// (a)、(SP)<---(SP)-2
// (b)、((SP+1),(SP))<---(CS)
// (c)、(SP)<---(SP)-2
// (d)、((SP+1),(SP))<---(IP)
// (e)、(IP)<--- DEST指定地址的偏移地址
// (f)、(CS)<--- DEST指定地址的段移地址
//
// -------------------- --------------------- 低端(0x0000)
// | | | |
// -------------------- ---------------------
// | | | IP | <---SP
// -------------------- ---------------------
// | | | CS |
// -------------------- ---------------------
// | | <---SP | |
// -------------------- ---------------------- 高端(0xFFFF)
// 执行CALL DEST指令前 执行CALL DEST指令后
//
// 说明:执行CALL DEST(段间直接调用指令)时,首先保存返回地址(调用处的地址),
// 把返回地址的CS和IP压入堆栈,然后跳转到由DEST指定的地址去执行。
//
//
//(4)、RET(段间返回指令)堆栈过程如下:
//
// (a)、(IP)<---((SP+1),(SP))
// (b)、(SP)<---(SP)+2
// (c)、(CS)<---((SP+1),(SP))
// (d)、(SP)<---(SP)+2
//
// -------------------- --------------------- 低端(0x0000)
// | | | |
// ------------------- ---------------------
// | IP | <---SP | |
// ------------------- ---------------------
// | CS | | |
// ------------------- ---------------------
// | | | | <---SP
// -------------------- --------------------- 高端(0xFFFF)
//
// 说明:RET指令放在函数的末尾,它使函数在功能完成后返回调用程序继续执行,而返回地址是
//调用程序调用函数时存放在堆栈中的。
// 因此执行RET(段间返回指令)时(即从函数返回时),从堆栈中依次弹出IP和CS,然后跳转到
//调用处的地址的下条指令位置继续执行
//
//
//(5)、INT type(软中断指令)堆栈过程如下:
//
// (a)、(SP)<---(SP)-2
// (b)、((SP+1),(SP))<---(PSW)
// (c)、(SP)<---(SP)-2
// (d)、((SP+1),(SP))<---(CS)
// (e)、(SP)<---(SP)-2
// (f)、((SP+1),(SP))<---(IP)
// (g)、(CS)<---(type*4+2)
// (h)、(IP)<---(type*4)
// (i)、 跳转到CS:IP执行中断服务程序
//
// --------------------- --------------------- 低端(0x0000)
// | | | IP | <---SP
// --------------------- ---------------------
// | | | CS |
// --------------------- ---------------------
// | | | PSW |
// --------------------- ---------------------
// | | <---SP | |
// --------------------- --------------------- 高端(0xFFFF)
// 执行INT type指令前 执行INT type指令后
//
// 说明:type=中断号,范围0~255(MSDOS有256个中断源),执行软中断前,
//首先把PSW(程序状态字节,相当于AVR单片机的SREG)压入堆栈,然后将返回地址(调用处地址)
//CS和IP压入堆栈
// 最后将中断号type的中断服务程序的段地址装入CS、偏移地址装入IP,接着跳转到CS:IP
//执行中断号=type的中断服务程序。
//
//
//(5)、IRET(中断返回指令)的堆栈过程如下:
// (a)、(IP)<---((SP+1),(SP))
// (b)、(SP)<---(SP)+2
// (c)、(CS)<---((SP+1),(SP))
// (d)、(SP)<---(SP)+2
// (e)、(PSW)<---((SP+1),(SP)) ---> 弹出程序状态字PSW
// (f)、(SP)<---(SP)+2
//
// --------------------- --------------------- 低端(0x0000)
// | IP | <---SP | |
// --------------------- ---------------------
// | CS | | |
// --------------------- ---------------------
// | PSW | | |
// --------------------- ---------------------
// | | | | <---SP
// --------------------- --------------------- 高端(0xFFFF)
// 执行IRET指令前 执行IRET指令后
//------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////
OS_STK*OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
INT16U *stk;
opt = opt; /* 'opt' 未使用,这样写是为了防止编译器编译时产生错误警告*/
stk = (INT16U *)ptos; /* stk指向任务堆栈的栈项 */
//----------------------------------------------------------------------------------
//模仿任务的调用,参见CALL DEST(段间直接调用指令)
//----------------------------------------------------------------------------------
*stk-- = (INT16U)FP_SEG(pdata); /* Borland C/C++编译器用堆栈而不是寄存器传递参数pdata */
*stk-- = (INT16U)FP_OFF(pdata);
*stk-- = (INT16U)FP_SEG(task); /* 任务的段地址压入当前任务的私栈中 */
*stk-- = (INT16U)FP_OFF(task); /* 任务的偏移地址压入当前任务的私栈中 */
//----------------------------------------------------------------------------------
//模仿中断,参见INT type(软中断指令)
//----------------------------------------------------------------------------------
*stk-- = (INT16U)0x0202; /* SW = Interrupts enabled */
*stk-- = (INT16U)FP_SEG(task); /* Put pointer to task on top of stack */
*stk-- = (INT16U)FP_OFF(task); */
//----------------------------------------------------------------------------------
//模仿PUSHA,保护现场
//----------------------------------------------------------------------------------
*stk-- = (INT16U)0xAAAA; /* AX = 0xAAAA */
*stk-- = (INT16U)0xCCCC; /* CX = 0xCCCC */
*stk-- = (INT16U)0xDDDD; /* DX = 0xDDDD */
*stk-- = (INT16U)0xBBBB; /* BX = 0xBBBB */
*stk-- = (INT16U)0x0000; /* SP = 0x0000 */
*stk-- = (INT16U)0x1111; /* BP = 0x1111 */
*stk-- = (INT16U)0x2222; /* SI = 0x2222 */
*stk-- = (INT16U)0x3333; /* DI = 0x3333 */
*stk-- = (INT16U)0x4444; /* ES = 0x4444 */
//----------------------------------------------------------------------------------
//模仿PUSH DS,保护现场
//----------------------------------------------------------------------------------
*stk = _DS; /* DS = Current value of DS */
return ((OS_STK *)stk);
}
/////////////////////////////////////////////////////////////////////
//执行*OSTaskStkInit () 前,堆栈示意图
/////////////////////////////////////////////////////////////////////
//
// | 存储器地址低端 |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | |
// --------------------
// | | <---- SP=*ptos
// --------------------
// | 存储器地址低端 |
/////////////////////////////////////////////////////////////////////
//执行*OSTaskStkInit () 后,堆栈示意图
/////////////////////////////////////////////////////////////////////
//
// | 存储器地址低端 |
// --------------------
// | DS | <---- SP
// --------------------
// | ES=0x4444 |
// --------------------
// | DI=0x3333 |
// --------------------
// | SI=0x2222 |
// --------------------
// | BP=0x1111 |
// --------------------
// | SP=0x0000 |
// --------------------
// | BX=0xBBBB |
// --------------------
// | DX=0xDDDD |
// --------------------
// | CX=0xCCCC |
// --------------------
// | AX=0xAAAA |
// --------------------
// | OFF task | ----
// -------------------- |
// | SEG task | |----> 模仿中断
// -------------------- |
// | PSW=0x0202 | ----
// --------------------
// | OFF task | ----
// -------------------- |
// | SEG task | |
// -------------------- |----> 模仿任务调用
// | OFF pdata | |
// -------------------- |
// | SEG pdata | ----
// --------------------
// | 存储器地址低端 |
//
// 注:上面两图的SP并不是指CPU当前的堆栈指针,而是针对任务堆栈初始化的堆栈指示。 连载四、 以下需要PC-DOS操作系统知识背景
// 在PC-DOS环境下 运行uC/OS-II以后
// 中断向量表(VECT) 中断向量表(VECT)
// --------------- ----------------
// 0x00 | | 0x00 | |
// ---------------- ----------------
// 0x01 | | 0x01 | |
// ---------------- ----------------
// 0x02 | | 0x02 | |
// ---------------- ----------------
//... | | ... | |
// ---------------- ----------------
// 0x08 | ● ---------> PC-DOS时钟节拍0x08 | ● --------->OSTickISR()
// ---------------- (18.206HZ) ---------------- (200HZ,每11个时钟节拍
//... | | ... | | 执行1次"INT0x81")
// ---------------- ----------------
// 0x7F | | 0x7F | |
// ---------------- ----------------
// 0x80 | ● ---------> 未定义 0x80 | ● ---------> OSCtxSW()
// ---------------- ----------------
// 0x81 | ● ---------> 未定义 0x81 | ● ---------> DOS时钟节拍(0x08中断服务程
// ---------------- ----------------
// | | | |
// | | | |
// | | | |
// ---------------- ----------------
//
// 图4. PC中断向量表(VECT)
//-----------------------------------------------------------------------
//VECT_TICK=0x08,uC/OS-II接管之前,PC机定时中断号=0x08
//-----------------------------------------------------------------------
#defineVECT_TICK 0x08 /* Vector number for 82C54 timer tick */
//-----------------------------------------------------------------------
//VECT_DOS_CHAIN=0x81,uC/OS-II接管之后,将PC机定时中断号(0x08)的中断服务程序向量地址保存到中断向量
//表中断(0x81)位置。采用MSDOS操作系统时代,计算机病毒同样的手法(偷梁换柱)
//-----------------------------------------------------------------------
#defineVECT_DOS_CHAIN 0x81 /* Vector number used to chain DOS */
static INT16U PC_ElapsedOverhead;
static jmp_buf PC_JumpBuf;
static BOOLEAN PC_ExitFlag;
void (*PC_TickISR)(void); /* PC-DOS操作系统 INT0x08中断服务程序地址存放单元
///////////////////////////////////////////////////////////////////////////////////////////////////////
//函数名=PC_VectSet
//功能:设置中断向量表中,中断号vetc对应的中断服务程序地址
//说明:在MSDOS操作系统时代,由于操作系统的中断向量可以任意由用户程序更改,因此计算机病毒也采用同样的
// 方法偷梁换柱,将中断向量表中系统原中断服务程序的段地址和偏移地址,偷偷换成病毒中断服务程序的
// 段地址和偏移地址,同时将中断向量表中系统原中断服务程序的段地址和偏移地址保存在病毒体内。
// 这样,在执行完病毒中断服务程序后,再跳转到原中断服务程序去执行,使得用户根本察觉不到操作系统
// 已经被病毒控制。
//入口:
// vetc=中断号
// void (*isr)(void)=新中断向量服务程序地址(病毒中断服务程序地址)
//出口:
// 无
//说明:在MSDOS操作系统中,内存区域0000:0000为中断向量表,其中保存的是系统256个中断号对应的中断服务程序
//地址,中断向量表每项4个字节,包含段地址(2个字节)和偏移地址(2个字节)。
///////////////////////////////////////////////////////////////////////////////////////////////////////
void PC_VectSet (INT8U vect, void (*isr)(void))
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
INT16U *pvect;
//---------------------------------------------------------------------
//得到中断向量表VECT中,中断号vect对应中断服务程序地址存放单元
//---------------------------------------------------------------------
pvect = (INT16U *)MK_FP(0x0000, vect * 4);
OS_ENTER_CRITICAL();
//---------------------------------------------------------------------
//得到中断向量表VECT中,中断号vect对应中断服务程序地址存放单元(病毒中断服务程序的偏移地址)
//---------------------------------------------------------------------
*pvect++ = (INT16U)FP_OFF(isr);
//---------------------------------------------------------------------
//得到中断向量表VECT中,中断号vect对应中断服务程序地址存放单元(病毒中断服务程序的段地址)
//---------------------------------------------------------------------
*pvect = (INT16U)FP_SEG(isr);
OS_EXIT_CRITICAL();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//函数名=PC_VectGet
//功能:获取中断向量对应的中断服务程序地址
//入口:
// vetc=中断号
//出口:
// 中断服务程序地址
///////////////////////////////////////////////////////////////////////////////////////////////////////
void *PC_VectGet(INT8U vect)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
INT16U *pvect;
INT16U off;
INT16U seg;
//---------------------------------------------------------------------
//得到中断向量表VECT中,中断号vect对应中断服务程序地址存放单元
//---------------------------------------------------------------------
pvect = (INT16U *)MK_FP(0x0000, vect * 4);
OS_ENTER_CRITICAL();
//---------------------------------------------------------------------
//从中断向量表VECT中,中断号vect对应中断服务程序地址存放单元中取出中断服务程序的偏移地址
//---------------------------------------------------------------------
off = *pvect++;
//---------------------------------------------------------------------
//从中断向量表VECT中,中断号vect对应中断服务程序地址存放单元中取出中断服务程序的段地址
//---------------------------------------------------------------------
seg = *pvect;
OS_EXIT_CRITICAL();
//---------------------------------------------------------------------
///返回中断号vect对应中断向量服务程序的地址
//---------------------------------------------------------------------
return (MK_FP(seg, off));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//函数名=PC_DOSSaveReturn
//功能:保存PC-DOS环境设置
//入口:
// 无
//出口:
// 无
////////////////////////////////////////////////////////////////////////////////////////////////////////
void PC_DOSSaveReturn (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
//---------------------------------------------------------------------
//PC_ExitFlag= FALSE,标志着现在开始不希望返回DOS
//---------------------------------------------------------------------
PC_ExitFlag= FALSE; /* Indicate that we are not exiting yet! */
//---------------------------------------------------------------------
//
//---------------------------------------------------------------------
OSTickDOSCtr = 1; /* Initialize the DOS tick counter */
//---------------------------------------------------------------------
//得到PC-DOS INT0x08中断服务程序向量地址
//---------------------------------------------------------------------
PC_TickISR = PC_VectGet(VECT_TICK); /* Get MS-DOS's tick vector */
//-----------------------------------------------------------------------------------
//偷梁换柱,把中断向量表中断号=0x81的中断向量替换成INT0x08中断服务服务程序的向量地址
//简单的说,就是把0x08向量地址填写到中断向量表中0x81中断向量单元
//VECT_DOS_CHAIN=0x81
//PC_TickISR=INT0x08中断服务服务程序的向量地址
//-----------------------------------------------------------------------------------
PC_VectSet(VECT_DOS_CHAIN, PC_TickISR); /* Store MS-DOS's tick to chain */
//------------------------------------------------------------------------------------
//调用函数setjmp(PC_JumpBuf)。这个函数将得到处理器的状态(各个主要寄存器的内容、PSW、AX、
// CX、DX、BX、BP、SP、SI、DI、DS、ES),并把它们保存在一个结构体PC_JumpBuf中。
//------------------------------------------------------------------------------------
setjmp(PC_JumpBuf); /* Capture where we are in DOS */
//------------------------------------------------------------------------------------
//在运行任务期间,用户按下“ESC”键,则置PC_ExitFlag=TRUE,即返回DOS。
//------------------------------------------------------------------------------------
if (PC_ExitFlag == TRUE) { /* See if we are exiting back to DOS */
OS_ENTER_CRITICAL();
//------------------------------------------------------------------------------------
//恢复DOS系统的时钟节拍,每秒钟产生18.2次定时中断
//------------------------------------------------------------------------------------
PC_SetTickRate(18); /* Restore tick rate to 18.2 Hz */
OS_EXIT_CRITICAL();
//------------------------------------------------------------------------------------
//把0x08向量地址填写到中断向量表中0x08中断向量单元
//------------------------------------------------------------------------------------
PC_VectSet(VECT_TICK, PC_TickISR); /* Restore DOS's tick vector */
//------------------------------------------------------------------------------------
//清除屏幕
//------------------------------------------------------------------------------------
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK);/* Clear the display */
//------------------------------------------------------------------------------------
//返回到DOS
//------------------------------------------------------------------------------------
exit(0); /* Return to DOS */
}
} 连载五、
;*********************************************************************************************************
; START MULTITASKING
; void OSStartHighRdy(void)
;
; The stack frame is assumed to look as follows:(堆栈的生长方向见下面:)
;
; OSTCBHighRdy->OSTCBStkPtr --> DS (Low memory)
; ES (内存低端)
; DI
; SI
; BP
; SP
; BX
; DX
; CX
; AX
; OFFSETof task code address (任务起始地址偏移地址=IP)
; SEGMENT of task code address (任务起始地址段地址=CS)
; Flags to load in PSW (CPU状态寄存器)
; OFFSETof task code address (任务起始地址偏移地址=IP)
; SEGMENT of task code address (任务起始地址段地址=CS)
; OFFSETof 'pdata'
; SEGMENT of 'pdata' (High memory)
; (内存高端)
; Note : OSStartHighRdy() MUST:
; a) Call OSTaskSwHook() then,
; b) Set OSRunning to TRUE,
; c) Switch to the highest priority task.
;*********************************************************************************************************
//--------------------------------------------------------------------------------------------------------
//存储路径=C:\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_C.ASM
//--------------------------------------------------------------------------------------------------------
//OSStartHighRdy()由函数OSStart()函数调用,功能是让进入就绪态的优先级最高的任务运行。
//函数OSStartHighRdy()默认指针OSTCBHighRdy指向优先级最高的就绪态任务的任务控制块(OS_TCB)。
//下图是由函数OSTaskCreate()建立的任务的堆栈结构,在OSStart()函数调用OSStartHighRdy()函数
//之前,任务堆栈结构就是这个样子。其中:OSTCBHighRdy->OSTCBStkPtr指向任务堆栈的项端。
//
// | |
// | 存储器地址低端 |
// ----------------------------
// | DS |<----OSTCBHighRdy->OSTCBStkPtr
// ----------------------------
// | ES=0x4444 |
// ----------------------------
// | DI=0x3333 |
// ----------------------------
// | SI=0x2222 |
// ----------------------------
// | BP=0x1111 | ^
// ----------------------------
// | SP=0x0000 |
// ----------------------------
// | BX=0xBBBB |
// ----------------------------
// | DX=0xDDDD |
// ----------------------------
// | CX=0xCCCC |
// ----------------------------
// | AX=0xAAAA |
// ----------------------------
// | OFF task | -------
// -------------------- | |
// | SEG task | |----> 模仿中断
// -------------------- | |
// | PSW=0x0202 | -------
// --------------------
// | OFF task | <---- 执行IRET后SS:SP指向此处
// -------------------- |
// | SEG task |
// ----------------------------
// | OFF pdata |
// ----------------------------
// | SEG pdata |
// --------------------
// | 存储器地址低端 |
//
//
// 图1 任务创建时的8086堆栈结构
//--------------------------------------------------------------------------------------------------------
_OSStartHighRdyPROC FAR
MOV AX, SEG _OSTCBHighRdy ; 获取多任务操作系统最高优先级任务控制块OSTCBHighRdy的数据段段地址 ---> DS
MOV DS, AX ;
;
CALL FAR PTR _OSTaskSwHook ; Call user defined task switch hook
;
MOV AL, 1 ; 设置 OSRunning = TRUE;
MOV BYTE PTR DS:_OSRunning, AL ; 说明:由于MSDOS要求访问变量时,必须指出变量所在段的段地址,
; ; 而变量OSRunning和OSTCBHighRdy在同一段,因此只需得到OSTCBHighRdy的数据段段地址(DS)即可。
//--------------------------------------------------------------------------------------
//说明:在MSDOS操作系统,指令LES reg , src 执行的动作如下:
// (1)(reg) <--- src
// (2)(ES)<--- (src+2)
//例如:LES BX, DWORD PTR DS:_OSTCBHighRdy
// (1)(BX)<--- OSTCBHighRdy
// (2)(ES) <--- (OSTCBHighRdy+2)
//我们知道在任务控制块OS_TCB中,OSTCBStkPtr是指向任务堆栈栈顶的指针,为了汇编语言方便访问成员OSTCBStkPtr,特意定义在结构体的开始位置。
//注解:
// OSStartHighRdy()从任务控制块OS_TCB中获得并恢复堆栈指针
//--------------------------------------------------------------------------------------
LES BX, DWORD PTR DS:_OSTCBHighRdy ; 获取最高优先级任务控制块OSTCBHighRdy的段地址和偏移地址 ---> ES:BX
MOV SS, ES: ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SP, ES: ; 从任务堆栈中获取SS:SP
;
POP DS ; 把任务堆栈中保存的DS值恢复到CPU的DS寄存器中
POP ES ; 把任务堆栈中保存的ES值恢复到CPU的ES寄存器中
POPA ; 把任务堆栈中保存的AX,BX,CX,DX,SI,DI,
;
//----------------------------------------------------------------------------------------
//执行IRET指令,从中断返回,这里建立了任务的堆栈结构,看起来好像刚刚发生了中断,所有的CPU
//寄存器都被推入堆栈中。IRET指令将任务的入口地址从堆栈中弹出,并把任务入口地址放到CS:IP
//寄存器中。地址后面跟着的值(叫做程序状态字)放在PSW寄存器中。
// 执行IRET指令时,堆栈指针(SS:SP)指向任务返回地址,这样看起来像是任务被一个普通
//函数调用了,SS:SP+4指向自变量pdada,pdata会给任务传递参数。换句话说,任务无须了解
//它是被OSStartHighRdy()调用的,还是被其它函数调用的。
//----------------------------------------------------------------------------------------
IRET ; 从任务私栈中弹出程序状态字--->PSW、任务起始地址的段地址--->CS、任务起始地址的偏移地址--->IP
; 然后跳转到CS:IP位置,开始运行当前最高优先级任务。
_OSStartHighRdyENDP
;*********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
;
; Note(s): 1) Upon entry,
; OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 2) The stack frame of the task to suspend looks as follows:
;
; SP -> OFFSETof task to suspend (Low memory)
; SEGMENT of task to suspend
; PSW of task to suspend (High memory)
;
; 3) The stack frame of the task to resume looks as follows:
;
; OSTCBHighRdy->OSTCBStkPtr --> DS (Low memory)
; ES
; DI
; SI
; BP
; SP
; BX
; DX
; CX
; AX
; OFFSETof task code address
; SEGMENT of task code address
; Flags to load in PSW (High memory)
;*********************************************************************************************************
//-----------------------------------------------------
//存储路径=C:\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_C.ASM
//-----------------------------------------------------
// OSCtxSW()是一个任务级的任务切换函数,在任务调用时,区别在于中断服务程序中调用的是OSIntCtxSW()。
//在80x86系统上,它是通过执行一条软中断指令实现任务切换,软中断向量指向OSCtxSW()。
// 在uC/OS-II中,如果任务调用了某函数,而该函数的执行结果可能造成系统任务重新调度,例如唤醒了一个
//优先级更高的任务,使当前运行着的任务已经不是最重要的任务了,则uC/OS-II会在函数的未尾调用OSSched()。
//如果OSSched()判断需要进行任务调度,则会找到该任务控制块OS_TCB的地址,并将该地址复制给OSTCBHighRdy,
//然后通过宏OS_TASK_SW()执行软中断,进行任务切换。
_OSCtxSw PROC FAR
//---------------------------------------------------------------------------------------------
//说明,由于任务调度函数OSSched()执行宏定义OS_TASK_SW()实际上是通过执行软中断INT80,因此首先把
//程序状态字PSW推入堆栈、然后把执行软中断指令的任务(调用OS_TASK_SW()的任务)的中断返回地址
//(段地址和偏移地址)。
//
// | |
// | 存储器地址低端 |
// --------------------
// | OFF task | <---- SP(说明:执行OS_TASK_SW()后,当前任务私栈的堆栈指针SP位置)
// --------------------
// | SEG task |
// --------------------
// | PSW | <---- SP'(说明:当前任务调用OSSched()进行任务切换,执行OS_TASK_SW()前,当前任务私栈的堆栈指针SP位置)
// --------------------
// | 存储器地址低端 |
// | |
//
// 图2 执行OS_TASK_SW()跳转到OSCtxSW()时,堆栈结构示意图
//---------------------------------------------------------------------------------------------
;
PUSHA ; 将挂起任务的其它寄存器保存到当前任务堆栈中
PUSH ES ;
PUSH DS ;
//
// | |
// | 存储器地址低端 |
// --------------------
// | DS | <---- 【SP】(说明:执行PUSHA/PUSH ES/PUSH DS后,当前任务私栈的堆栈指针SP位置)
// --------------------
// | ES |
// --------------------
// | DI |
// --------------------
// | SI |
// --------------------
// | BP |
// --------------------
// | SP |
// --------------------
// | BX |
// --------------------
// | DX |
// --------------------
// | CX |
// --------------------
// | AX |
// --------------------
// | OFF task | <---- SP(说明:调用宏定义OS_TASK_SW(),进入OSCtxSW()函数后,当前任务私栈的堆栈指针SP位置)
// --------------------
// | SEG task |
// --------------------
// | PSW | <---- SP'(说明:执行OS_TASK_SW()前,当前任务私栈的堆栈指针SP位置)
// --------------------
// | 存储器地址低端 |
// | |
//
// 图3 执行PUSA
// PUSH ES
// PUSH DS后,堆栈结构示意图
//---------------------------------------------------------------------------------------------
;
MOV AX, SEG _OSTCBCur ; Reload DS in case it was altered
MOV DS, AX ;
;
LES BX, DWORD PTR DS:_OSTCBCur ; OSTCBCur->OSTCBStkPtr = SS:SP
MOV ES:, SS ; 把指向新的堆栈指针位置保存在任务控制块OS_TCB的OSTCBStkPtr成员中
MOV ES:, SP ; 即图3中【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 ; OSTCBHighRdy复制给OSTCBCur,这样,高优先级任务就成为当前任务。
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 ; OSPrioHighRdy复制给OSPrioCur,高优先级任务的优先级号就成为当前任务的优先级号
;
LES BX, DWORD PTR DS:_OSTCBHighRdy; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SS, ES: ;
MOV SP, ES: ; 从高优先级任务的私栈中弹出高优先级任务的堆栈指针SS:SP
;
POP DS ; Load new task's context(装载新任务的上下文内容)
POP ES ; 从高优先级任务的私栈中弹出“DS”值到CPU的DS寄存器中
POPA ; 从高优先级任务的私栈中弹出“ES”值到CPU的ES寄存器中
; ; 从高优先级任务的私栈中弹出“AX”、“CX”,“DX”、“BX”值到CPU的AX、CX、DX、BX寄存器中
//---------------------------------------------------------------------------------------------------
//执行IRET(中断返回指令),则高优先级任务的私栈中弹出:
//(1)“PSW”程序状态字值到CPU的PSW寄存器中
//(2)高优先级任务断点地址的偏移地址到CPU的IP寄存器中
//(3)高优先级任务断点地址的段地址到CPU的CS寄存器中
//(4)处理器开始从上次断点位置,继续运行高优先级任务
//---------------------------------------------------------------------------------------------------
IRET ; Return to new task(跳转到高优先级任务的断点处开始运行)
;
_OSCtxSw ENDP
;*********************************************************************************************************
; 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.
;
; 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;
; OSIntNesting++;
; if (OSIntNesting == 1) {
; OSTCBCur->OSTCBStkPtr = SS:SP
; }
; OSTickDOSCtr--;
; if (OSTickDOSCtr == 0) {
; OSTickDOSCtr = 11;
; 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)
; }
; OSTimeTick(); Notify uC/OS-II that a tick has occured
; OSIntExit(); Notify uC/OS-II about end of ISR
; Restore all registers that were save on the current task's stack;
; Return from Interrupt;
;*********************************************************************************************************
;
//-----------------------------------------------------
//存储路径=C:\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_C.ASM
//-----------------------------------------------------
//
//说明:在PC中,时钟节拍由硬件定时器产生,硬件定时器会中断CPU,间隙是54.93ms(18.20648HZ)。uC/OS-II将时钟
//节拍频率设置为200HZ。PC机时钟节拍的中断向量为0x08,uC/OS-II将此向量截取,使之指向uC/OS-II的时钟节拍中断
//服务程序子程序OSTickISR(),而原先的0x08中断向量保存在中断向量129(0x81)中。
//为满足DOS的需要,原先的中断服务程序还必须每隔54.93ms调用1次。
//
//////////////////////////////////////////////////////////////////////////
// 在什么时候启动时钟节拍中断
//////////////////////////////////////////////////////////////////////////
//(1)、在OSStart()运行之后,uC/OS-II启动运行的第1个任务中初始化节拍中断。
//这个任务是调用OSStart()之前建立的任务中优先级最高的任务
//////////////////////////////////////////////////////////////////////////
// 错误的时候启动时钟节拍中断
//////////////////////////////////////////////////////////////////////////
//(1)、在OSInit()和OSStart()之间打开了时钟节拍中断。
_OSTickISRPROC FAR
;
//-------------------------------------------------------------------------------
//同所有uC/OS-II的中断服务程序一样,所有的寄存器必须保存在当前任务的堆栈中
//-------------------------------------------------------------------------------
PUSHA ; Save interrupted task's context
PUSH ES
PUSH DS
;
//-------------------------------------------------------------------------------
//OSIntNesting++; if (OSIntNesting == 1)
//当进入中断服务子程序ISR时,必须告诉uC/OS-II,进入中断服务子程序了。可通过调用
//OSIntEnter()实现,或者直接给中断嵌套层数OSIntNesting加1的方式实现。
//(1)、直接给中断嵌套层数OSIntNesting加1会更快一些。
//(2)、OSIntEnter()会检查OSIntNesting是否超过了255,这样,中断嵌套更安全。
//-------------------------------------------------------------------------------
MOV AX, SEG(_OSIntNesting) ; Reload DS
MOV DS, AX
INC BYTE PTR DS:_OSIntNesting ; Notify uC/OS-II of ISR
;
//-------------------------------------------------------------------------------
//如果中断嵌套层数OSIntNesting=1,则没有中断嵌套,必须把堆栈指针SS:SP保存到当前任务
//的任务控制块OS_TCB的OSTCBStkPtr成员中。
//-------------------------------------------------------------------------------
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:, SS ; CPU堆栈指针SS:SP保存到当前任务的
MOV ES:, SP ;任务控制块OS_TCB的OSTCBStkPtr成员中
//-------------------------------------------------------------------------------
//计数器OSTickDOSCtr减1,当OSTickDOSCtr为0时,调用DOS的时钟节拍处理函数(DOS系统
//原中断服务程序),同时初始化计数器OSTickDOSCtr为11。
//-------------------------------------------------------------------------------
_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 ; 调用DOS的时钟节拍处理函数
JMP SHORT _OSTickISR3
//-------------------------------------------------------------------------------
//发送中断结束命令(EOI)。MSDOS操作系统相关知识背景:
// 当某一个中断源的服务程序完成时,必须给8259A一个中断结束指令,使这个源在ISR中
//的相应位复位。在不同的工作情况下,8259A可以有几种不同的给出中断结束命令的方法:
//(1)、自动中断结束模式(AEOI)
// 这种方式只能用于不要求中断嵌套的情况
//(2)、非自动中断结束模式(EOI)
// 在这种工作方式下,当中断服务程序完成从中断服务程序返回前,必须输送中断结束
//(EOI)命令。若是在8259A_级连的情况下,则必须送两个EOI命令,一个送从8259A,另一个
//送给主8259A。
//-------------------------------------------------------------------------------
// PC-DOS 8259A中断控制器口地址
//(1)、20H口
// 中断命令寄存器在每次外部中断结束以后,要给该口发送中断结束信号,其指令为
// MOV AL,20H
// OUT 20H,AL
//(2)、21H口 ---> 中断屏蔽寄存器
// 8259A芯片可以接收8个外部可屏蔽中断。这些中断有两种状态,即允许和屏蔽状态。
//21H口的8位分别对应于8个外部中断源,某位为0,则允许接收相应的中断,否则不接收相
//应的中断。其对应关系如下图:
// ------------------------------------------------------------------
// 7 6 5 4 3 2 1 0
// ------------------------------------------------------------------
// | | | | | | | |
// | | | | | | | ---> 定时器中断
// | | | | | | --->键盘中断
// | | | | | --->保留
// | | | | --->异步通讯口2
// | | | --->异步通讯口1
// | | --->硬盘中断
// | --->软盘中断
// --->并行打印机
//
// 图1 中断屏蔽寄存器位定义
//-------------------------------------------------------------------------------
_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.
//-------------------------------------------------------------------------------
//OSTickISR()【uC/OS-II的时钟节拍】调用OSTimeTick()函数,这样uC/OS-II就给所有延迟
//任务的等待时间以及有限等待某事件发生的任务的等待时间的节拍参数减1。
//-------------------------------------------------------------------------------
_OSTickISR3:
CALL FAR PTR _OSTimeTick ; Process system tick
//-------------------------------------------------------------------------------
//当前面代码全部执行完毕,调用OSIntExit()函数。如果uC/OS-II的时钟节拍OSTickISR()
//或者其它嵌套的中断服务子程序使一个更高优先级的任务进入了就绪态,并且当前中断服
//务子程序已经脱离了中断嵌套(即OSIntNesting=0),那么OSIntExit()不再返回时钟节
//拍OSTickISR()。此时OSIntCtxSW()恢复新任务的所有寄存器,并执行一条IRET指令。
// 如果中断服务子程序没有脱离中断嵌套,或者中断服务子程序没有使更高优先级的任务
//进入就绪态,OSIntExit()就返回OSTickISR(),即返回uC/OS-II的时钟节拍中断服务子程
//序。
//-------------------------------------------------------------------------------
CALL FAR PTR _OSIntExit ; Notify uC/OS-II of end of ISR
//-------------------------------------------------------------------------------
// 如果OSIntExit()返回到这里,则是因为OSIntExit()没有发现更高优先级的任务。这样
//被中断任务的寄存器会被恢复。当执行IRET指令时,中断服务子程序返回,让被中断了的
//任务继续运行。
//-------------------------------------------------------------------------------
POP DS ; Restore interrupted task's context
POP ES
POPA
;
IRET ; Return to interrupted task
;
_OSTickISRENDP
;
;*********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From an ISR)
; void OSIntCtxSw(void)
;
; Note(s): 1) Upon entry,
; OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 2) The stack frame of the task to suspend looks as follows:
;
; OSTCBCur->OSTCBStkPtr ------>DS (Low memory)
; ES
; DI
; SI
; BP
; SP
; BX
; DX
; CX
; AX
; OFFSETof task code address
; SEGMENT of task code address
; Flags to load in PSW (High memory)
;
;
; 3) The stack frame of the task to resume looks as follows:
;
; OSTCBHighRdy->OSTCBStkPtr --> DS (Low memory)
; ES
; DI
; SI
; BP
; SP
; BX
; DX
; CX
; AX
; OFFSETof task code address
; SEGMENT of task code address
; Flags to load in PSW (High memory)
;*********************************************************************************************************
//-----------------------------------------------------
//存储路径=C:\SOFTWARE\uCOS-II\Ix86L\BC45\OS_CPU_C.ASM
//-----------------------------------------------------
//由于中断可能会使更高优先级的任务进入就绪态,为了让更高优先级的任务能立即运行,需要
//进行任务切换。在中断服务子程序的最后,OSIntExit()函数会调用 OSIntCtxSW()函数做任务
//切换,故OSIntCtxSW()又称为中断级的任务切换函数。由于调用OSIntCtxSW()之前已经发生了
//中断,中断服务子程序会执行PUSHA/PUSH ES/PUSH DS将被挂起任务的现场保存到该挂起任务
//的私栈中。因此OSIntCtxSW()不会再次保存被中断了的任务的现场到该任务的堆栈中。
// OSIntCtxSW()大部分代码和OSCtxSW()的代码相同,不同之处是,由于中断已经发生,此处
//无需再保存CPU寄存器(没有PUSHA,PUSH ES,PUSH DS指令)。
// 特别需要指出的是,当中断嵌套=1时,时钟节拍中断服务子程序已经将被中断了的任务的
//堆栈指针保存在该任务的任务控制块OS_TCB。
//-----------------------------------------------------------------------------------
_OSIntCtxSw PROC FAR
;
CALL FAR PTR _OSTaskSwHook ; Call user defined task switch hook
;
MOV AX, SEG _OSTCBCur ; Reload DS in case it was altered
MOV DS, AX ;
;
//---------------------------------------------------------------------------
//OSTCBHighRdy复制给OSTCBCur(高优先级任务的任务控制块复制给正运行任务的任务控制块指针变量中)
//---------------------------------------------------------------------------
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 ;
//---------------------------------------------------------------------------
//OSPrioHighRdy复制给OSPrioCur(高优先级任务的优先级号复制给正运行任务的优先级号变量中)
//---------------------------------------------------------------------------
MOV AL, BYTE PTR DS:_OSPrioHighRdy; OSPrioCur = OSPrioHighRdy
MOV BYTE PTR DS:_OSPrioCur, AL
;
//---------------------------------------------------------------------------
//得到高优先级任务的堆栈指针SS:SP
//---------------------------------------------------------------------------
LES BX, DWORD PTR DS:_OSTCBHighRdy; SS:SP = OSTCBHighRdy->OSTCBStkPtr
MOV SS, ES: ;
MOV SP, ES: ;
//---------------------------------------------------------------------------
//高优先级任务的现场恢复到CPU
//---------------------------------------------------------------------------
POP DS ; Load new task's context
POP ES ;
POPA ;
//---------------------------------------------------------------------------
//跳转到高优先级任务的断点地址,继续运行高优先级任务。
//---------------------------------------------------------------------------
IRET ; Return to new task
;
_OSIntCtxSw ENDP 连载五:
首先提个问题:
//1.为什么OSIntExit()函数中,中断嵌套层数-1时,要关闭中断呢?因为已经进入了中断服务程序了,
// 难道允许中断嵌套吗?(高优先级中断可以打断低优先级中断)
// 既然如此,为什么OSIntEnter()函数中,中断嵌套层数+1时,为什么不关闭中断呢?
/*
*********************************************************************************************************
* ENTER ISR
*
* Description: This function is used to notify uC/OS-II that you are about to service an interrupt
* service routine (ISR).This allows uC/OS-II to keep track of interrupt nesting and thus
* only perform rescheduling at the last nested ISR.
*
* Arguments: none
*
* Returns : none
*
* Notes : 1) This function should be called ith interrupts already disabled
* 2) Your ISR can directly increment OSIntNesting without calling this function because
* OSIntNesting has been declared 'global'.
* 3) You MUST still call OSIntExit() even though you increment OSIntNesting directly.
* 4) You MUST invoke OSIntEnter() and OSIntExit() in pair.In other words, for every call
* to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the
* end of the ISR.
* 5) You are allowed to nest interrupts up to 255 levels deep.
* 6) I removed the OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() around the increment because
* OSIntEnter() is always called with interrupts disabled.
*********************************************************************************************************
*/
//-----------------------------------------------------
C:\SOFTWARE\uCOS-II\SOURCE\OS_CORE.C
//-----------------------------------------------------
//
voidOSIntEnter (void)
{
//---------------------------------------------------------------------------
//如果uC/OS-II已经运行,当进入中断服务子程序后,中断嵌套层数+1
//---------------------------------------------------------------------------
if (OSRunning == TRUE) {
if (OSIntNesting < 255) {
OSIntNesting++; /* Increment ISR nesting level */
}
}
}
/*$PAGE*/
/*
*********************************************************************************************************
* EXIT ISR
*
* Description: This function is used to notify uC/OS-II that you have completed serviving an ISR.When
* the last nested ISR has completed, uC/OS-II will call the scheduler to determine whether
* a new, high-priority task, is ready to run.
*
* Arguments: none
*
* Returns : none
*
* Notes : 1) You MUST invoke OSIntEnter() and OSIntExit() in pair.In other words, for every call
* to OSIntEnter() at the beginning of the ISR you MUST have a call to OSIntExit() at the
* end of the ISR.
* 2) Rescheduling is prevented when the scheduler is locked (see OS_SchedLock())
*********************************************************************************************************
*/
//-----------------------------------------------------
C:\SOFTWARE\uCOS-II\SOURCE\OS_CORE.C
//调用者:中断服务程序
//-----------------------------------------------------
voidOSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
//---------------------------------------------------------------------------
//如果uC/OS-II已经运行
//---------------------------------------------------------------------------
if (OSRunning == TRUE) {
OS_ENTER_CRITICAL();
//---------------------------------------------------------------------------
//中断嵌套层数-1
//---------------------------------------------------------------------------
if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
//---------------------------------------------------------------------------
//如果中断嵌套层数=0,表示已经完全退出了所有的中断服务子程序,此时开始在就绪
//表中查找优先级最高的任务进行调度,如果找到,则将CPU的控制权交给优先级最高的
//任务,否则返回被(中断服务程序)中断了任务的断点处继续执行原任务。
//---------------------------------------------------------------------------
if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Reschedule only if all ISRs complete ... */
OSIntExitY = OSUnMapTbl; /* ... and not locked. */
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl]);
//---------------------------------------------------------------------------
//如果找到(因为找到的优先级号不等于被中断任务的任务号),则得到高优先级任务
//的任务控制块OSTCBHighRdy
//---------------------------------------------------------------------------
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy= OSTCBPrioTbl;
OSCtxSwCtr++; /* Keep track of the number of ctx switches */
//---------------------------------------------------------------------------
//调用中断级任务调度函数OSIntCtxSw()进行任务调度(注:中断级任务调度函数OSIntCtxSw()
//和任务级任务调度函数OSCtxSW()唯一的区别是,不需要将被中断任务的现场保存到被中断任务
//的私栈中,因为中断服务子程序会自动执行入栈功能。
// 至此,uC/OS-II自动跳转到高优先级任务继续执行,不会返回到中断服务程序断点处。
//因为如果返回到中断服务程序断点处,是因为没有在就绪表中找到高优先级任务,而
//返回到中断服务程序断点处,然后执行出栈指令(POPA/POP ES/POP DS),将被中断任务
//的现场送到CPU相关寄存器中,最后执行IRET指令,继续执行被中断任务。
//---------------------------------------------------------------------------
OSIntCtxSw(); /* Perform interrupt level ctx switch */
}
}
OS_EXIT_CRITICAL();
}
} 总结:
要想弄清楚作者书上的例子1,必须具备扎实的PC-DOS操作系统方面的知识。
(1)、中断向量表
(2)、CALL DEST指令 入栈情况
(3)、IRET指令 入栈情况
(4)、SEG指令
(5)、LES指令
(6)、如何获取中断向量
(7)、如何盗取中断向量
(8)、PC-DOS的定时中断的中断号=0x08,每隔多少毫秒执行一次
(9)、直接写屏技术(用DOS提供的函数往屏幕写字符太慢了)
(10)、PC-DOS的段寄存器有那些(DS,ES,CS,SS)
作用分别是什么?
其实初学者想学习uC/OS-II时,看到厚厚的一本书,头都大了,其实学习uC/OS-II主要核心的就是任务调度,其它如:信号量等可以不必先了解,当把以下几个函数吃透了(了解每一步的工作过程,堆栈状态,堆栈切换过程),就抓住了学习的捷径。
(1)、任务堆栈初始化
(2)、如何启动uC/OS-II
(3)、OS_TASK_SW()
(4)、OSCtxSW()
(5)、OSIntCtxSW()
(6)、OSTickISR()
(7)、OSIntExit()
(8)、OSStartHighRdy()
(9)、OSTimeTick()
如果能把上述9个函数之间每发生的过程都了解得清楚,初学者就基本了解了uC/OS-II的工作过程。 以下是uC/OS-II第1个实例的修改版本。原版本需要访问显存(作者为加快显示速度,没有调用DOS函数显示字符,而直接写屏方式显示字符),如果初学者不具备PC-DOS操作系统的知识,可能会看不懂。
/*
*********************************************************************************************************
* uC/OS-II
* The Real-Time Kernel
*
* (c) Copyright 1992-2002, Jean J. Labrosse, Weston, FL
* All Rights Reserved
*
* EXAMPLE #1
*********************************************************************************************************
*/
#include "includes.h"
/*
*********************************************************************************************************
* CONSTANTS
*********************************************************************************************************
*/
#defineTASK_STK_SIZE 512 /* Size of each task's stacks (# of WORDs) */
#define PrioTask0 0 // 任务0的优先级号
#define PrioTask1 1 // 任务1的优先级号
#define PrioTask2 2 // 任务2的优先级号
OS_STK Task0Stk; /* Tasks stacks */
OS_STK Task1Stk; /* Tasks stacks */
OS_STK Task2Stk; /* Tasks stacks */
voidTask0(void *pdata);
voidTask1(void *pdata);
voidTask2(void *pdata);
/*
*********************************************************************************************************
* STARTUP TASK
*********************************************************************************************************
*/
voidTask0(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SRcpu_sr;
#endif
INT16S key;
pdata = pdata; /* Prevent compiler warning */
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); /* Install uC/OS-II's clock tick ISR */
PC_SetTickRate(OS_TICKS_PER_SEC); /* Reprogram tick rate */
OS_EXIT_CRITICAL();
OSTaskCreate(Task1, (void *)0, &Task1Stk, PrioTask1);
OSTaskCreate(Task2, (void *)0, &Task2Stk, PrioTask2);
while (1)
{
if (PC_GetKey(&key) == TRUE)
{
if (key == 0x1B) // 按ESC键,返回到DOS
{
PC_DOSReturn(); /* Return to DOS */
}
}
printf("TASK 0\n");
OSTimeDlyHMSM(0, 0, 1, 0); // 1秒运行1次
}
}
voidTask1(void *pdata)
{
pdata = pdata; /* Prevent compiler warning */
while (1)
{
printf("TASK 1\n");
OSTimeDlyHMSM(0, 0, 2, 0); // 2秒运行1次
}
}
voidTask2 (void *pdata)
{
pdata = pdata; /* Prevent compiler warning */
while (1)
{
printf("TASK 2\n");
OSTimeDlyHMSM(0, 0, 5, 0); // 5秒运行1次
}
}
voidmain (void)
{
OSInit(); /* Initialize uC/OS-II */
PC_DOSSaveReturn(); /* Save environment to return to DOS */
PC_VectSet(uCOS, OSCtxSw); /* Install uC/OS-II's context switch vector */
OSTaskCreate(Task0, (void *)0, &Task0Stk, PrioTask0);
OSStart(); /* Start multitasking */
}
以下是运行结果:
http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_436807.JPG
(原文件名:多任务-未命名.JPG) 呵呵,我也在弄,楼主桌面背景,我很喜欢,能不能传上来! 哈哈
这么牛的帖子
小弟我赶快来占座啦
顶你!! 支持一下 什么环境运行呢 mark mark mark mark 楼主对uc/os学习的要点很了解
楼主有过移值经验吗
可以写个贴谈谈 呵呵,不错 正想学,值得借鉴 不错!
SIMATIC 楼主也玩西门子?呵呵 SIMATIC:不菜一蝶,不必花费多长时间就能够入门了。
最验的是自动控制系统入门,像505/505E就是一套比较复杂的自动控制器,广泛应用于工业控制。 支持!! 支持啊! mark! 不错的经验 yao 顶啊! 支持啊! 谢谢楼主的分享。
我刚学习ucos,看不懂。
借楼主的人气在这有个问题想请教各位前辈。
在邵贝贝的书中讲中断时有这么一句话:
“μC/OS-Ⅱ可以用在这类微处理器中,当任务切换时,寄存器是保存在被中断了的那个任务的栈中的”
这是什么意思呢?
比如我用的芯片是arm7的,进入中断之后,它会自动使用中断模式下的栈空间,而不是被中断的任务的栈,那该怎么解决这个问题呢?
恳请赐教!
感激不尽…… 顺手回答楼上的问题
ARM7进入中断后,先使用中断模式栈空间,然后会切换到系统模式,保存相应的寄存器 ccccccccccccccc mark 顶 mark ddddddddddddddd 顶顶 mark 马克 强。。。。有空学习 LZ用的是哪个编译环境啊我们这些还未入门的不知道怎么入门啊 mark mark mark mark下 mark mark mark,坐等pdf版 OK DDDDDDDDDDDDDDDDDDDDDDD mark 顶 mark!!! mark mark mark too mark 学习 mark mark mark 好东西 mark 一直没看懂UCOS 学习了 mark,还没学习这么深 学习一下
页:
[1]