📄 os_cpu_c.c
字号:
push cx
push dx
push si
push di
push bp
push sp
pushf
popa可以用
popf
pop sp
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
来代替
如果使用没有PUSHA指令的8086处理器,就要使用多个PUSH指令压入上述寄存器,且顺序要与PUSHA相同。在程序清单中,每个寄存器被初
始化为不同的值,仅仅是为了调试方便。*/
*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 */
*stk = _DS; /* DS = Current value of DS;Borland编译器支持虚拟寄存器变量操作,可以用_DS关键
字取得CPU中DS寄存器的值,程序清单中用_DS直接把DS寄存器复制到堆栈中。*/
return ((OS_STK *)stk); /*堆栈初始化工作结束后,OSTaskStkInit()函数返回新的堆栈栈顶指针,OSTaskCreate()
或OSTaskCreateExt()将指针保存在任务的控制块OS_TCB中。*/
}
/*$PAGE*/
/*
*********************************************************************************************************
* INITIALIZE A TASK'S STACK FOR FLOATING POINT EMULATION
当使用浮点仿真库时 见Borland文档,用Borland编译器编译生成的代码堆栈被安排成如图见 (邵贝贝UCOS P344)编译器假设程序运行在
单线程或者说单任务环境下。Borland C浮点仿真库FPE占用从SS:0x0000开始的大约300B,保存浮点仿真变量。这只适用于大模式。为了到达
到此目的,在调用OSTaskCreate()或者OSTaskCreateExt()建立任何一个用到浮点运算库的任务前,必须调用专门的函数
OSTaskStkInit_FPE_x86(),对其堆栈结构进行正确的初始化。该函数适用于Borland V3.x和V4.5x编译器,所以在使用别的编译器时,最好
不要把OSTaskStkInit_FPE_x86()函数包含进去。浮点仿真库根据当前的SS寄存器值,将它的数据保存到上述空间中。这里认为从SS:0x0000
开始往上的一些空间是保留给浮点操作用的。
*
* Description: This function MUST be called BEFORE calling either OSTaskCreate() or OSTaskCreateExt() in
* order to initialize the task's stack to allow the task to use the Borland floating-point
* emulation. The returned pointer MUST be used in the task creation call.
*
* Ex.: OS_STK TaskStk[1000];
*
*
* void main (void)
* {
* OS_STK *ptos;
* OS_STK *pbos;
* INT32U size;
*
*
* OSInit();
* .
* .
* ptos = &TaskStk[999];
* pbos = &TaskStk[0];
* psize = 1000;
* OSTaskStkInit_FPE_x86(&ptos, &pbos, &size);
* OSTaskCreate(Task, (void *)0, ptos, 10);
* .
* .
* OSStart();
* }
*
* Arguments : pptos is the pointer to the task's top-of-stack pointer which would be passed to
* OSTaskCreate() or OSTaskCreateExt().一个指向任务栈顶指针TOP的指针(指向指针的指针)。当建立任务
时,任务的栈顶指针TOP会被传递给OSTaskCreate()或者OSTaskCreateExt()函数。从数据空间分配到的栈的位置由DS寄存器和一个偏移量确
定。由于OSTaskStkInit_FPE_x86()函数规格化TOP,所以指向初始TOS的指针被传递给这个函数,使得TOS值可以修改。
*
* ppbos is the pointer to the new bottom of stack pointer which would be passed to
* OSTaskCreateExt().一个指向任务栈底指针BOS的指针(指向指针的指针)。任务的栈底指针(BOS)不传递给
OSTaskCreate()函数,但会传递给OSTaskCreateExt()函数。换句话说,ppbos对OSTaskCreateExt()函数是必需的。栈底常常不是在DS:0000
处,而是在DS寄存器加一个偏移量处。因为OSTaskStkInit_FPE_x86()函数规格化BOS,所以指向初试BOS的指针被传递给这个函数,使得BOS
值可以修改。
*
* psize is a pointer to the size of the stack (in number of stack elements). You
* MUST allocate sufficient stack space to leave at least 384 bytes for the
* floating-point emulation.一个指向变量的指针,这个变量是堆栈的容量。堆栈的容量参数对于函数
OSTaskCreate()不是必需的,但是对于OSTaskCreateExt()函数却是必需的。由于OSTaskStkInit_FPE_x86()函数为浮点方针变量保留了一部
分空间,所以实际的堆栈容量会被这个函数修改,这就是要传递这个变量指针的原因。必须保证传递给OSTaskStkInit_FPE_x86()函数的堆
栈足够大,能够容纳浮点仿真变量和任务预期的空间。
*
* Returns : The new size of the stack once memory is allocated to the floating-point emulation.
*
* Note(s) : 1) _SS is a Borland 'pseudo-register' and returns the contents of the Stack Segment (SS)
* 2) The pointer to the top-of-stack (pptos) will be modified so that it points to the new
* top-of-stack.
* 3) The pointer to the bottom-of-stack (ppbos) will be modified so that it points to the new
* bottom-of-stack.
* 4) The new size of the stack is adjusted to reflect the fact that memory was reserved on
* the stack for the floating-point emulation.
*********************************************************************************************************
*/
/*$PAGE*/
void OSTaskStkInit_FPE_x86 (OS_STK **pptos, OS_STK **ppbos, INT32U *psize)
{
INT32U lin_tos; /* 'Linear' version of top-of-stack address */
INT32U lin_bos; /* 'Linear' version of bottom-of-stack address */
INT16U seg;
INT16U off;
INT32U bytes;
seg = FP_SEG(*pptos); /* Decompose top-of-stack pointer into seg:off;
OSTaskStkInit_FPE_x86()函数从TOS分解出段地址和偏移量开始。*/
off = FP_OFF(*pptos);
lin_tos = ((INT32U)seg << 4) + (INT32U)off; /* Convert seg:off to linear address将栈顶地址转换为线性地址。
注意:80x86实模式下,段地址乘以16,再加上偏移地址,形成实际内存地址。*/
bytes = *psize * sizeof(OS_STK); /* Determine how many bytes for the stack确定栈的容量,以字节为单位。
注意:在UCOS中,必须将堆栈声明为OS_STK数据类型,它可以是8位,16位或者32位宽的堆栈。对于Borland编译器,堆栈宽度是16位,
但最好使用C语言中的sizeof()操作。*/
lin_bos = (lin_tos - bytes + 15) & 0xFFFFFFF0L; /* Ensure paragraph alignment for BOS通过从栈顶地址减去分配给堆栈的
字节数,就得到栈底的线性地址。注意:将栈底地址增加了15B,并且和0xFFFFFFF0L作‘与’运算,用以将BOS与段的边界,或者说按16
B边界对齐。就好像是在盖楼砌砖一样 为了让砖的高度相同 而砖的大小不一 就要用水泥填进去 然后抹平 15B就是水泥 然后
& 0xFFFFFFF0L就抹平了 而没有伤到砖。
所位数据对齐,是指数据所在的内存地址必须是该 数据长度的整数倍,DWORD数据的内存起始地址能被4除尽,WORD数据的内存起始地
址能被2除尽,x86 CPU能直接访问对齐的数据,当他试图访问 一个未对齐的数据时,会在内部进行一系列的调整,这些调整对于程序
来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐。
*/
seg = (INT16U)(lin_bos >> 4); /* Get new 'normalized' segment从BOS的线性地址,确定栈底的新的段地址
注意:上一步做了对齐 所以第四位都为0*/
*ppbos = (OS_STK *)MK_FP(seg, 0x0000); /* Create 'normalized' BOS pointer建立一个带偏移量0x0000的远程指针,
并赋值给BOS指针。*/
memcpy(*ppbos, MK_FP(_SS, 0), 384); /* Copy FP emulation memory to task's stack为了初始化新任务堆栈的浮点
仿真变量区,只需简单的将调用本函数任务的堆栈底部复制给新任务的堆栈。注意:调用本函数的任务栈必须是已经初始化了浮点仿真
变量区的栈,不这样做会引起不可预期的后果。Borland浮点方针FPT库任务认为,从SS:0x0000开始的大约300B是保留给浮点仿真变量
的。这只适用于大模式下的编译。笔者决定复制384B(0x0180)。实际上用户不必复制这么多字节,但笔者认为有一些余量会安全些。这
样,用户任务的堆栈至少须有384B加上用户任务原先的堆栈需求(当然包括中断嵌套的需求)._SS是Borland的虚拟寄存器,使代码可以
得到CPU堆栈段寄存器的当前值。而且,笔者决定用ANSI的标准函数memcpy()完成复制,因为Borland优化了该函数。
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#include <string.h>
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
*/
bytes = bytes - 16; /* Loose 16 bytes because of alignment确定规格化的栈顶地址。因为要将
堆栈对齐到段的边界上,需要先减去16B。如果能保证堆栈段地址的边界总是对齐的,就不必这么做。这样作和&0xFFFFFFF0L的做法基本
相同都使得实际地址末四位为零。*/
*pptos = (OS_STK *)MK_FP(seg, (INT16U)bytes); /* Determine new top-of-stack利用新的段地址和新的堆栈容量(对齐到段的
边界),通过一个远程指针确定新的栈顶地址TOS。*/
*ppbos = (OS_STK *)MK_FP(seg, 384); /* Determine new bottom-of-stack将BOS上移384B,使之适用于堆栈检查(如
果程序调用OSTaskStkChk())。*/
bytes = bytes - 384;
*psize = bytes / sizeof(OS_STK); /* Determine new stack size如果使用堆栈检查,则UCOS需要知道新堆栈的
容量。当然不希望从原先堆栈的底部,而是从新堆栈底部开始检查。*/
} /*如果任务要作浮点数学运算,那么函数OSTaskStkInit_FPE_x86()的调用必须
在调用OSTaskCreate()或OSTaskCreateExt()函数之前,用以初始化任务堆栈,如同前面描述过的那样。返回的指针(ptos和pbos)在建立任务
的函数中要用到。注意:pbos被传递给OSTaskCreateExt(),作为新堆栈的栈底。若调用OSTaskStkChk()(任务必须是由OSTaskCreateExt()
函数建立的)在运行时检查堆栈的使用情况,那么OSTaskStkChk()报告的值比堆栈的实际容量少了384B。必须当心,应用代码不会作任何浮点
异常处理(例如被0除)。这是因为浮点库在这样的环境中工作不完全正常,可以通过增加数据范围检查,以避免运行中出现异常。*/
/*$PAGE*/
/*
*********************************************************************************************************
* TASK SWITCH HOOK
*
* Description: This function is called when a task switch is performed. This allows you to perform other
* operations during a context switch.作任务切换时,会调用OSTaskSwHook()函数。不管任务切换是通过OSCtxSw()函数
实现的,还是通过OSIntCtxSw()函数(见OS_CPU_A.ASM)实现的,都会调用该函数。OSTaskSwHook()可以直接访问OSTCBCur和OSTCBHighRdy这
2个全局变量。OSTCBCur指向被切换出去的任务的任务控制块,而OSTCBHighRdy指向新任务的任务控制块。注意:在调用OSTaskSwHook()期
间,中断一直是关掉的。因此附加代码会影响中断的响应时间,所以尽量使这部分代码减至最少。
*
* Arguments : none
*
* Note(s) : 1) Interrupts are disabled during this call.
* 2) It is assumed that the global pointer 'OSTCBHighRdy' points to the TCB of the task that
* will be 'switched in' (i.e. the highest priority task) and, 'OSTCBCur' points to the
* task being switched out (i.e. the preempted task).
*********************************************************************************************************
*/
#if OS_CPU_HOOKS_EN > 0
void OSTaskSwHook (void)
{
}
#endif
/*
*********************************************************************************************************
* OSTCBInit() HOOK
*
* Description: This function is called by OS_TCBInit() after setting up most of the TCB.OS_TCBInit()函数在调用
OSTaskCreateHook()之前,会先调用OSTCBInitHook()函数中做一些与初始化控制块OS_TCB有关的处理;在OSTaskCreateHook()中做一些与
初始化任务有关的处理。是否用OSTaskCreateHook()和OSTCBInitHook()函数,完全取决于用户。同OSTaskCreateHook()一样,
OSTCBInitHook()会收到指向新添加任务的任务控制块的指针,而这个新添加任务的任务控制块绝大部分已经初始化完成,但是还没有链接
到已经建立任务的链表中。详见OS_TCBInit()。
*
* Arguments : ptcb is a pointer to the TCB of the task being created.
*
* Note(s) : 1) Interrupts may or may not be ENABLED during this call.
*********************************************************************************************************
*/
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSTCBInitHook (OS_TCB *ptcb)
{
ptcb = ptcb; /* Prevent Compiler warning */
}
#endif
/*
*********************************************************************************************************
* TICK HOOK
*
* Description: This function is called every tick.OSTimeTickHook()函数在每个时钟节拍都会被OSTimeTick()调用。实际上,
OSTimeTickHook()是在UCOS真正处理时钟节拍之前被调用的,以便于用户能先处理应急的事务。
*
* Arguments : none
*
* Note(s) : 1) Interrupts may or may not be ENABLED during this call.
*********************************************************************************************************
*/
#if OS_CPU_HOOKS_EN > 0
void OSTimeTickHook (void)
{
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -