任务转换
80386不仅支持多种工作模式,而且还支持多任务。所谓多任务,是指系统在同一时间内能够运行多个任务。但我们知道,CPU在每一个确定的时刻只能执行一个指令流,要实现多任务,只能不断地在多个任务之间做切换。现在来看看如何进行任务切换。
    要进行任务切换,就要用到任务状态段TSS。任务状态段保存着各个任务的各个寄存器的值(通用寄存器、堆栈指针、标志寄存器等)、操作系统的有关信息、任务所访问的I/O口等。
    任务切换有两种方法,可以直接通过任务状态段TSS切换,也可以通过任务门进行切换。直接通过TSS切换时,用CALL指令指向一个任状态段,执行CALL指令,则CPU会保存当前的任务状态,并取出目标任务状态段的状态装入CPU,执行新的任务状态的任务,这样就完成了一次任务转换。
    通过任务门进行切换时,将任务门的选择子指向某个任务的TSS描述符,用CALL指令指向任务门,执行CALL指令,则CPU就会进行任务切换,并且交换任务状态。
    一般来说,分时操作系统会在定时器中断时判断是否要进行任务切换,操作系统查看当前进程或线程的时间片是否已用完,若是,则进行任务切换。
    例5.5  编写程序,用任务状态段进行任务切换。
    
    .386p
    JUMP16 MACRO selector,offsetv				;无条件转移宏指令
    DB 0EAH
    DW offsetv
    DW selector
    ENDM
    ;------------------------------------------
    CALL16 MACRO selector ,offsetv				;过程调用宏指令
    DB 09AH
    DW offsetv
    DW selector
    ENDM
    ;------------------------------------------
    Descriptor STRUC							;描述符结构体
    limitl     	dw 0
    basel	   	dw 0
    basem 	   	db 0
    attributes 	dw 0
    baseh      	db 0
    Descriptor ENDS       
    ;===============================
    Data Segment use16							;数据段
    gdt0 Descriptor <>
    
    DataSel = $-gdt0
    DataDes Descriptor <0ffffh,,,92H,>
    
    CodeSel = $-gdt0
    CodeDes Descriptor <0ffffh,,,98H,>
    
    Tss1Sel = $-gdt0                       	;Tss1 Descriptor
    Tss1Des Descriptor <Tss1Len-1,,,89H,>
    
    Tss2Sel = $-gdt0                       	;Tss2 Descriptor
    Tss2Des Descriptor <Tss2Len-1,,,89H,>
    
    Stack1Sel = $-gdt0						;任务1堆栈段选择子
    Stack1Des Descriptor <0,,,97H,>			;任务1堆栈段描述符
    
    Stack2Sel = $-gdt0 3
    Stack2Des Descriptor <0,,,0F7H,>
    
    Tss2CodeSel = $-gdt0 3					;任务2代码段选择子
    Tss2CodeDes Descriptor <0ffffh,,,0F8H,>		;任务2代码段描述符
    
    GdtLen  = $-gdt0
    
    GdtPtr  dw GdtLen-1 
    dd 0
    ;                      
    Tss1 label byte								;任务1的任务状态段
    dd 0
    dd 0       								;Stack Pointer for Ring 0
    dw 0,0
    dd 0       								;Stack Pointer for Ring 1
    dw 0,0
    dd 0       								;Stack Pointer for Ring 2
    dw 0,0
    dd 0       								;cr3
    dd 0       								;eip  
    dd 0       								;eflags
    dd ?       								;eax
    dd ?       								;ecx
    dd ?       								;edx
    dd ?       								;ebx
    dd ?       								;esp
    dd ?       								;ebp
    dd ?       								;esi
    dd ?       								;edi
    dw 0,0     								;es
    dw 0,0     								;cs
    dw 0,0     								;ss
    dw 0,0     								;ds
    dw 0,0     								;fs
    dw 0,0     								;gs
    dw 0,0     								;ldt
    dw 0         
    dw $ 2
    db 0ffh
    Tss1Len = $-Tss1
    
    Tss2 label byte								;任务2的任务状态段
    dd 0       								;Link
    dd 0       								;Stack Pointer for Ring 0
    dw 0,0
    dd 0       								;Stack Pointer for Ring 1
    dw 0,0
    dd 0       								;Stack Pointer for Ring 2
    dw 0,0
    dd 0       								;cr3
    dw offset Tss2Begin,0 						;eip  
    dd 0       								;eflags
    dd ?       								;eax
    dd ?       								;ecx
    dd ?       								;edx
    dd ?       								;ebx
    dd 1024    								;esp
    dd ?       								;ebp
    dd ?       								;esi
    dd ?       								;edi
    dw 0,0     								;es
    dw Tss2CodeSel,0      					;cs
    dw Stack2Sel,0        					;ss
    dw 0,0     								;ds
    dw 0,0     								;fs
    dw 0,0     								;gs
    dw 0,0     								;ldt
    dw 0         
    dw $ 2
    db 0ffh
    Tss2Len = $-Tss2
    ;                              
    OldSPSS dw 0,0
    Data ends
    ;==============================
    Stack1 Segment use16 							;任务1的堆栈段
    db 1024 dup (0)
    Stack1 Ends
    Stack2 Segment use16 							;任务2的堆栈段
    db 1024 dup (0)
    Stack2 Ends
    ;==============================
    Tss2Code Segment use16						;任务2的代码段
    assume cs:Tss2Code
    Tss2Begin:
    iretd
    Tss2Code Ends
    ;=============================
    Code Segment use16							;代码段
    assume cs:Code,ds:Data
    Start:
    mov ax,Data
    mov ds,ax
    
    xor eax,eax								;初始化数据段描述符
    mov ax,Data
    shl eax,4
    mov dword ptr [GdtPtr 2],eax				;GDT地址
    xor eax,eax								;初始化代码段描述符
    mov ax,Code
    shl eax,4
    mov CodeDes.basel,ax
    shr eax,16
    mov CodeDes.basem,al
    mov CodeDes.baseh,ah
    mov ax,Data		      				;Tss1,初始化任务1的状态段描述符
    movzx eax,ax
    shl eax,4
    xor ebx,ebx
    mov bx,offset Tss1
    add eax,ebx
    mov Tss1Des.basel,ax
    shr eax,16
    mov Tss1Des.basem,al
    mov Tss1Des.baseh,ah
    mov ax,Data           				;Tss2,初始化任务2的状态段描述符
    movzx eax,ax
    shl eax,4
    xor ebx,ebx
    mov bx,offset Tss2
    add eax,ebx
    mov Tss2Des.basel,ax
    shr eax,16
    mov Tss2Des.basem,al
    mov Tss2Des.baseh,ah
    mov ax,Stack1						;Stack
    movzx eax,ax          
    shl eax,4
    mov Stack1Des.basel,ax
    shr eax,16
    mov Stack1Des.basem,al
    mov Stack1Des.baseh,ah
    mov ax,Stack2
    movzx eax,ax
    shl eax,4
    mov Stack2Des.basel,ax
    shr eax,16
    mov Stack2Des.basem,al
    mov Stack2Des.baseh,ah
    mov ax,Tss2Code       					;Tss2Code
    movzx eax,ax
    shl eax,4
    mov Tss2CodeDes.basel,ax
    shr eax,16
    mov Tss2CodeDes.basem,al
    mov Tss2CodeDes.baseh,ah
    
    lgdt fword ptr GdtPtr   					;Load GDT
    cli
    mov OldSPSS,sp
    mov [OldSPSS 2],ss
    mov eax,cr0								;转到保护模式
    or  eax,1
    mov cr0,eax
    JUMP16 CodeSEL,<offset Protect>
    Protect:
    mov ax,DataSel
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ax,Stack1Sel
    mov ss,ax
    mov sp,1024
    mov ax,Tss1Sel
    ltr ax									;装入任务状态寄存器
    CALL16 Tss2Sel,0        					;任务切换
    
    mov eax,cr0
    and eax,0fffffffeH
    mov cr0,eax
    JUMP16 Code,<offset Real>        
    Real:
    mov ax,data
    mov ds,ax
    lss sp,dword ptr OldSPSS
    sti
    mov ax,4C00H
    int 21H
    Code ends
    end Start







