📄 os_cpu_a.asm
字号:
;********************************************************************************************************
; uC/OS-II
; The Real-Time Kernel
;
; (c) Copyright 1992-2002, Jean J. Labrosse, Weston, FL
; All Rights Reserved
;
;
; 80x86/80x88 Specific code
; LARGE MEMORY MODEL
;
; Borland C/C++ V4.51
; (IBM/PC Compatible Target)
;
; File : OS_CPU_A.ASM
; By : Jean J. Labrosse
;********************************************************************************************************
;********************************************************************************************************
; PUBLIC and EXTERNAL REFERENCES
;********************************************************************************************************
PUBLIC _OSTickISR ;汇编的子程序共享的方法
;把一个程序中多次重复出现的程序段编写成子程序是为了个别程序的简化,而把具有固定功能的程序段写成子程序则是为了
;在多个程序间共享。因此有必要对那些普遍适用的子程序进行适当的管理,当需要使用它们时,可以很方便的取出。子程序
;共享的方式大致有以下3种。
;1:复制子程序的源代码,但要注意去了子程序名之外可能还会用到一些标识符,如子程序内需要使用的变量,标号等。当
;子程序清单复制到某个源程序之后,子程序内的标识符有可能与程序的其余部分的标识符同名,这种标识符的重复定义是不
;符合汇编语言语法规定的,需要修改。
;2:INCLUDE 伪指令 每个子程序以独立的文本文件的形式存在,有利有弊,其中使程序员感到不便的是,每当需要使用某个
;子程序时,总是要手工地把这个子程序的程序清单调到使用它的源程序中。如果能够把源程序和所用到的子程序分别放在不同
;的问家中,只在源程序中很简洁的申明在何处需要引用哪个子程序文件,那么使用起来就方便多了。使用INCLUDE伪指令可以
;解决这个问题。
;3,库文件.LIB 子程序共享还有一种方法是建立子程序的库文件.LIB 在微软提供的汇编语言工作环境中,有一个专门管理子程序
;库文件的程序LIB.EXE,它的主要功能包括建立新的库文件,向库文件中放入新的子程序和从中取出子程序,以及更新其中的子程序
;市容LIB软件之前,必须先把子程序写入一个段中,并且用PUBLIC伪指令说明该子程序可供外部调用,然后把子程序汇编成目标程序。
;PUBLIC 指令功能:由PUBLIC定义的符号名为全局符号名,由本模块程序定义,允许程序中其他模块直接调用,没有被说明的符号名
;不能被其他模块调用。
;EXTRN 指令功能:通知汇编程序本模块中的符号名是外部符号名,已在其他模块中被定义,即被其他模块中的PUBLIC说明,在本模块
;中可被调用,没有定义的外部符号名不能被引用。EXTRN定义的符号名为变量名,类型可以为BYTE,WORD,DWORD,或QWORD。若定义的
;符号名为标号,则类型为NEAR或FAR。若定义的符号名为常量,则类型为ABS。注意:PUBLIC模块定义伪指令和SEGMENT伪指令中的
;PUBLIC组合类型是两个不同的概念。个模块内的PUBLIC语句和EXTRN语句应相关联,在EXTRN中出现的符号名必须在PUBIL中被定义,且
;类型要相一致,否则链接会出错。
PUBLIC _OSStartHighRdy
PUBLIC _OSCtxSw
PUBLIC _OSIntCtxSw
EXTRN _OSIntExit:FAR
EXTRN _OSTimeTick:FAR
EXTRN _OSTaskSwHook:FAR
EXTRN _OSIntNesting:BYTE
EXTRN _OSTickDOSCtr:BYTE
EXTRN _OSPrioHighRdy:BYTE
EXTRN _OSPrioCur:BYTE
EXTRN _OSRunning:BYTE
EXTRN _OSTCBCur:DWORD
EXTRN _OSTCBHighRdy:DWORD
.MODEL LARGE;指令功能:定义数据段和代码段存储模式,常用存储模式选择符有 TINY,SMALL,MEDIUM,COMPACT,LARGE,HUGE,FLAT
;LARGE 大型模式:代码段和数据段分别放在多个64K段中,但全部静态数据长度不能超过64KB。代码段和数据段默认为远访问。
.CODE ;指令功能:定义一个代码段,对多个代码段各段应有自己的段名。在LARGE模式下,默认代码段名为 模块名_TEXT
.186 ;这是一条说明处理器类型的伪指令 .186,只支持对80186指令的汇编
PAGE ;规定段从页边界开始,即该段起始地址的最低字节位必须为‘00000000B’,属性为256
; /*$PAGE*/
;*********************************************************************************************************
; START MULTITASKING
; void OSStartHighRdy(void)
该函数由OSStart()函数调用,功能是让进入就绪态的优先级最高的任务运行。在调用OSStart()之前,必须先调用OSInit()函数,且已经
建立了只好一个任务(见OSTaskCreate()和OSTaskCreateExt()函数)。OSStartHighRdy()默认指针OSTCBHighRdy指向优先级最高的就绪态任
务的任务控制块(OS_TCB)
;
; The stack frame is assumed to look as follows:
;
; OSTCBHighRdy->OSTCBStkPtr --> DS (Low memory)
; ES
; DI
; SI
; BP
; SP
; BX
; DX
; CX
; AX
; OFFSET of task code address
; SEGMENT of task code address
; Flags to load in PSW
; OFFSET of task code address
; SEGMENT of task code address
; OFFSET of '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.
;*********************************************************************************************************
_OSStartHighRdy PROC FAR ;PROC定义了一个子程序子程序名是一个标识符_OSStartHighRdy,它即是编程者给子程序起的名字,也是子
;程序第一条指令所在的逻辑地址,成为子程序的入口地址。PROC后面的类型 只有FAR和NEAR两种,它将影响汇编程序对子程序调用指令
;CALL和返回指令RET的翻译方式。通常指令序列的最后一条指令是返回指令RET。
MOV AX, SEG _OSTCBHighRdy ; Reload DS
MOV DS, AX ;
;
CALL FAR PTR _OSTaskSwHook ; Call user defined task switch hook在第13章提到过的,在OSStartHighRdy()
; 启动之前,必须先调用函数OSTaskSwHook(),而OSTaskSwHook()函数必须查询OSRunning的状态(此时OSRunning的状态是FALSE
; )。OSTaskSwHook()只做恢复CPU寄存器的操作,并不需要作先保存,再恢复的操作。注意操作符PTR 这里指定了标号跳转远跳
;转。如果是 JMP DWORD PTR[SI] 则是CS<-((SI+2)),IP<-((SI))
;
MOV AL, 1 ; OSRunning = TRUE;OSStartHighRdy()函数把OSRunning设成TRUE,这样以后在
;调用OSTaskSwHook()时,OSTaskSwHook()函数便可以进行先保存,再恢复环境的操作了。由于代码是用汇编语言写的,不能从C编译器中
;得到TURE的准确值。这里假设TRUE是1.
MOV BYTE PTR DS:_OSRunning, AL ; (Indicates that multitasking has started)
;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr;OSStartHighRdy()从任务控制块OS_TCB
;中获得并恢复堆栈指针。如同前面提到的,堆栈指针的值保存在任务控制块TCB的开始处(也就是任务控制块OS_TCB),这样较容易用汇编
;语言访问该指针。这里DWORD PTR 强制转换了类型
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX+0] ;
;
POP DS ; Load task's context;OSStartHighRdy()把CPU所有整数寄存器的值保存在任务
;堆栈中
POP ES ;
POPA ;等价于
; popf
; pop sp
; pop bp
; pop di
; pop si
; pop dx
; pop cx
; pop bx
; pop ax
; 来代替
;
IRET ; Run task;执行IRET指令,从中断返回。这里建立了任务的堆栈结构,看起来好
;像刚发生了中断,所有的CPU寄存器都被推入堆栈中。IRET指令将任务的入口地址从堆栈中弹出,并把任务入口地址放在CS:IP寄存器中,
;地址后面跟着的值(叫做状态字或者状态位)放在SW寄存器中。 IRET的作用是 出栈三个字,依次是IP,CS,标志寄存器。注意使用IRET比
;使用RET多了 一个返回标志寄存器的功能。
;执行IRET指令,堆栈指针SS:SP指向任务返回地址,这样看起来好像是任务被一个普通函数调用了。SS:SP+4指向自变量pdata,pdata会给
;任务传递参数。换句话说,任务无须了解它是被OSStartHighRdy函数调用的,还是被其他函数调用的。
_OSStartHighRdy ENDP
PAGE ; /*$PAGE*/
;*********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
OSCtxSw()是一个任务级的任务切换函数,在任务中调用,区别在于中断程序中调用的OSIntCtxSw()。80X86系统上,它通过执行一条软中断
指令实现任务切换,软中断向量指向OSCtxSw()。在UCOS中,如果任务调用了某函数,而该函数的执行结果可能造成系统任务重新调度,例
如唤醒了一个优先级更高的任务,使当前运行着的任务已经不是最重要的任务了,则UCOS会在函数的末尾调用OSSched(),如果OSSched()
判断需要进行任务调度,则会找到该任务控制块OS_TCB的地址,并将该地址复制给OSTCBHighRdy,然后通过宏OS_TASK_SW()执行软中断,
进行任务切换。
注意:在此过程中变量OSTCBCur始终包含指向当前运行任务的控制块OS_TCB的指针。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -