zoukankan      html  css  js  c++  java
  • ASM:《X86汇编语言-从实模式到保护模式》第15章:任务切换

    15章其实应该是和14章相辅相成的(感觉应该是作者觉得14章内容太多了然后切出来了一点)。任务切换和14章的某些概念是分不开的。

    ★PART1:任务门与任务切换的方法

    1. 任务管理程序

      14章的时候我们说过,一个程序他可以有很多个任务,特权级指的是任务的不同部分的特权级,一个任务可以有两个空间,一个全局空间,一个局部空间。在一个任务内,全局空间和局部空间具有不同的特权级别,使用门,可以在任务内将控制从3特权级的局部空间转移到0特权级的全局空间,以使用内核或者操作系统提供的服务。

      任务切换时以任务为单位的,是指离开一个任务,转到另一个任务中去执行。任务转移相对复杂得多,执行任务时,系统必须至少有两个任务,而且已经有一个正在执行,为了实现任务在全局空间和局部空间可以很快速地切换,系统使用任务管理器机制来管理系统(任务管理器随内核加载而加载,处于0特权级)。任务管理程序以一个任务而独立存在,以完成一些管理和控制功能(相当于一个中继)。

      任务管理程序可以没有自己的LDT(处理器允许一个程序没有LDT),但是他必须要有TSS来进行任务切换。下面就演示一段来设置任务管理器的程序(把内核看成任务管理器)。

    经过上面一番设置以后,就可以认为任务管理器已经加载了。

    2. 任务切换的方法

      Intel处理器为任务切换提供了很多方法,以灵活地在各个任务实施切换。

      第一个方法就是借助中断来进行任务切换,这是现代抢占式多任务的基础。在实模式下,内存最低端1KB是中断向量表,保存着256个中断处理过程的段地址和偏移地址。在保护模式下,处理器不再使用中断向量表,而是使用中断描述符表。中段描述符表和GDT,LDT是一样的,用于保存描述符,唯一不同的地方是,他保存的是门描述符,包括中断门,陷阱门和任务门。中断门和陷阱门允许在任务内实施中断处理,转到全局空间去执行一些系统级的管理工作,本质上,也是任务内的控制转移行为。但是,在中断发生的时候,如果该中断号对应的门是任务门,那么必须进行任务切换(中断当前任务的执行,保护当前任务的现场,并转移到另一个任务去执行)。

      任务门的样子如上,任务门也是一个系统段,所以S一定是0,TYPE位是0101表明是任务门。任务门的P位来表示这个门是否有效,当P=0时候,不允许通过此门来进行任务切换,DPL是任务门描述符的特权级,但是对因为中断而发起的任务切换不起作用,处理器不按特权级施加任何保护。

      当中断发生的时候,处理器用中断号乘以8作为索引访问中段描述符表,当这个门是任务门时,则处理器会取出新任务的TSS选择子,然后用TSS的选择子访问GDT,取出TSS描述符。然后,处理器访问新的TSS,从中恢复在TSS登记的所有东西。然后,TR开始指向新任务的TSS,一旦新任务开始执行,则处理器固件会自动将TSS描述符的B位设置为1(表示在忙)。

      当中断发生的时候,可以执行常规的中断处理过程。也可以进行任务切换。这两者都使用iret指令返回。前者是返回到同一个任务的不同代码段,后者是返回到被中断的任务。处理器通过EFLAGS位来区分这两者的区别。

      如图,EFLAGS的14位就是NT位(Nested Task Flag),这个位指示的是任务是否嵌套。本质上,因为现在中断的描述符全部都是门,所以都是可以根据NT位来确定如何进行返回的。如果一个任务的切换是因为中断发起的,发生任务切换以后,旧任务的TSS描述符的B位不会改变,处理器将现场的TSS的所有需要填写的项都填写好,然后把旧任务的TSS描述符选择子写入新任务的任务连接域中(0x00)处,同时将新任务EFLAGS寄存器的NT位置变为1,以允许新任务返回旧任务的操作,同时还要把新任务的TSS描述符的B位设置为1(忙)。

      回到iret指令上来,当处理器看见iret指令,则会立马检查NT位,如果这个位是0,那么就按一般的中断返回操作进行;如果这个位是1,那么就会返回原先被中断的任务的位置继续进行旧任务,同时把NT位复原,并把切换前的任务的TSS描述符的B位设置为0。然后TR指向原来被切换的任务的TSS,进行任务切换。

      事实上,处理器除了可以用中断进行任务切换,还有其他三种切换任务的方式。处理器切换任务的方式有4种,如下:

      1. 当前程序,任务或者过程执行控制转移到GDT内某个TSS描述符(使用jmp或者call进行切换)。
      2. 当前程序,任务或者过程执行控制转移到GDT或者当前LDT内某个任务门描述符(任务门可以在任务的LDT上,TSS一定要在GDT上)(使用jmp或者call进行切换)。
      3. 一个异常或者中断发生时候,中断号指向中断描述表内的任务门。
      4. 在EFLAGS寄存器的NT位置为的情况下,当前任务执行一个iret指令。

      由call指令发起的任务切换类似于因为中断发起的任务切换,也就是说,call指令发起的任务切换是嵌套的,当前任务(旧任务)TSS描述符的B位保持原来的“1”不变。EFLAGS寄存器的NT位也不会发生变化。新任务的TSS描述符的B位置1,EFLAGS的NT位也置1,表示此任务嵌套于其他任务中,同时,TSS任务连接域的内容改为旧任务的TSS描述符选择子。

      和call指令不同,使用jmp指令发起的任务切换,不会形成任务的嵌套关系。执行任务切换的时候,当前任务(旧任务)的TSS描述符清零,变为非忙状态,EFLAGS寄存器的NT位不变;新任务的TSS描述符的B位置1,进入忙的状态,EFLAGS寄存器的NT位保持从TSS中加载时的状态度不变。

           不管如何,任务是不可以重入的,本质上来说,就是发起任务切换的时候,新任务的TSS描述符的B位不能是1,处理器每次切换任务都会检查TSS描述符的B位。

           在进行任务切换的时候,处理器执行以下操作:

      1. 使用JMP或者CALL指令的操作数(注意任务门的选择子问题,和调用门一样,进行远调用的时候任务门会忽略32位的偏移地址,直接用16位的选择子部分),任务门或者当前任务的TSS任务连接域取得新任务的TSS描述符选择子。
      2. 检查是否允许从当前任务切换到新任务,数据访问的特权级检查适用于jmp和call指令,当前(旧)任务的CPL和新任务段的RPL必须在数值上小于或者等于目标TSS或者任务门的DPL。异常,中断(除了由int n指令发起的中断)和iret指令引起的任务切换忽略任务门或者TSS描述符的DPL,对于以int n发起的任务切换,要检查DPL。
      3. 检查新任务的TSS描述符是否被标记为有效(P=1),而且界限要大于103,同时检查任务的B位,除了iret指令发起的切换,其他切换B位要等于0(防止任务重入),iret指令发起的切换B=1。
      4. 检查当前任务(旧任务)和新任务的TSS,以及所有在任务切换时要用到的段描述符已经安排到系统内存中。
      5. 如果任务切换是由jmp或者iret发起的,处理器清除当前(旧)任务的B位,如果是由call,异常或者中断发起的,那么B位就维持原来的状态。
      6. 如果任务切换是由iret发起的,那么处理器建立EFLAGS寄存器的副本并且把NT位清除,如果是其他三种方式发起的切换,那么副本中的NT位保持不变。
      7. 保存当前任务状态到他的TSS中(TSS所需要的所有副本)。
      8. 如果任务切换是由call,异常或者中断发起的,则处理器从新任务加载的EFLAGS寄存器的NT位置1;如果是由iret或者jmp发起的,则NT位保持不变。
      9. 如果任务是由call,jmp,异常或者中断发起的,则处理器将新任务的TSS描述符的B位置1;如果由iret发起,则B位保持不变。
      10. 用新任务的TSS选择子和TSS描述符加载到任务寄存器TR(注意描述符的基地址,段界限和属性加载到TR的高速缓存器中了)。
      11. 新任务的TSS状态数据被加载到处理器(TSS里面有的都要加载)。载入期间只要有一个发生故障,架构状态会被破坏(无法撤销)。所谓架构状态就是从一个状态转移到另一个状态是一确定的,不会出现不可预知的状况。
      12. 段选择子对应的描述符也会被加载,与加载和验证新环境有关的任务错误都会破坏架构状态,如果前面1-10步出现了不可恢复的错误,处理器将不能完成任务切换,并且确保处理器返回到执行发起任务切换的那条指令前的状态,如果在11步出现了不可恢复的错误,架构状态将会被破坏。如果在12步(提交点)以后发生了不可恢复错误,处理器将会完成任务切换并且在开始执行新任务之前产生一个相应的异常。
      13. 开始执行新任务。任务切换的时候,新任务的特权级别不是从那个被挂起的任务继承来的。新任务的特权级别都是由其段寄存器CS的低2位决定的,而该寄存器的内容取自新任务的TSS。因为每个任务都有自己的独立地址空间和任务状态段TSS,所以任务之间是彼此隔离的。只需要用特权级规则控制对TSS的访问就行,软件不需要在任务切换时进行显式的特权级检查。

    ★PART2:本章代码

    1. 书上原来配套代码

      注意书上返回的时候用的是iretd,这个指令功能其实是和iret是一样的,只是他是编译器提供的,和pushfd,popfd一样,只是在16位模式下往iret前面加前缀而已。iret指令的机器码是CF,在16位模式下,iret是没有指令前缀0x66的,ireted有0x66;在32位保护模式下,iret和iretd指令的机器码都是CF。

      不过书上的任务返回的时候,为了说清楚call和jmp指令的返回差别,他用一个调用门强行把任务转移回内核中,事实上这样的话就任务切换机制就被破坏了,也就是不能再用TSS再加载到上一个任务的跳转位置再进行指令。(可以跑回调用门的最后,但是这样就没有意义了)。

      1          ;代码清单15-1
      2          ;文件名:c15_core.asm
      3          ;文件说明:保护模式微型核心程序 
      4          ;创建日期:2011-11-19 21:40
      5 
      6          ;以下常量定义部分。内核的大部分内容都应当固定 
      7          core_code_seg_sel     equ  0x38    ;内核代码段选择子
      8          core_data_seg_sel     equ  0x30    ;内核数据段选择子 
      9          sys_routine_seg_sel   equ  0x28    ;系统公共例程代码段的选择子 
     10          video_ram_seg_sel     equ  0x20    ;视频显示缓冲区的段选择子
     11          core_stack_seg_sel    equ  0x18    ;内核堆栈段选择子
     12          mem_0_4_gb_seg_sel    equ  0x08    ;整个0-4GB内存的段的选择子
     13 
     14 ;-------------------------------------------------------------------------------
     15          ;以下是系统核心的头部,用于加载核心程序 
     16          core_length      dd core_end       ;核心程序总长度#00
     17 
     18          sys_routine_seg  dd section.sys_routine.start
     19                                             ;系统公用例程段位置#04
     20 
     21          core_data_seg    dd section.core_data.start
     22                                             ;核心数据段位置#08
     23 
     24          core_code_seg    dd section.core_code.start
     25                                             ;核心代码段位置#0c
     26 
     27 
     28          core_entry       dd start          ;核心代码段入口点#10
     29                           dw core_code_seg_sel
     30 
     31 ;===============================================================================
     32          [bits 32]
     33 ;===============================================================================
     34 SECTION sys_routine vstart=0                ;系统公共例程代码段 
     35 ;-------------------------------------------------------------------------------
     36          ;字符串显示例程
     37 put_string:                                 ;显示0终止的字符串并移动光标 
     38                                             ;输入:DS:EBX=串地址
     39          push ecx
     40   .getc:
     41          mov cl,[ebx]
     42          or cl,cl
     43          jz .exit
     44          call put_char
     45          inc ebx
     46          jmp .getc
     47 
     48   .exit:
     49          pop ecx
     50          retf                               ;段间返回
     51 
     52 ;-------------------------------------------------------------------------------
     53 put_char:                                   ;在当前光标处显示一个字符,并推进
     54                                             ;光标。仅用于段内调用 
     55                                             ;输入:CL=字符ASCII码 
     56          pushad
     57 
     58          ;以下取当前光标位置
     59          mov dx,0x3d4
     60          mov al,0x0e
     61          out dx,al
     62          inc dx                             ;0x3d5
     63          in al,dx                           ;高字
     64          mov ah,al
     65 
     66          dec dx                             ;0x3d4
     67          mov al,0x0f
     68          out dx,al
     69          inc dx                             ;0x3d5
     70          in al,dx                           ;低字
     71          mov bx,ax                          ;BX=代表光标位置的16位数
     72 
     73          cmp cl,0x0d                        ;回车符?
     74          jnz .put_0a
     75          mov ax,bx
     76          mov bl,80
     77          div bl
     78          mul bl
     79          mov bx,ax
     80          jmp .set_cursor
     81 
     82   .put_0a:
     83          cmp cl,0x0a                        ;换行符?
     84          jnz .put_other
     85          add bx,80
     86          jmp .roll_screen
     87 
     88   .put_other:                               ;正常显示字符
     89          push es
     90          mov eax,video_ram_seg_sel          ;0xb8000段的选择子
     91          mov es,eax
     92          shl bx,1
     93          mov [es:bx],cl
     94          pop es
     95 
     96          ;以下将光标位置推进一个字符
     97          shr bx,1
     98          inc bx
     99 
    100   .roll_screen:
    101          cmp bx,2000                        ;光标超出屏幕?滚屏
    102          jl .set_cursor
    103 
    104          push ds
    105          push es
    106          mov eax,video_ram_seg_sel
    107          mov ds,eax
    108          mov es,eax
    109          cld
    110          mov esi,0xa0                       ;小心!32位模式下movsb/w/d 
    111          mov edi,0x00                       ;使用的是esi/edi/ecx 
    112          mov ecx,1920
    113          rep movsd
    114          mov bx,3840                        ;清除屏幕最底一行
    115          mov ecx,80                         ;32位程序应该使用ECX
    116   .cls:
    117          mov word[es:bx],0x0720
    118          add bx,2
    119          loop .cls
    120 
    121          pop es
    122          pop ds
    123 
    124          mov bx,1920
    125 
    126   .set_cursor:
    127          mov dx,0x3d4
    128          mov al,0x0e
    129          out dx,al
    130          inc dx                             ;0x3d5
    131          mov al,bh
    132          out dx,al
    133          dec dx                             ;0x3d4
    134          mov al,0x0f
    135          out dx,al
    136          inc dx                             ;0x3d5
    137          mov al,bl
    138          out dx,al
    139 
    140          popad
    141          
    142          ret                                
    143 
    144 ;-------------------------------------------------------------------------------
    145 read_hard_disk_0:                           ;从硬盘读取一个逻辑扇区
    146                                             ;EAX=逻辑扇区号
    147                                             ;DS:EBX=目标缓冲区地址
    148                                             ;返回:EBX=EBX+512
    149          push eax 
    150          push ecx
    151          push edx
    152       
    153          push eax
    154          
    155          mov dx,0x1f2
    156          mov al,1
    157          out dx,al                          ;读取的扇区数
    158 
    159          inc dx                             ;0x1f3
    160          pop eax
    161          out dx,al                          ;LBA地址7~0
    162 
    163          inc dx                             ;0x1f4
    164          mov cl,8
    165          shr eax,cl
    166          out dx,al                          ;LBA地址15~8
    167 
    168          inc dx                             ;0x1f5
    169          shr eax,cl
    170          out dx,al                          ;LBA地址23~16
    171 
    172          inc dx                             ;0x1f6
    173          shr eax,cl
    174          or al,0xe0                         ;第一硬盘  LBA地址27~24
    175          out dx,al
    176 
    177          inc dx                             ;0x1f7
    178          mov al,0x20                        ;读命令
    179          out dx,al
    180 
    181   .waits:
    182          in al,dx
    183          and al,0x88
    184          cmp al,0x08
    185          jnz .waits                         ;不忙,且硬盘已准备好数据传输 
    186 
    187          mov ecx,256                        ;总共要读取的字数
    188          mov dx,0x1f0
    189   .readw:
    190          in ax,dx
    191          mov [ebx],ax
    192          add ebx,2
    193          loop .readw
    194 
    195          pop edx
    196          pop ecx
    197          pop eax
    198       
    199          retf                               ;段间返回 
    200 
    201 ;-------------------------------------------------------------------------------
    202 ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 
    203 put_hex_dword:                              ;在当前光标处以十六进制形式显示
    204                                             ;一个双字并推进光标 
    205                                             ;输入:EDX=要转换并显示的数字
    206                                             ;输出:无
    207          pushad
    208          push ds
    209       
    210          mov ax,core_data_seg_sel           ;切换到核心数据段 
    211          mov ds,ax
    212       
    213          mov ebx,bin_hex                    ;指向核心数据段内的转换表
    214          mov ecx,8
    215   .xlt:    
    216          rol edx,4
    217          mov eax,edx
    218          and eax,0x0000000f
    219          xlat
    220       
    221          push ecx
    222          mov cl,al                           
    223          call put_char
    224          pop ecx
    225        
    226          loop .xlt
    227       
    228          pop ds
    229          popad
    230          retf
    231       
    232 ;-------------------------------------------------------------------------------
    233 allocate_memory:                            ;分配内存
    234                                             ;输入:ECX=希望分配的字节数
    235                                             ;输出:ECX=起始线性地址 
    236          push ds
    237          push eax
    238          push ebx
    239       
    240          mov eax,core_data_seg_sel
    241          mov ds,eax
    242       
    243          mov eax,[ram_alloc]
    244          add eax,ecx                        ;下一次分配时的起始地址
    245       
    246          ;这里应当有检测可用内存数量的指令
    247           
    248          mov ecx,[ram_alloc]                ;返回分配的起始地址
    249 
    250          mov ebx,eax
    251          and ebx,0xfffffffc
    252          add ebx,4                          ;强制对齐 
    253          test eax,0x00000003                ;下次分配的起始地址最好是4字节对齐
    254          cmovnz eax,ebx                     ;如果没有对齐,则强制对齐 
    255          mov [ram_alloc],eax                ;下次从该地址分配内存
    256                                             ;cmovcc指令可以避免控制转移 
    257          pop ebx
    258          pop eax
    259          pop ds
    260 
    261          retf
    262 
    263 ;-------------------------------------------------------------------------------
    264 set_up_gdt_descriptor:                      ;在GDT内安装一个新的描述符
    265                                             ;输入:EDX:EAX=描述符 
    266                                             ;输出:CX=描述符的选择子
    267          push eax
    268          push ebx
    269          push edx
    270 
    271          push ds
    272          push es
    273 
    274          mov ebx,core_data_seg_sel          ;切换到核心数据段
    275          mov ds,ebx
    276 
    277          sgdt [pgdt]                        ;以便开始处理GDT
    278 
    279          mov ebx,mem_0_4_gb_seg_sel
    280          mov es,ebx
    281 
    282          movzx ebx,word [pgdt]              ;GDT界限
    283          inc bx                             ;GDT总字节数,也是下一个描述符偏移
    284          add ebx,[pgdt+2]                   ;下一个描述符的线性地址
    285 
    286          mov [es:ebx],eax
    287          mov [es:ebx+4],edx
    288 
    289          add word [pgdt],8                  ;增加一个描述符的大小
    290 
    291          lgdt [pgdt]                        ;对GDT的更改生效
    292 
    293          mov ax,[pgdt]                      ;得到GDT界限值
    294          xor dx,dx
    295          mov bx,8
    296          div bx                             ;除以8,去掉余数
    297          mov cx,ax
    298          shl cx,3                           ;将索引号移到正确位置
    299 
    300          pop es
    301          pop ds
    302 
    303          pop edx
    304          pop ebx
    305          pop eax
    306 
    307          retf
    308 ;-------------------------------------------------------------------------------
    309 make_seg_descriptor:                        ;构造存储器和系统的段描述符
    310                                             ;输入:EAX=线性基地址
    311                                             ;      EBX=段界限
    312                                             ;      ECX=属性。各属性位都在原始
    313                                             ;          位置,无关的位清零 
    314                                             ;返回:EDX:EAX=描述符
    315          mov edx,eax
    316          shl eax,16
    317          or ax,bx                           ;描述符前32位(EAX)构造完毕
    318 
    319          and edx,0xffff0000                 ;清除基地址中无关的位
    320          rol edx,8
    321          bswap edx                          ;装配基址的31~24和23~16  (80486+)
    322 
    323          xor bx,bx
    324          or edx,ebx                         ;装配段界限的高4位
    325 
    326          or edx,ecx                         ;装配属性
    327 
    328          retf
    329 
    330 ;-------------------------------------------------------------------------------
    331 make_gate_descriptor:                       ;构造门的描述符(调用门等)
    332                                             ;输入:EAX=门代码在段内偏移地址
    333                                             ;       BX=门代码所在段的选择子 
    334                                             ;       CX=段类型及属性等(各属
    335                                             ;          性位都在原始位置)
    336                                             ;返回:EDX:EAX=完整的描述符
    337          push ebx
    338          push ecx
    339       
    340          mov edx,eax
    341          and edx,0xffff0000                 ;得到偏移地址高16位 
    342          or dx,cx                           ;组装属性部分到EDX
    343        
    344          and eax,0x0000ffff                 ;得到偏移地址低16位 
    345          shl ebx,16                          
    346          or eax,ebx                         ;组装段选择子部分
    347       
    348          pop ecx
    349          pop ebx
    350       
    351          retf                                   
    352                              
    353 ;-------------------------------------------------------------------------------
    354 terminate_current_task:                     ;终止当前任务
    355                                             ;注意,执行此例程时,当前任务仍在
    356                                             ;运行中。此例程其实也是当前任务的
    357                                             ;一部分 
    358          pushfd
    359          mov edx,[esp]                      ;获得EFLAGS寄存器内容
    360          add esp,4                          ;恢复堆栈指针
    361 
    362          mov eax,core_data_seg_sel
    363          mov ds,eax
    364 
    365          test dx,0100_0000_0000_0000B       ;测试NT位
    366          jnz .b1                            ;当前任务是嵌套的,到.b1执行iretd 
    367          mov ebx,core_msg1                  ;当前任务不是嵌套的,直接切换到 
    368          call sys_routine_seg_sel:put_string
    369          jmp far [prgman_tss]               ;程序管理器任务 
    370        
    371   .b1: 
    372          mov ebx,core_msg0
    373          call sys_routine_seg_sel:put_string
    374          iretd
    375       
    376 sys_routine_end:
    377 
    378 ;===============================================================================
    379 SECTION core_data vstart=0                  ;系统核心的数据段 
    380 ;------------------------------------------------------------------------------- 
    381          pgdt             dw  0             ;用于设置和修改GDT 
    382                           dd  0
    383 
    384          ram_alloc        dd  0x00100000    ;下次分配内存时的起始地址
    385 
    386          ;符号地址检索表
    387          salt:
    388          salt_1           db  '@PrintString'
    389                      times 256-($-salt_1) db 0
    390                           dd  put_string
    391                           dw  sys_routine_seg_sel
    392 
    393          salt_2           db  '@ReadDiskData'
    394                      times 256-($-salt_2) db 0
    395                           dd  read_hard_disk_0
    396                           dw  sys_routine_seg_sel
    397 
    398          salt_3           db  '@PrintDwordAsHexString'
    399                      times 256-($-salt_3) db 0
    400                           dd  put_hex_dword
    401                           dw  sys_routine_seg_sel
    402 
    403          salt_4           db  '@TerminateProgram'
    404                      times 256-($-salt_4) db 0
    405                           dd  terminate_current_task
    406                           dw  sys_routine_seg_sel
    407 
    408          salt_item_len   equ $-salt_4
    409          salt_items      equ ($-salt)/salt_item_len
    410 
    411          message_1        db  '  If you seen this message,that means we '
    412                           db  'are now in protect mode,and the system '
    413                           db  'core is loaded,and the video display '
    414                           db  'routine works perfectly.',0x0d,0x0a,0
    415 
    416          message_2        db  '  System wide CALL-GATE mounted.',0x0d,0x0a,0
    417          
    418          bin_hex          db '0123456789ABCDEF'
    419                                             ;put_hex_dword子过程用的查找表 
    420 
    421          core_buf   times 2048 db 0         ;内核用的缓冲区
    422 
    423          cpu_brnd0        db 0x0d,0x0a,'  ',0
    424          cpu_brand  times 52 db 0
    425          cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0
    426 
    427          ;任务控制块链
    428          tcb_chain        dd  0
    429 
    430          ;程序管理器的任务信息 
    431          prgman_tss       dd  0             ;程序管理器的TSS基地址
    432                           dw  0             ;程序管理器的TSS描述符选择子 
    433 
    434          prgman_msg1      db  0x0d,0x0a
    435                           db  '[PROGRAM MANAGER]: Hello! I am Program Manager,'
    436                           db  'run at CPL=0.Now,create user task and switch '
    437                           db  'to it by the CALL instruction...',0x0d,0x0a,0
    438                  
    439          prgman_msg2      db  0x0d,0x0a
    440                           db  '[PROGRAM MANAGER]: I am glad to regain control.'
    441                           db  'Now,create another user task and switch to '
    442                           db  'it by the JMP instruction...',0x0d,0x0a,0
    443                  
    444          prgman_msg3      db  0x0d,0x0a
    445                           db  '[PROGRAM MANAGER]: I am gain control again,'
    446                           db  'HALT...',0
    447 
    448          core_msg0        db  0x0d,0x0a
    449                           db  '[SYSTEM CORE]: Uh...This task initiated with '
    450                           db  'CALL instruction or an exeception/ interrupt,'
    451                           db  'should use IRETD instruction to switch back...'
    452                           db  0x0d,0x0a,0
    453 
    454          core_msg1        db  0x0d,0x0a
    455                           db  '[SYSTEM CORE]: Uh...This task initiated with '
    456                           db  'JMP instruction,  should switch to Program '
    457                           db  'Manager directly by the JMP instruction...'
    458                           db  0x0d,0x0a,0
    459 
    460 core_data_end:
    461                
    462 ;===============================================================================
    463 SECTION core_code vstart=0
    464 ;-------------------------------------------------------------------------------
    465 fill_descriptor_in_ldt:                     ;在LDT内安装一个新的描述符
    466                                             ;输入:EDX:EAX=描述符
    467                                             ;          EBX=TCB基地址
    468                                             ;输出:CX=描述符的选择子
    469          push eax
    470          push edx
    471          push edi
    472          push ds
    473 
    474          mov ecx,mem_0_4_gb_seg_sel
    475          mov ds,ecx
    476 
    477          mov edi,[ebx+0x0c]                 ;获得LDT基地址
    478          
    479          xor ecx,ecx
    480          mov cx,[ebx+0x0a]                  ;获得LDT界限
    481          inc cx                             ;LDT的总字节数,即新描述符偏移地址
    482          
    483          mov [edi+ecx+0x00],eax
    484          mov [edi+ecx+0x04],edx             ;安装描述符
    485 
    486          add cx,8                           
    487          dec cx                             ;得到新的LDT界限值 
    488 
    489          mov [ebx+0x0a],cx                  ;更新LDT界限值到TCB
    490 
    491          mov ax,cx
    492          xor dx,dx
    493          mov cx,8
    494          div cx
    495          
    496          mov cx,ax
    497          shl cx,3                           ;左移3位,并且
    498          or cx,0000_0000_0000_0100B         ;使TI位=1,指向LDT,最后使RPL=00 
    499 
    500          pop ds
    501          pop edi
    502          pop edx
    503          pop eax
    504      
    505          ret
    506          
    507 ;------------------------------------------------------------------------------- 
    508 load_relocate_program:                      ;加载并重定位用户程序
    509                                             ;输入: PUSH 逻辑扇区号
    510                                             ;      PUSH 任务控制块基地址
    511                                             ;输出:无 
    512          pushad
    513       
    514          push ds
    515          push es
    516       
    517          mov ebp,esp                        ;为访问通过堆栈传递的参数做准备
    518       
    519          mov ecx,mem_0_4_gb_seg_sel
    520          mov es,ecx
    521       
    522          mov esi,[ebp+11*4]                 ;从堆栈中取得TCB的基地址
    523 
    524          ;以下申请创建LDT所需要的内存
    525          mov ecx,160                        ;允许安装20个LDT描述符
    526          call sys_routine_seg_sel:allocate_memory
    527          mov [es:esi+0x0c],ecx              ;登记LDT基地址到TCB中
    528          mov word [es:esi+0x0a],0xffff      ;登记LDT初始的界限到TCB中 
    529 
    530          ;以下开始加载用户程序 
    531          mov eax,core_data_seg_sel
    532          mov ds,eax                         ;切换DS到内核数据段
    533        
    534          mov eax,[ebp+12*4]                 ;从堆栈中取出用户程序起始扇区号 
    535          mov ebx,core_buf                   ;读取程序头部数据     
    536          call sys_routine_seg_sel:read_hard_disk_0
    537 
    538          ;以下判断整个程序有多大
    539          mov eax,[core_buf]                 ;程序尺寸
    540          mov ebx,eax
    541          and ebx,0xfffffe00                 ;使之512字节对齐(能被512整除的数低 
    542          add ebx,512                        ;9位都为0 
    543          test eax,0x000001ff                ;程序的大小正好是512的倍数吗? 
    544          cmovnz eax,ebx                     ;不是。使用凑整的结果
    545       
    546          mov ecx,eax                        ;实际需要申请的内存数量
    547          call sys_routine_seg_sel:allocate_memory
    548          mov [es:esi+0x06],ecx              ;登记程序加载基地址到TCB中
    549       
    550          mov ebx,ecx                        ;ebx -> 申请到的内存首地址
    551          xor edx,edx
    552          mov ecx,512
    553          div ecx
    554          mov ecx,eax                        ;总扇区数 
    555       
    556          mov eax,mem_0_4_gb_seg_sel         ;切换DS到0-4GB的段
    557          mov ds,eax
    558 
    559          mov eax,[ebp+12*4]                 ;起始扇区号 
    560   .b1:
    561          call sys_routine_seg_sel:read_hard_disk_0
    562          inc eax
    563          loop .b1                           ;循环读,直到读完整个用户程序
    564 
    565          mov edi,[es:esi+0x06]              ;获得程序加载基地址
    566 
    567          ;建立程序头部段描述符
    568          mov eax,edi                        ;程序头部起始线性地址
    569          mov ebx,[edi+0x04]                 ;段长度
    570          dec ebx                            ;段界限
    571          mov ecx,0x0040f200                 ;字节粒度的数据段描述符,特权级3 
    572          call sys_routine_seg_sel:make_seg_descriptor
    573       
    574          ;安装头部段描述符到LDT中 
    575          mov ebx,esi                        ;TCB的基地址
    576          call fill_descriptor_in_ldt
    577 
    578          or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    579          mov [es:esi+0x44],cx               ;登记程序头部段选择子到TCB 
    580          mov [edi+0x04],cx                  ;和头部内 
    581       
    582          ;建立程序代码段描述符
    583          mov eax,edi
    584          add eax,[edi+0x14]                 ;代码起始线性地址
    585          mov ebx,[edi+0x18]                 ;段长度
    586          dec ebx                            ;段界限
    587          mov ecx,0x0040f800                 ;字节粒度的代码段描述符,特权级3
    588          call sys_routine_seg_sel:make_seg_descriptor
    589          mov ebx,esi                        ;TCB的基地址
    590          call fill_descriptor_in_ldt
    591          or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    592          mov [edi+0x14],cx                  ;登记代码段选择子到头部
    593 
    594          ;建立程序数据段描述符
    595          mov eax,edi
    596          add eax,[edi+0x1c]                 ;数据段起始线性地址
    597          mov ebx,[edi+0x20]                 ;段长度
    598          dec ebx                            ;段界限 
    599          mov ecx,0x0040f200                 ;字节粒度的数据段描述符,特权级3
    600          call sys_routine_seg_sel:make_seg_descriptor
    601          mov ebx,esi                        ;TCB的基地址
    602          call fill_descriptor_in_ldt
    603          or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    604          mov [edi+0x1c],cx                  ;登记数据段选择子到头部
    605 
    606          ;建立程序堆栈段描述符
    607          mov ecx,[edi+0x0c]                 ;4KB的倍率 
    608          mov ebx,0x000fffff
    609          sub ebx,ecx                        ;得到段界限
    610          mov eax,4096                        
    611          mul ecx                         
    612          mov ecx,eax                        ;准备为堆栈分配内存 
    613          call sys_routine_seg_sel:allocate_memory
    614          add eax,ecx                        ;得到堆栈的高端物理地址 
    615          mov ecx,0x00c0f600                 ;字节粒度的堆栈段描述符,特权级3
    616          call sys_routine_seg_sel:make_seg_descriptor
    617          mov ebx,esi                        ;TCB的基地址
    618          call fill_descriptor_in_ldt
    619          or cx,0000_0000_0000_0011B         ;设置选择子的特权级为3
    620          mov [edi+0x08],cx                  ;登记堆栈段选择子到头部
    621 
    622          ;重定位SALT 
    623          mov eax,mem_0_4_gb_seg_sel         ;这里和前一章不同,头部段描述符
    624          mov es,eax                         ;已安装,但还没有生效,故只能通
    625                                             ;过4GB段访问用户程序头部          
    626          mov eax,core_data_seg_sel
    627          mov ds,eax
    628       
    629          cld
    630 
    631          mov ecx,[es:edi+0x24]              ;U-SALT条目数(通过访问4GB段取得) 
    632          add edi,0x28                       ;U-SALT在4GB段内的偏移 
    633   .b2: 
    634          push ecx
    635          push edi
    636       
    637          mov ecx,salt_items
    638          mov esi,salt
    639   .b3:
    640          push edi
    641          push esi
    642          push ecx
    643 
    644          mov ecx,64                         ;检索表中,每条目的比较次数 
    645          repe cmpsd                         ;每次比较4字节 
    646          jnz .b4
    647          mov eax,[esi]                      ;若匹配,则esi恰好指向其后的地址
    648          mov [es:edi-256],eax               ;将字符串改写成偏移地址 
    649          mov ax,[esi+4]
    650          or ax,0000000000000011B            ;以用户程序自己的特权级使用调用门
    651                                             ;故RPL=3 
    652          mov [es:edi-252],ax                ;回填调用门选择子 
    653   .b4:
    654       
    655          pop ecx
    656          pop esi
    657          add esi,salt_item_len
    658          pop edi                            ;从头比较 
    659          loop .b3
    660       
    661          pop edi
    662          add edi,256
    663          pop ecx
    664          loop .b2
    665 
    666          mov esi,[ebp+11*4]                 ;从堆栈中取得TCB的基地址
    667 
    668          ;创建0特权级堆栈
    669          mov ecx,4096
    670          mov eax,ecx                        ;为生成堆栈高端地址做准备 
    671          mov [es:esi+0x1a],ecx
    672          shr dword [es:esi+0x1a],12         ;登记0特权级堆栈尺寸到TCB 
    673          call sys_routine_seg_sel:allocate_memory
    674          add eax,ecx                        ;堆栈必须使用高端地址为基地址
    675          mov [es:esi+0x1e],eax              ;登记0特权级堆栈基地址到TCB 
    676          mov ebx,0xffffe                    ;段长度(界限)
    677          mov ecx,0x00c09600                 ;4KB粒度,读写,特权级0
    678          call sys_routine_seg_sel:make_seg_descriptor
    679          mov ebx,esi                        ;TCB的基地址
    680          call fill_descriptor_in_ldt
    681          ;or cx,0000_0000_0000_0000          ;设置选择子的特权级为0
    682          mov [es:esi+0x22],cx               ;登记0特权级堆栈选择子到TCB
    683          mov dword [es:esi+0x24],0          ;登记0特权级堆栈初始ESP到TCB
    684       
    685          ;创建1特权级堆栈
    686          mov ecx,4096
    687          mov eax,ecx                        ;为生成堆栈高端地址做准备
    688          mov [es:esi+0x28],ecx
    689          shr [es:esi+0x28],12               ;登记1特权级堆栈尺寸到TCB
    690          call sys_routine_seg_sel:allocate_memory
    691          add eax,ecx                        ;堆栈必须使用高端地址为基地址
    692          mov [es:esi+0x2c],eax              ;登记1特权级堆栈基地址到TCB
    693          mov ebx,0xffffe                    ;段长度(界限)
    694          mov ecx,0x00c0b600                 ;4KB粒度,读写,特权级1
    695          call sys_routine_seg_sel:make_seg_descriptor
    696          mov ebx,esi                        ;TCB的基地址
    697          call fill_descriptor_in_ldt
    698          or cx,0000_0000_0000_0001          ;设置选择子的特权级为1
    699          mov [es:esi+0x30],cx               ;登记1特权级堆栈选择子到TCB
    700          mov dword [es:esi+0x32],0          ;登记1特权级堆栈初始ESP到TCB
    701 
    702          ;创建2特权级堆栈
    703          mov ecx,4096
    704          mov eax,ecx                        ;为生成堆栈高端地址做准备
    705          mov [es:esi+0x36],ecx
    706          shr [es:esi+0x36],12               ;登记2特权级堆栈尺寸到TCB
    707          call sys_routine_seg_sel:allocate_memory
    708          add eax,ecx                        ;堆栈必须使用高端地址为基地址
    709          mov [es:esi+0x3a],ecx              ;登记2特权级堆栈基地址到TCB
    710          mov ebx,0xffffe                    ;段长度(界限)
    711          mov ecx,0x00c0d600                 ;4KB粒度,读写,特权级2
    712          call sys_routine_seg_sel:make_seg_descriptor
    713          mov ebx,esi                        ;TCB的基地址
    714          call fill_descriptor_in_ldt
    715          or cx,0000_0000_0000_0010          ;设置选择子的特权级为2
    716          mov [es:esi+0x3e],cx               ;登记2特权级堆栈选择子到TCB
    717          mov dword [es:esi+0x40],0          ;登记2特权级堆栈初始ESP到TCB
    718       
    719          ;在GDT中登记LDT描述符
    720          mov eax,[es:esi+0x0c]              ;LDT的起始线性地址
    721          movzx ebx,word [es:esi+0x0a]       ;LDT段界限
    722          mov ecx,0x00408200                 ;LDT描述符,特权级0
    723          call sys_routine_seg_sel:make_seg_descriptor
    724          call sys_routine_seg_sel:set_up_gdt_descriptor
    725          mov [es:esi+0x10],cx               ;登记LDT选择子到TCB中
    726        
    727          ;创建用户程序的TSS
    728          mov ecx,104                        ;tss的基本尺寸
    729          mov [es:esi+0x12],cx              
    730          dec word [es:esi+0x12]             ;登记TSS界限值到TCB 
    731          call sys_routine_seg_sel:allocate_memory
    732          mov [es:esi+0x14],ecx              ;登记TSS基地址到TCB
    733       
    734          ;登记基本的TSS表格内容
    735          mov word [es:ecx+0],0              ;反向链=0
    736       
    737          mov edx,[es:esi+0x24]              ;登记0特权级堆栈初始ESP
    738          mov [es:ecx+4],edx                 ;到TSS中
    739       
    740          mov dx,[es:esi+0x22]               ;登记0特权级堆栈段选择子
    741          mov [es:ecx+8],dx                  ;到TSS中
    742       
    743          mov edx,[es:esi+0x32]              ;登记1特权级堆栈初始ESP
    744          mov [es:ecx+12],edx                ;到TSS中
    745 
    746          mov dx,[es:esi+0x30]               ;登记1特权级堆栈段选择子
    747          mov [es:ecx+16],dx                 ;到TSS中
    748 
    749          mov edx,[es:esi+0x40]              ;登记2特权级堆栈初始ESP
    750          mov [es:ecx+20],edx                ;到TSS中
    751 
    752          mov dx,[es:esi+0x3e]               ;登记2特权级堆栈段选择子
    753          mov [es:ecx+24],dx                 ;到TSS中
    754 
    755          mov dx,[es:esi+0x10]               ;登记任务的LDT选择子
    756          mov [es:ecx+96],dx                 ;到TSS中
    757       
    758          mov dx,[es:esi+0x12]               ;登记任务的I/O位图偏移
    759          mov [es:ecx+102],dx                ;到TSS中 
    760       
    761          mov word [es:ecx+100],0            ;T=0
    762       
    763          mov dword [es:ecx+28],0            ;登记CR3(PDBR)
    764       
    765          ;访问用户程序头部,获取数据填充TSS 
    766          mov ebx,[ebp+11*4]                 ;从堆栈中取得TCB的基地址
    767          mov edi,[es:ebx+0x06]              ;用户程序加载的基地址 
    768 
    769          mov edx,[es:edi+0x10]              ;登记程序入口点(EIP) 
    770          mov [es:ecx+32],edx                ;到TSS
    771 
    772          mov dx,[es:edi+0x14]               ;登记程序代码段(CS)选择子
    773          mov [es:ecx+76],dx                 ;到TSS中
    774 
    775          mov dx,[es:edi+0x08]               ;登记程序堆栈段(SS)选择子
    776          mov [es:ecx+80],dx                 ;到TSS中
    777 
    778          mov dx,[es:edi+0x04]               ;登记程序数据段(DS)选择子
    779          mov word [es:ecx+84],dx            ;到TSS中。注意,它指向程序头部段
    780       
    781          mov word [es:ecx+72],0             ;TSS中的ES=0
    782 
    783          mov word [es:ecx+88],0             ;TSS中的FS=0
    784 
    785          mov word [es:ecx+92],0             ;TSS中的GS=0
    786 
    787          pushfd
    788          pop edx
    789          
    790          mov dword [es:ecx+36],edx          ;EFLAGS
    791 
    792          ;在GDT中登记TSS描述符
    793          mov eax,[es:esi+0x14]              ;TSS的起始线性地址
    794          movzx ebx,word [es:esi+0x12]       ;段长度(界限)
    795          mov ecx,0x00408900                 ;TSS描述符,特权级0
    796          call sys_routine_seg_sel:make_seg_descriptor
    797          call sys_routine_seg_sel:set_up_gdt_descriptor
    798          mov [es:esi+0x18],cx               ;登记TSS选择子到TCB
    799 
    800          pop es                             ;恢复到调用此过程前的es段 
    801          pop ds                             ;恢复到调用此过程前的ds段
    802       
    803          popad
    804       
    805          ret 8                              ;丢弃调用本过程前压入的参数 
    806       
    807 ;-------------------------------------------------------------------------------
    808 append_to_tcb_link:                         ;在TCB链上追加任务控制块
    809                                             ;输入:ECX=TCB线性基地址
    810          push eax
    811          push edx
    812          push ds
    813          push es
    814          
    815          mov eax,core_data_seg_sel          ;令DS指向内核数据段 
    816          mov ds,eax
    817          mov eax,mem_0_4_gb_seg_sel         ;令ES指向0..4GB段
    818          mov es,eax
    819          
    820          mov dword [es: ecx+0x00],0         ;当前TCB指针域清零,以指示这是最
    821                                             ;后一个TCB
    822                                              
    823          mov eax,[tcb_chain]                ;TCB表头指针
    824          or eax,eax                         ;链表为空?
    825          jz .notcb 
    826          
    827   .searc:
    828          mov edx,eax
    829          mov eax,[es: edx+0x00]
    830          or eax,eax               
    831          jnz .searc
    832          
    833          mov [es: edx+0x00],ecx
    834          jmp .retpc
    835          
    836   .notcb:       
    837          mov [tcb_chain],ecx                ;若为空表,直接令表头指针指向TCB
    838          
    839   .retpc:
    840          pop es
    841          pop ds
    842          pop edx
    843          pop eax
    844          
    845          ret
    846          
    847 ;-------------------------------------------------------------------------------
    848 start:
    849          mov ecx,core_data_seg_sel          ;令DS指向核心数据段 
    850          mov ds,ecx
    851 
    852          mov ecx,mem_0_4_gb_seg_sel         ;令ES指向4GB数据段 
    853          mov es,ecx
    854 
    855          mov ebx,message_1                    
    856          call sys_routine_seg_sel:put_string
    857                                          
    858          ;显示处理器品牌信息 
    859          mov eax,0x80000002
    860          cpuid
    861          mov [cpu_brand + 0x00],eax
    862          mov [cpu_brand + 0x04],ebx
    863          mov [cpu_brand + 0x08],ecx
    864          mov [cpu_brand + 0x0c],edx
    865       
    866          mov eax,0x80000003
    867          cpuid
    868          mov [cpu_brand + 0x10],eax
    869          mov [cpu_brand + 0x14],ebx
    870          mov [cpu_brand + 0x18],ecx
    871          mov [cpu_brand + 0x1c],edx
    872 
    873          mov eax,0x80000004
    874          cpuid
    875          mov [cpu_brand + 0x20],eax
    876          mov [cpu_brand + 0x24],ebx
    877          mov [cpu_brand + 0x28],ecx
    878          mov [cpu_brand + 0x2c],edx
    879 
    880          mov ebx,cpu_brnd0                  ;显示处理器品牌信息 
    881          call sys_routine_seg_sel:put_string
    882          mov ebx,cpu_brand
    883          call sys_routine_seg_sel:put_string
    884          mov ebx,cpu_brnd1
    885          call sys_routine_seg_sel:put_string
    886 
    887          ;以下开始安装为整个系统服务的调用门。特权级之间的控制转移必须使用门
    888          mov edi,salt                       ;C-SALT表的起始位置 
    889          mov ecx,salt_items                 ;C-SALT表的条目数量 
    890   .b3:
    891          push ecx   
    892          mov eax,[edi+256]                  ;该条目入口点的32位偏移地址 
    893          mov bx,[edi+260]                   ;该条目入口点的段选择子 
    894          mov cx,1_11_0_1100_000_00000B      ;特权级3的调用门(3以上的特权级才
    895                                             ;允许访问),0个参数(因为用寄存器
    896                                             ;传递参数,而没有用栈) 
    897          call sys_routine_seg_sel:make_gate_descriptor
    898          call sys_routine_seg_sel:set_up_gdt_descriptor
    899          mov [edi+260],cx                   ;将返回的门描述符选择子回填
    900          add edi,salt_item_len              ;指向下一个C-SALT条目 
    901          pop ecx
    902          loop .b3
    903 
    904          ;对门进行测试 
    905          mov ebx,message_2
    906          call far [salt_1+256]              ;通过门显示信息(偏移量将被忽略) 
    907       
    908          ;为程序管理器的TSS分配内存空间 
    909          mov ecx,104                        ;为该任务的TSS分配内存
    910          call sys_routine_seg_sel:allocate_memory
    911          mov [prgman_tss+0x00],ecx          ;保存程序管理器的TSS基地址 
    912       
    913          ;在程序管理器的TSS中设置必要的项目 
    914          mov word [es:ecx+96],0             ;没有LDT。处理器允许没有LDT的任务。
    915          mov word [es:ecx+102],103          ;没有I/O位图。0特权级事实上不需要。
    916          mov word [es:ecx+0],0              ;反向链=0
    917          mov dword [es:ecx+28],0            ;登记CR3(PDBR)
    918          mov word [es:ecx+100],0            ;T=0
    919                                             ;不需要0、1、2特权级堆栈。0特级不
    920                                             ;会向低特权级转移控制。
    921          
    922          ;创建TSS描述符,并安装到GDT中 
    923          mov eax,ecx                        ;TSS的起始线性地址
    924          mov ebx,103                        ;段长度(界限)
    925          mov ecx,0x00408900                 ;TSS描述符,特权级0
    926          call sys_routine_seg_sel:make_seg_descriptor
    927          call sys_routine_seg_sel:set_up_gdt_descriptor
    928          mov [prgman_tss+0x04],cx           ;保存程序管理器的TSS描述符选择子 
    929 
    930          ;任务寄存器TR中的内容是任务存在的标志,该内容也决定了当前任务是谁。
    931          ;下面的指令为当前正在执行的0特权级任务“程序管理器”后补手续(TSS)。
    932          ltr cx                              
    933 
    934          ;现在可认为“程序管理器”任务正执行中
    935          mov ebx,prgman_msg1
    936          call sys_routine_seg_sel:put_string
    937 
    938          mov ecx,0x46
    939          call sys_routine_seg_sel:allocate_memory
    940          call append_to_tcb_link            ;将此TCB添加到TCB链中 
    941       
    942          push dword 50                      ;用户程序位于逻辑50扇区
    943          push ecx                           ;压入任务控制块起始线性地址 
    944        
    945          call load_relocate_program         
    946       
    947          call far [es:ecx+0x14]             ;执行任务切换。和上一章不同,任务切
    948                                             ;换时要恢复TSS内容,所以在创建任务
    949                                             ;时TSS要填写完整 
    950                                           
    951          ;重新加载并切换任务 
    952          mov ebx,prgman_msg2
    953          call sys_routine_seg_sel:put_string
    954 
    955          mov ecx,0x46
    956          call sys_routine_seg_sel:allocate_memory
    957          call append_to_tcb_link            ;将此TCB添加到TCB链中
    958 
    959          push dword 50                      ;用户程序位于逻辑50扇区
    960          push ecx                           ;压入任务控制块起始线性地址
    961 
    962          call load_relocate_program
    963 
    964          jmp far [es:ecx+0x14]              ;执行任务切换
    965 
    966          mov ebx,prgman_msg3
    967          call sys_routine_seg_sel:put_string
    968 
    969          hlt
    970             
    971 core_code_end:
    972 
    973 ;-------------------------------------------------------------------------------
    974 SECTION core_trail
    975 ;-------------------------------------------------------------------------------
    976 core_end:

     

     1          ;代码清单15-2
     2          ;文件名:c15.asm
     3          ;文件说明:用户程序 
     4          ;创建日期:2011-11-15 19:11   
     5 
     6 ;===============================================================================
     7 SECTION header vstart=0
     8 
     9          program_length   dd program_end          ;程序总长度#0x00
    10          
    11          head_len         dd header_end           ;程序头部的长度#0x04
    12 
    13          stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
    14          stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
    15                                                   ;以4KB为单位
    16                                                   
    17          prgentry         dd start                ;程序入口#0x10 
    18          code_seg         dd section.code.start   ;代码段位置#0x14
    19          code_len         dd code_end             ;代码段长度#0x18
    20 
    21          data_seg         dd section.data.start   ;数据段位置#0x1c
    22          data_len         dd data_end             ;数据段长度#0x20
    23 ;-------------------------------------------------------------------------------
    24          ;符号地址检索表
    25          salt_items       dd (header_end-salt)/256 ;#0x24
    26          
    27          salt:                                     ;#0x28
    28          PrintString      db  '@PrintString'
    29                      times 256-($-PrintString) db 0
    30                      
    31          TerminateProgram db  '@TerminateProgram'
    32                      times 256-($-TerminateProgram) db 0
    33                      
    34          ReadDiskData     db  '@ReadDiskData'
    35                      times 256-($-ReadDiskData) db 0
    36                  
    37 header_end:
    38   
    39 ;===============================================================================
    40 SECTION data vstart=0                
    41 
    42          message_1        db  0x0d,0x0a
    43                           db  '[USER TASK]: Hi! nice to meet you,'
    44                           db  'I am run at CPL=',0
    45                           
    46          message_2        db  0
    47                           db  '.Now,I must exit...',0x0d,0x0a,0
    48 
    49 data_end:
    50 
    51 ;===============================================================================
    52       [bits 32]
    53 ;===============================================================================
    54 SECTION code vstart=0
    55 start:
    56          ;任务启动时,DS指向头部段,也不需要设置堆栈 
    57          mov eax,ds
    58          mov fs,eax
    59      
    60          mov eax,[data_seg]
    61          mov ds,eax
    62      
    63          mov ebx,message_1
    64          call far [fs:PrintString]
    65          
    66          mov ax,cs
    67          and al,0000_0011B
    68          or al,0x30
    69          mov [message_2],al
    70          
    71          mov ebx,message_2
    72          call far [fs:PrintString]
    73      
    74          call far [fs:TerminateProgram]      ;退出,并将控制权返回到核心 
    75     
    76 code_end:
    77 
    78 ;-------------------------------------------------------------------------------
    79 SECTION trail
    80 ;-------------------------------------------------------------------------------
    81 program_end:

     

    3. 习题2

           这一题要求演示任务嵌套,说实话这题还是有点难度的,因为他要求我们在程序A中加载任务B并且切换的,这就要我们直接调用内核的加载程序的程序。我们把内核调用程序的例程改成调用门就好了,这个不是问题。但是问题是我们怎么切换任务B呢?当然我们可以改一下load_program让他返回TSS的选择子给我们,但是我又想到了程序A最好要有他自己所有的私有数据,所以想了一想我还是往任务A的LDT写了调用程序B的任务门了,程序A要有一个地方可以记录加载程序的LDT选择子。最后我给所有任务都填上了任务管理器的TSS选择子,方便切换。

       1 ;===============================内核程序=================================
       2         ;定义内核所要用到的选择子
       3         All_4GB_Segment         equ 0x0008      ;4GB的全内存区域
       4         Stack_Segement          equ 0x0018      ;内核栈区
       5         Print_Segement          equ 0x0020      ;显存映射区
       6         Sys_Routine_Segement    equ 0x0028      ;公用例程段
       7         Core_Data_Segement      equ 0x0030      ;内核数据区
       8         Core_Code_Segement      equ 0x0038      ;内核代码段
       9         ;----------------------------------------------------------------
      10         User_Program_AddressA   equ 50          ;用户程序所在逻辑扇区
      11         Switch_Stack_Size       equ 4096        ;切换栈段的大小
      12 ;=============================内核程序头部===============================
      13 SECTION header vstart=0
      14         Program_Length          dd  Program_end                 ;内核总长度
      15         Sys_Routine_Seg         dd  section.Sys_Routine.start   ;公用例程段线性地址
      16         Core_Data_Seg           dd  section.Core_Data.start     ;内核数据区线性地址
      17         Core_Code_Seg           dd  section.Core_Code.start     ;内核代码区线性地址
      18         Code_Entry              dd  start                       ;注意偏移地址一定是32位的
      19                                 dw  Core_Code_Segement
      20     ;----------------------------------------------------------------
      21                             [bits 32]
      22 ;=========================================================================
      23 ;============================公用例程区===================================
      24 ;=========================================================================
      25 SECTION Sys_Routine align=16 vstart=0
      26     ReadHarddisk:                           ;push1:28位磁盘号(esi)
      27                                             ;push2:应用程序数据段选择子(ax->ds)
      28                                             ;push3: 偏移地址(ebx)
      29                                             ;push4: 应用程序代码段选择子(dx)
      30         pushad
      31         push ds
      32         push es
      33         
      34         mov ebp,esp
      35         
      36         mov esi,[ebp+15*4]
      37         movzx eax,word[ebp+14*4]
      38         mov ebx,[ebp+13*4]
      39         movzx edx,word[ebp+12*4]
      40         
      41         arpl ax,dx
      42         mov ds,ax
      43         
      44         mov dx,0x1f2
      45         mov al,0x01     ;读一个扇区                                
      46         out dx,al
      47         
      48         inc edx         ;0-7位
      49         mov eax,esi
      50         out dx,al
      51         
      52         inc edx         ;8-15位
      53         mov al,ah
      54         out dx,al
      55         
      56         inc edx         ;16-23位
      57         shr eax,16
      58         out dx,al
      59         
      60         inc edx         ;24-28位,主硬盘,LBA模式
      61         mov al,ah
      62         and al,0x0f
      63         or al,0xe0
      64         out dx,al
      65         
      66         inc edx
      67         mov al,0x20
      68         out dx,al
      69         
      70         _wait:
      71             in al,dx
      72             and al,0x88
      73             cmp al,0x08
      74             jne _wait
      75         
      76         mov dx,0x1f0
      77         mov ecx,256
      78         
      79         _read:
      80             in ax,dx
      81             mov [ebx],ax
      82             add ebx,2
      83             loop _read
      84         
      85         pop es
      86         pop ds
      87         popad
      88         retf 16     ;4个数据
      89     ;----------------------------------------------------------------
      90     put_string:                                                 ;ebx:偏移地址
      91         pushad
      92         push ds
      93         push es
      94         
      95         _print:
      96             mov cl,[ebx]
      97             cmp cl,0
      98             je _exit
      99             call put_char
     100             inc ebx
     101             jmp _print
     102         _exit:
     103             pop es
     104             pop ds
     105             popad
     106             retf            ;段间返回
     107         ;-------------------------------------------------------------- 
     108         put_char:           ;cl就是要显示的字符
     109             push ebx
     110             push es
     111             push ds
     112             
     113             mov dx,0x3d4
     114             mov al,0x0e     ;高8位
     115             out dx,al
     116             mov dx,0x3d5
     117             in al,dx
     118             mov ah,al       ;先把高8位存起来
     119             mov dx,0x3d4
     120             mov al,0x0f     ;低8位
     121             out dx,al
     122             mov dx,0x3d5
     123             in al,dx        ;现在ax就是当前光标的位置
     124             
     125             _judge:
     126                 cmp cl,0x0a
     127                 je _set_0x0a
     128                 cmp cl,0x0d
     129                 je _set_0x0d
     130             _print_visible:
     131                 mov bx,ax
     132                 mov eax,Print_Segement
     133                 mov es,eax
     134                 shl bx,1    ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍
     135                 mov [es:bx],cl          ;注意这里是屏幕!
     136                 mov byte[es:bx+1],0x07      
     137                 add bx,2
     138                 shr bx,1
     139                 jmp _roll_screen
     140             _set_0x0d:      ;回车
     141                 mov bl,80
     142                 div bl
     143                 mul bl
     144                 mov bx,ax
     145                 jmp _set_cursor
     146             _set_0x0a:      ;换行
     147                 mov bx,ax
     148                 add bx,80
     149                 jmp _roll_screen
     150             _roll_screen:
     151                 cmp bx,2000
     152                 jl _set_cursor
     153                 mov eax,Print_Segement
     154                 mov ds,eax
     155                 mov es,eax
     156                 
     157                 cld
     158                 mov edi,0x00
     159                 mov esi,0xa0
     160                 mov ecx,1920
     161                 rep movsw
     162             _cls:
     163                 mov bx,3840
     164                 mov ecx,80
     165                 _print_blank:
     166                     mov word[es:bx],0x0720
     167                     add bx,2
     168                     loop _print_blank   
     169                 mov bx,1920 ;别总是忘了光标的位置!
     170             _set_cursor:        ;改变后的光标位置在bx上
     171             mov dx,0x3d4
     172             mov al,0x0f     ;低8位
     173             out dx,al
     174             
     175             mov al,bl
     176             mov dx,0x3d5
     177             out dx,al
     178             
     179             mov dx,0x3d4
     180             mov al,0x0e     ;高8位
     181             out dx,al
     182             
     183             mov al,bh
     184             mov dx,0x3d5
     185             out dx,al
     186             
     187             pop ds
     188             pop es
     189             pop ebx
     190             ret
     191     ;----------------------------------------------------------------       
     192     allocate_memory:                            ;简易内存分配策略
     193                                                 ;输入ecx:想要分配的总字节数
     194                                                 ;输出ecx:分配的线性基地址
     195         push ds
     196         push eax
     197         push ebx
     198         call Cal_User_Mem
     199             
     200         mov eax,Core_Data_Segement
     201         mov ds,eax
     202         mov eax,[ram_alloc]
     203         mov edx,eax                             ;edx暂存一下eax
     204         add eax,ecx
     205         
     206         cmp eax,edx                             ;发现新分配的现地址比原来的还小,说明已经溢出
     207         jge _alloc
     208             mov ebx,mem_alloc_fail
     209             call Sys_Routine_Segement:put_string
     210             mov ecx,0                       ;分配为0说明已经分配失败
     211             jmp _exit1
     212         _alloc:
     213             
     214         mov ebx,eax
     215         and ebx,0xfffffffc
     216         add ebx,4                           ;强行向上取整
     217         test eax,0x00000003
     218         cmovnz eax,ebx
     219         mov ecx,[ram_alloc]                 ;要返回要分配的初始地址
     220         mov [ram_alloc],eax                 ;下一次分配的线性基地址
     221             
     222         _exit1:
     223         pop ebx
     224         pop eax
     225         pop ds
     226         
     227         retf
     228     ;----------------------------------------------------------------
     229     recycled_memory_and_gdt:
     230         mov eax,[ram_recycled]
     231         sub [ram_alloc],eax
     232         mov dword[ram_recycled],0               ;因为我们还没学到多任务,先这样简单地清零
     233         
     234         sgdt [pgdt_base_tmp]
     235         sub word[pgdt_base_tmp],16              ;应用程序的LDT,TSS
     236         lgdt [pgdt_base_tmp]                    ;重新加载内核
     237         retf
     238     ;----------------------------------------------------------------
     239     Cal_User_Mem:                               ;输入ecx:应用程序用到的内存(字节)
     240         add [ram_recycled],ecx
     241         ret
     242     ;----------------------------------------------------------------
     243     Make_Seg_Descriptor:                        ;构造段描述符
     244                                             ;输入:
     245                                             ;eax:线性基地址
     246                                             ;ebx:段界限
     247                                             ;ecx:属性
     248                                             ;输出:
     249                                             ;eax:段描述符低32位
     250                                             ;edx:段描述符高32位
     251         mov edx,eax
     252         and edx,0xffff0000
     253         rol edx,8
     254         bswap edx
     255         or edx,ecx
     256         
     257         shl eax,16
     258         or ax,bx
     259         and ebx,0x000f0000
     260         or edx,ebx
     261         retf                
     262     ;----------------------------------------------------------------       
     263     Make_Gate_Descriptor:                   ;构造门描述符
     264                                             ;输入:
     265                                             ;eax:段内偏移地址
     266                                             ;bx: 段的选择子
     267                                             ;cx: 段的属性
     268                                             ;输出:
     269                                             ;eax:门描述符低32位
     270                                             ;edx:门描述符高32位
     271         push ebx
     272         push ecx
     273         
     274         mov edx,eax
     275         and edx,0xffff0000                  ;要高16位
     276         or dx,cx
     277         
     278         shl ebx,16
     279         and eax,0x0000ffff
     280         or eax,ebx
     281         
     282         pop ecx
     283         pop ebx
     284         
     285         retf                
     286     ;----------------------------------------------------------------
     287     Set_New_GDT:                            ;装载新的全局描述符
     288                                             ;输入:edx:eax描述符
     289                                             ;输出:cx选择子
     290         push ds
     291         push es
     292         
     293         mov ebx,Core_Data_Segement
     294         mov ds,ebx
     295         
     296         mov ebx,All_4GB_Segment
     297         mov es,ebx
     298         
     299         sgdt [pgdt_base_tmp]
     300         
     301         movzx ebx,word[pgdt_base_tmp]
     302         inc bx                              ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的
     303                                             ;要用到回绕特性
     304         add ebx,[pgdt_base_tmp+0x02]        ;得到pgdt的线性基地址
     305         
     306         mov [es:ebx],eax
     307         mov [es:ebx+0x04],edx               ;装载新的gdt符
     308                                             ;装载描述符要装载到实际位置上
     309         
     310         add word[pgdt_base_tmp],8           ;给gdt的段界限加上8(字节)
     311         
     312         lgdt [pgdt_base_tmp]                ;加载gdt到gdtr的位置和实际表的位置无关
     313         
     314         mov ax,[pgdt_base_tmp]              ;得到段界限
     315         xor dx,dx
     316         mov bx,8                            ;得到gdt大小
     317         div bx
     318         mov cx,ax
     319         shl cx,3                            ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级)
     320         
     321         pop es
     322         pop ds
     323         retf
     324     ;----------------------------------------------------------------
     325     Set_New_LDT_To_TCB:                     ;装载新的局部描述符
     326                                             ;输入:edx:eax描述符
     327                                             ;    : ebx:TCB线性基地址
     328                                             ;输出:cx选择子
     329         
     330         push edi
     331         push eax
     332         push ebx
     333         push edx
     334         push ds
     335         
     336         mov ecx,All_4GB_Segment
     337         mov ds,ecx
     338         
     339         mov edi,[ebx+0x0c]                  ;LDT的线性基地址
     340         movzx ecx,word[ebx+0x0a]
     341         inc cx                              ;得到实际的LDT的大小(界限还要-1)
     342         
     343         mov [edi+ecx+0x00],eax
     344         mov [edi+ecx+0x04],edx
     345         
     346         add cx,8
     347         dec cx
     348         
     349         mov [ebx+0x0a],cx
     350         
     351         mov ax,cx
     352         xor dx,dx
     353         mov cx,8
     354         div cx
     355         
     356         shl ax,3
     357         mov cx,ax
     358         or cx,0x0004                        ;LDT,第三位TI位一定是1
     359         
     360         pop ds
     361         pop edx
     362         pop ebx
     363         pop eax
     364         pop edi
     365         retf
     366     ;----------------------------------------------------------------   
     367     PrintDword:                             ;显示edx内容的一个调试函数
     368         pushad
     369         push ds
     370         
     371         mov eax,Core_Data_Segement
     372         mov ds,eax
     373         
     374         mov ebx,bin_hex
     375         mov ecx,8
     376         
     377         _query:
     378             rol edx,4
     379             mov eax,edx
     380             and eax,0x0000000f
     381             xlat
     382             
     383             push ecx
     384             mov cl,al
     385             call put_char
     386             pop ecx
     387             
     388         loop _query
     389             
     390         pop ds
     391         popad
     392         
     393         retf
     394     ;---------------------------------------------------------------------  
     395     load_program_form_user:             ;输入push:用户程序逻辑扇区号
     396                                         ;      fs: 指向用户程序头部
     397         pushad
     398         push ds
     399         push es
     400         
     401         mov ecx,0x46                    ;另一个TCB链
     402         call Sys_Routine_Segement:allocate_memory
     403         call Sys_Routine_Segement:append_to_tcb
     404         
     405         mov ebp,esp                     
     406         
     407         mov eax,Core_Data_Segement
     408         mov ds,eax                      
     409         
     410         mov eax,All_4GB_Segment
     411         mov es,eax
     412         
     413         mov edi,[ebp+11*4]              ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push
     414         
     415         mov ecx,160
     416         call Sys_Routine_Segement:allocate_memory
     417         mov [es:edi+0x0c],ecx
     418         mov word[es:edi+0x0a],0xffff    ;初始化LDT界限位0xffff
     419 
     420         mov esi,[ebp+12*4]              ;esi必须是逻辑扇区号
     421         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了)
     422         
     423         push esi
     424         push ds
     425         push ebx
     426         push cs
     427         call Sys_Routine_Segement:ReadHarddisk
     428         
     429         mov eax,[core_buf]              ;读取用户程序长度
     430         
     431         mov ebx,eax                     
     432         and ebx,0xfffffe00              ;清空低9位(强制对齐512)
     433         add ebx,512                     
     434         test eax,0x000001ff             
     435         cmovnz eax,ebx                  ;低9位不为0则使用向上取整的结果
     436         
     437         mov ecx,eax                     ;eax是整个程序的向上取整的大小
     438         call Sys_Routine_Segement:allocate_memory   
     439                                         ;先分配内存给整个程序,再分配内存给栈区
     440         mov ebx,ecx                                 
     441         mov [es:edi+0x06],ecx           ;tcb 0x06:程序加载基地址
     442         
     443         xor edx,edx
     444         mov ecx,512                     ;千万不要改掉ebx
     445         div ecx
     446         mov ecx,eax
     447         
     448         mov eax,All_4GB_Segment         
     449         mov ds,eax
     450         
     451         _loop_read:
     452             push esi
     453             push ds
     454             push ebx
     455             push cs
     456             call Sys_Routine_Segement:ReadHarddisk  ;esi还是User_Program_Address
     457             inc esi
     458             add ebx,512
     459         loop _loop_read
     460         
     461         mov esi,edi                     ;esi: TCB的线性基地址
     462         mov edi,[es:esi+0x06]           ;程序加载的线性基地址
     463         
     464         ;建立头部描述符
     465         mov eax,edi
     466         mov ebx,[edi+0x04]
     467         dec ebx                         ;段界限
     468         mov ecx,0x0040f200
     469         call Sys_Routine_Segement:Make_Seg_Descriptor
     470         mov ebx,esi
     471         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     472         or cx,0x0003                    ;特权级3
     473         mov [es:esi+0x44],cx            ;记得要登记头部的选择子
     474         mov [edi+0x04],cx
     475         
     476         ;建立代码段描述符
     477         mov eax,edi
     478         add eax,[edi+0x14]
     479         mov ebx,[edi+0x18]
     480         dec ebx
     481         mov ecx,0x0040f800
     482         call Sys_Routine_Segement:Make_Seg_Descriptor
     483         mov ebx,esi
     484         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     485         or cx,0x0003
     486         mov [edi+0x14],cx 
     487         
     488         ;建立数据段描述符
     489         mov eax,edi
     490         add eax,[edi+0x1c]
     491         mov ebx,[edi+0x20]
     492         dec ebx
     493         mov ecx,0x0040f200
     494         call Sys_Routine_Segement:Make_Seg_Descriptor
     495         mov ebx,esi
     496         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     497         or cx,0x0003
     498         mov [edi+0x1c],cx 
     499         
     500         ;建立栈段描述符
     501         mov ecx,[edi+0x0c]
     502         mov ebx,0x000fffff
     503         sub ebx,ecx
     504         mov eax,4096                    ;4KB粒度
     505         mul ecx
     506         mov ecx,eax
     507         call Sys_Routine_Segement:allocate_memory
     508         mov eax,ecx                     ;eax是栈段的线性基地址
     509         mov ecx,0x00c0f600
     510         call Sys_Routine_Segement:Make_Seg_Descriptor
     511         mov ebx,esi
     512         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     513         or cx,0x0003
     514         mov [edi+0x08],cx
     515         
     516         mov eax,[esi+0x0c]              ;LDT线性基地址
     517         mov [edi+0x32],eax          
     518         mov ax,[esi+0x0a]               ;LDT段界限
     519         mov [edi+0x30],ax           
     520         
     521         ;现在开始重定位API符号表
     522         ;---------------------------------------------------------------------
     523         mov eax,All_4GB_Segment         ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问
     524         mov es,eax
     525         mov eax,Core_Data_Segement
     526         mov ds,eax
     527         
     528         cld
     529         mov ecx,[es:edi+0x36]           ;得到用户程序符号表的条数
     530         add edi,0x3a                    ;用户符号表的偏移地址是0x3a
     531 
     532         _loop_U_SALT:                   
     533             push edi
     534             push ecx
     535             
     536             mov ecx,salt_items_sum
     537             mov esi,salt
     538             
     539             _loop_C_SALT:
     540                 push edi
     541                 push esi
     542                 push ecx
     543                 
     544                 mov ecx,64              ;比较256个字节
     545                 repe cmpsd
     546                 jne _re_match           ;如果成功匹配,那么esi和edi刚好会在数据区之后的
     547                 
     548                 mov eax,[esi]           ;偏移地址
     549                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区
     550                 mov ax,[esi+0x04]       ;段的选择子
     551                 
     552                 or ax,0x0002            ;把RPL改为3,代表(内核)赋予应用程序以特权级3
     553                 mov [es:edi-252],ax     ;把段的选择子填入用户程序的段选择区
     554                 
     555                 _re_match:
     556                 pop ecx
     557                 pop esi
     558                 add esi,salt_length
     559                 pop edi
     560             loop _loop_C_SALT
     561             
     562             pop ecx
     563             pop edi
     564             add edi,256
     565         loop _loop_U_SALT
     566         ;---------------------------------------------------------------------
     567         ;----------------------填入临时中转任务门选择子-----------------------
     568         mov edi,[ebp+11*4]                  
     569         mov edi,[es:edi+0x06]               ;从TCB线性基地址中获得用户程序加载的基地址
     570         
     571         mov ax,[salt_tp]
     572         mov [es:edi+0x28],ax                ;填充任务门选择子
     573         ;---------------------------------------------------------------------
     574         
     575         
     576         mov esi,[ebp+11*4]              ;重新获得TCB的线性基地址
     577                     
     578         mov ecx,Switch_Stack_Size
     579         mov eax,ecx
     580         mov [es:esi+0x1a],ecx
     581         shr dword[es:esi+0x1a],12       ;相当于除以4096
     582         call Sys_Routine_Segement:allocate_memory
     583         add eax,ecx                     ;得到最高地址
     584         mov [es:esi+0x1e],eax           ;登记线性基地址
     585         mov ebx,0x000fffff
     586         sub ebx,[es:esi+0x1a]           
     587         mov ecx,0x00c09600              ;特权级0
     588         call Sys_Routine_Segement:Make_Seg_Descriptor
     589         mov ebx,esi
     590         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     591         or cx,0x0000                    ;RPL为0
     592         mov [es:esi+0x22],cx
     593         mov dword[es:esi+0x24],0
     594                     
     595         mov ecx,Switch_Stack_Size
     596         mov eax,ecx
     597         mov [es:esi+0x28],ecx
     598         shr dword[es:esi+0x28],12       ;相当于除以4096
     599         call Sys_Routine_Segement:allocate_memory
     600         add eax,ecx                     ;得到最高地址
     601         mov [es:esi+0x2c],eax           ;登记线性基地址
     602         mov ebx,0x000fffff
     603         sub ebx,[es:esi+0x28]           
     604         mov ecx,0x00c0b600              ;特权级1
     605         call Sys_Routine_Segement:Make_Seg_Descriptor
     606         mov ebx,esi
     607         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     608         or cx,0x0001                    ;RPL为1
     609         mov [es:esi+0x30],cx
     610         mov dword[es:esi+0x32],0
     611                             
     612         mov ecx,Switch_Stack_Size
     613         mov eax,ecx
     614         mov [es:esi+0x36],ecx
     615         shr dword[es:esi+0x36],12       ;相当于除以4096
     616         call Sys_Routine_Segement:allocate_memory
     617         add eax,ecx                     ;得到最高地址
     618         mov [es:esi+0x3a],eax           ;登记线性基地址
     619         mov ebx,0x000fffff
     620         sub ebx,[es:esi+0x36]           
     621         mov ecx,0x00c0d600              ;特权级2
     622         call Sys_Routine_Segement:Make_Seg_Descriptor
     623         mov ebx,esi
     624         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     625         or cx,0x0002                    ;RPL为2
     626         mov [es:esi+0x3e],cx
     627         mov dword[es:esi+0x40],0
     628         
     629         ;在GDT中存入LDT信息
     630         mov eax,[es:esi+0x0c]
     631         movzx ebx,word[es:esi+0x0a]
     632         mov ecx,0x00408200              ;LDT描述符,特权级0级
     633         call Sys_Routine_Segement:Make_Seg_Descriptor
     634         call Sys_Routine_Segement:Set_New_GDT
     635         mov [es:esi+0x10],cx            ;在TCB放入LDT选择子
     636         
     637         ;-------------------在TCB中登记TSS的信息-------------------------------
     638         mov ecx,104                     ;创建一个最小尺寸的TSS
     639         mov [es:esi+0x12],cx
     640         dec word[es:esi+0x12]           ;记得-1,要的是段界限
     641         call Sys_Routine_Segement:allocate_memory
     642         mov [es:esi+0x14],ecx           ;TSS基地址
     643         
     644         ;构建TSS信息表
     645         mov dword[es:ecx+0x00],0        ;没有前一个任务
     646         
     647         mov edx,[es:esi+0x24]           ;0栈段
     648         mov [es:ecx+4],edx
     649         mov dx,[es:esi+0x22]
     650         mov [es:ecx+8],dx
     651         
     652         mov edx,[es:esi+0x32]           ;1栈段
     653         mov [es:ecx+12],edx
     654         mov dx,[es:esi+0x30]
     655         mov [es:ecx+16],dx
     656         
     657         mov edx,[es:esi+0x40]           ;2栈段
     658         mov [es:ecx+20],edx
     659         mov dx,[es:esi+0x3e]
     660         mov [es:ecx+24],dx
     661         
     662         mov edx,[es:esi+0x10]           ;LDT选择子
     663         mov [es:ecx+96],edx
     664         
     665         mov dx,[es:esi+0x12]            ;I/O偏移
     666         mov [es:ecx+102],dx             ;是102不是104
     667         
     668         mov word[es:ecx+100],0          ;T=0
     669         
     670         mov edi,[es:esi+0x06]           ;用户程序的线性基地址
     671         
     672         mov edx,[es:edi+0x10]           ;EIP
     673         mov [es:ecx+32],edx
     674         
     675         mov edx,[es:edi+0x14]           ;CS
     676         mov [es:ecx+76],dx
     677         
     678         mov edx,[es:edi+0x08]           ;SS
     679         mov [es:ecx+80],dx
     680         
     681         mov edx,[es:edi+0x04]           ;DS(是指向用户头部,而不是用户程序的数据区) 
     682         mov [es:ecx+84],dx
     683         
     684         mov word[es:ecx+72],0           ;ES
     685         mov word[es:ecx+88],0           ;FS
     686         mov word[es:ecx+92],0           ;GS
     687         
     688         pushfd
     689         pop edx
     690         mov [es:ecx+36],edx             ;EFLAGS 
     691         
     692         ;在GDT中存入TSS信息,必须放入GDT,这样才能在当前LDT中加入调用门
     693         mov eax,[es:esi+0x14]
     694         movzx ebx,word[es:esi+0x12]
     695         mov ecx,0x00408900
     696         call Sys_Routine_Segement:Make_Seg_Descriptor
     697         call Sys_Routine_Segement:Set_New_GDT
     698         mov [es:esi+0x18],cx
     699         
     700         ;在当前用户程序的LDT中加入需要加载的程序的任务门,且把任务门的选择子写入fs指向的用户程序头部的相应位置
     701         mov eax,0x00000
     702         mov bx,[es:esi+0x18]
     703         mov cx,0xe500
     704         
     705         call Sys_Routine_Segement:Make_Gate_Descriptor
     706         call Sys_Routine_Segement:Set_Task_Gate_To_UsersLDT
     707         
     708         pop es
     709         pop ds
     710         popad
     711         retf 4                          ;相当于是stdcall,过程清栈
     712     ;---------------------------------------------------------------------
     713     append_to_tcb:                      ;写入新的TCB链
     714                                         ;输入:ecx新的TCB线性基地址
     715         pushad
     716         
     717         push ds
     718         push es
     719         
     720         mov eax,All_4GB_Segment
     721         mov es,eax
     722         
     723         mov eax,Core_Data_Segement
     724         mov ds,eax
     725         
     726         mov eax,[tcb_chain]
     727         cmp eax,0x00
     728         je _notcb
     729         
     730         _search_tcb:
     731             mov edx,[tcb_chain+0x00]
     732             mov eax,[es:edx]
     733             cmp eax,0x00
     734         jne _search_tcb
     735         
     736         mov [es:edx+0x00],ecx
     737         jmp _out_tcb_search
     738         
     739         _notcb:
     740         mov [tcb_chain],ecx
     741         
     742         _out_tcb_search:
     743         pop es
     744         pop ds
     745         
     746         popad
     747         retf
     748     ;---------------------------------------------------------------------
     749     Set_Task_Gate_To_UsersLDT:          ;输入:eax:任务门描述符的低32位
     750                                         ;      edx:任务门描述符的高32位
     751                                         ;输出:无
     752         mov edi,[fs:0x32]               ;LDT基地址
     753         movzx ebx,word[fs:0x30]         ;段界限
     754         
     755         inc bx
     756         
     757         mov [es:edi+ebx+0x00],eax
     758         mov [es:edi+ebx+0x04],edx
     759         
     760         add bx,8
     761         dec bx                          ;记得要段界限
     762         
     763         mov [fs:0x30],bx                ;重新写入
     764         
     765         mov ax,bx
     766         xor dx,dx
     767         mov bx,8
     768         div bx                          ;得到LDT选择位置
     769         
     770         shl ax,3
     771         or ax,0x0004                    ;TI=1,特权级是3
     772         mov [fs:0x2e],ax
     773         
     774         retf                        
     775     ;---------------------------------------------------------------------  
     776 ;=========================================================================
     777 ;===========================内核数据区====================================
     778 ;=========================================================================
     779 SECTION Core_Data align=16 vstart=0
     780 ;-------------------------------------------------------------------------------
     781         pgdt_base_tmp:          dw  0                           ;这一章的用户程序都是从GDT中加载的
     782                                 dd  0
     783 
     784         ram_alloc:              dd  0x00100000                  ;下次分配内存时的起始地址(直接暴力从0x00100000开始分配了)
     785         ram_recycled            dd  0                           ;这里储存程序实际用的大小           
     786         salt:
     787         salt_1:                 db  '@Printf'                   ;@Printf函数(公用例程)
     788         times 256-($-salt_1)    db  0
     789                                 dd  put_string
     790                                 dw  Sys_Routine_Segement
     791                                 dw  0                           ;参数个数
     792                                 
     793         salt_2:                 db  '@ReadHarddisk'             ;@ReadHarddisk函数(公用例程)
     794         times 256-($-salt_2)    db  0
     795                                 dd  ReadHarddisk
     796                                 dw  Sys_Routine_Segement
     797                                 dw  4                           ;参数个数
     798                                 
     799         salt_3:                 db  '@PrintDwordAsHexString'    ;@PrintDwordAsHexString函数(公用例程)
     800         times 256-($-salt_3)    db  0
     801                                 dd  PrintDword
     802                                 dw  Sys_Routine_Segement
     803                                 dw  0                           ;参数个数
     804                                             
     805         salt_4:                 db  '@Fopen'                    ;@Fopen函数(内核例程)
     806         times 256-($-salt_4)    db  0
     807                                 dd  load_program_form_user
     808                                 dw  Sys_Routine_Segement
     809                                 dw  1                           ;参数个数
     810                                 
     811         salt_length:            equ $-salt_4
     812         salt_items_sum          equ ($-salt)/salt_length        ;得到项目总数
     813         
     814         salt_tp:                dw  0                           ;任务门,专门拿来给程序切换到全局空间的
     815         
     816         message_1               db  '   If you seen this message,that means we '
     817                                 db  'are now in protect mode,and the system '
     818                                 db  'core is loaded,and the video display '
     819                                 db  'routine works perfectly.',0x0d,0x0a,0
     820 
     821         message_2               db  '   Loading user program...',0
     822 
     823         do_status               db  'Done.',0x0d,0x0a,0
     824 
     825         message_3               db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
     826                                 db  '   User program terminated,control returned.'
     827                                 db  0x0d,0x0a,0x0d,0x0a,0
     828         message_4               db  '   We have been backed to kernel.',0x0d,0x0a,0
     829         message_5               db  '   The GDT and memory have benn recycled.',0
     830         message_6               db  '   From the system wide gate:',0x0d,0x0a,0
     831         message_7               db  '   Setting the gate discriptor...',0
     832         message_In_Gate         db  '   Hi!My name is Philip:',0x0d,0x0a,0
     833 
     834         bin_hex                 db '0123456789ABCDEF'
     835                                                                 ;put_hex_dword子过程用的查找表
     836         core_buf     times 2048 db 0                            ;内核用的缓冲区(2049个字节(2MB))
     837 
     838         esp_pointer             dd 0                            ;内核用来临时保存自己的栈指针
     839 
     840         cpu_brnd0               db 0x0d,0x0a,'  ',0
     841         cpu_brand      times 52 db 0
     842         cpu_brnd1               db 0x0d,0x0a,0x0d,0x0a,0  
     843         mem_alloc_fail          db  'The Program is too large to load'
     844         core_ss                 dw 0
     845         core_sp                 dd 0
     846         ;程序管理器的任务信息 
     847         prgman_tss              dd  0             ;程序管理器的TSS基地址
     848                                 dw  0             ;程序管理器的TSS描述符选择子 
     849 
     850         prgman_msg1             db  0x0d,0x0a
     851                                 db  '[PROGRAM MANAGER]: Hello! I am Program Manager,'
     852                                 db  'run at CPL=0.Now,create user task and switch '
     853                                 db  'to it by the CALL instruction...',0x0d,0x0a,0
     854                  
     855         prgman_msg2             db  0x0d,0x0a
     856                                 db  '[PROGRAM MANAGER]: I am glad to regain control.'
     857                                 db  'Now,create another user task and switch to '
     858                                 db  'it by the JMP instruction...',0x0d,0x0a,0
     859                  
     860         prgman_msg3             db  0x0d,0x0a
     861                                 db  '[PROGRAM MANAGER]: I am gain control again,',0
     862 
     863         core_msg0               db  0x0d,0x0a
     864                                 db  '[SYSTEM CORE]: Uh...This task initiated with '
     865                                 db  'CALL instruction or an exeception/ interrupt,'
     866                                 db  'should use IRETD instruction to switch back...'
     867                                 db  0x0d,0x0a,0
     868 
     869         core_msg1               db  0x0d,0x0a
     870                                 db  '[SYSTEM CORE]: Uh...This task initiated with '
     871                                 db  'JMP instruction,  should switch to Program '
     872                                 db  'Manager directly by the JMP instruction...'
     873                                 db  0x0d,0x0a,0 
     874         core_stop               db  'HALT...',0
     875          
     876         tcb_chain               dd  0                           ;任务控制块链头指针
     877 ;=========================================================================
     878 ;===========================内核代码区====================================
     879 ;=========================================================================
     880 SECTION Core_Code align=16 vstart=0     
     881     load_program:                       ;输入push1:逻辑扇区号
     882                                         ;    push2: 线性基地址
     883         pushad
     884         push ds
     885         push es
     886         
     887         mov ebp,esp                     ;别忘了把参数传给ebp
     888         
     889         mov eax,Core_Data_Segement
     890         mov ds,eax                      ;切换到内核数据段
     891         
     892         mov eax,All_4GB_Segment
     893         mov es,eax
     894         
     895         mov edi,[ebp+11*4]              ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push
     896         
     897         mov ecx,160
     898         call Sys_Routine_Segement:allocate_memory
     899         mov [es:edi+0x0c],ecx
     900         mov word[es:edi+0x0a],0xffff    ;初始化LDT界限位0xffff
     901 
     902         mov esi,[ebp+12*4]              ;esi必须是逻辑扇区号
     903         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了)
     904         
     905         push esi
     906         push ds
     907         push ebx
     908         push cs
     909         call Sys_Routine_Segement:ReadHarddisk
     910         
     911         mov eax,[core_buf]              ;读取用户程序长度
     912         
     913         mov ebx,eax                     
     914         and ebx,0xfffffe00              ;清空低9位(强制对齐512)
     915         add ebx,512                     
     916         test eax,0x000001ff             
     917         cmovnz eax,ebx                  ;低9位不为0则使用向上取整的结果
     918         
     919         mov ecx,eax                     ;eax是整个程序的向上取整的大小
     920         call Sys_Routine_Segement:allocate_memory   
     921                                         ;先分配内存给整个程序,再分配内存给栈区
     922         mov ebx,ecx                                 
     923         mov [es:edi+0x06],ecx           ;tcb 0x06:程序加载基地址
     924         
     925         xor edx,edx
     926         mov ecx,512                     ;千万不要改掉ebx
     927         div ecx
     928         mov ecx,eax
     929         
     930         mov eax,All_4GB_Segment         ;切换到4GB段区域(平坦模式)
     931         mov ds,eax
     932         
     933         _loop_read_u:
     934             push esi
     935             push ds
     936             push ebx
     937             push cs
     938             call Sys_Routine_Segement:ReadHarddisk  ;esi还是User_Program_Address
     939             inc esi
     940             add ebx,512
     941         loop _loop_read_u
     942         
     943         mov esi,edi                     ;esi: TCB的线性基地址
     944         mov edi,[es:esi+0x06]           ;程序加载的线性基地址
     945         
     946         ;建立头部描述符
     947         mov eax,edi
     948         mov ebx,[edi+0x04]
     949         dec ebx                         ;段界限
     950         mov ecx,0x0040f200
     951         call Sys_Routine_Segement:Make_Seg_Descriptor
     952         mov ebx,esi
     953         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     954         or cx,0x0003                    ;特权级3
     955         mov [es:esi+0x44],cx            ;记得要登记头部的选择子
     956         mov [edi+0x04],cx
     957         
     958         ;建立代码段描述符
     959         mov eax,edi
     960         add eax,[edi+0x14]
     961         mov ebx,[edi+0x18]
     962         dec ebx
     963         mov ecx,0x0040f800
     964         call Sys_Routine_Segement:Make_Seg_Descriptor
     965         mov ebx,esi
     966         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     967         or cx,0x0003
     968         mov [edi+0x14],cx 
     969         
     970         ;建立数据段描述符
     971         mov eax,edi
     972         add eax,[edi+0x1c]
     973         mov ebx,[edi+0x20]
     974         dec ebx
     975         mov ecx,0x0040f200
     976         call Sys_Routine_Segement:Make_Seg_Descriptor
     977         mov ebx,esi
     978         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     979         or cx,0x0003
     980         mov [edi+0x1c],cx 
     981         
     982         ;建立栈段描述符
     983         mov ecx,[edi+0x0c]
     984         mov ebx,0x000fffff
     985         sub ebx,ecx
     986         mov eax,4096                    ;4KB粒度
     987         mul ecx
     988         mov ecx,eax
     989         call Sys_Routine_Segement:allocate_memory
     990         mov eax,ecx                     ;eax是栈段的线性基地址
     991         mov ecx,0x00c0f600
     992         call Sys_Routine_Segement:Make_Seg_Descriptor
     993         mov ebx,esi
     994         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     995         or cx,0x0003
     996         mov [edi+0x08],cx
     997         
     998         mov eax,[esi+0x0c]              ;LDT线性基地址
     999         mov [edi+0x32],eax          
    1000         mov ax,[esi+0x0a]               ;LDT段界限
    1001         mov [edi+0x30],ax           
    1002         
    1003         ;现在开始重定位API符号表
    1004         ;---------------------------------------------------------------------
    1005         mov eax,All_4GB_Segment         ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问
    1006         mov es,eax
    1007         mov eax,Core_Data_Segement
    1008         mov ds,eax
    1009         
    1010         cld
    1011         mov ecx,[es:edi+0x36]           ;得到用户程序符号表的条数
    1012         add edi,0x3a                    ;用户符号表的偏移地址是0x3a
    1013 
    1014         _loop_U_SALT_u:                 
    1015             push edi
    1016             push ecx
    1017             
    1018             mov ecx,salt_items_sum
    1019             mov esi,salt
    1020             
    1021             _loop_C_SALT_u:
    1022                 push edi
    1023                 push esi
    1024                 push ecx
    1025                 
    1026                 mov ecx,64              ;比较256个字节
    1027                 repe cmpsd
    1028                 jne _re_match_u         ;如果成功匹配,那么esi和edi刚好会在数据区之后的
    1029                 
    1030                 mov eax,[esi]           ;偏移地址
    1031                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区
    1032                 mov ax,[esi+0x04]       ;段的选择子
    1033                 
    1034                 or ax,0x0002            ;把RPL改为3,代表(内核)赋予应用程序以特权级3
    1035                 mov [es:edi-252],ax     ;把段的选择子填入用户程序的段选择区
    1036                 
    1037                 _re_match_u:
    1038                 pop ecx
    1039                 pop esi
    1040                 add esi,salt_length
    1041                 pop edi
    1042             loop _loop_C_SALT_u
    1043             
    1044             pop ecx
    1045             pop edi
    1046             add edi,256
    1047         loop _loop_U_SALT_u
    1048         ;---------------------------------------------------------------------
    1049         ;----------------------填入临时中转任务门选择子-----------------------
    1050         mov edi,[ebp+11*4]                  
    1051         mov edi,[es:edi+0x06]               ;从TCB线性基地址中获得用户程序加载的基地址
    1052         
    1053         mov ax,[salt_tp]
    1054         mov [es:edi+0x28],ax                ;填充任务门选择子
    1055         ;---------------------------------------------------------------------
    1056         mov esi,[ebp+11*4]                  ;重新获得TCB的线性基地址
    1057         
    1058         ;现在设置所有的特权级栈段,并且把特权级栈段放到TCB中(为了等一下设置TSS)
    1059         ;设置TSS特权0级栈段(暂存在TCB中)         
    1060         mov ecx,Switch_Stack_Size
    1061         mov eax,ecx
    1062         mov [es:esi+0x1a],ecx
    1063         shr dword[es:esi+0x1a],12       ;相当于除以4096
    1064         call Sys_Routine_Segement:allocate_memory
    1065         add eax,ecx                     ;得到最高地址
    1066         mov [es:esi+0x1e],eax           ;登记线性基地址
    1067         mov ebx,0x000fffff
    1068         sub ebx,[es:esi+0x1a]           
    1069         mov ecx,0x00c09600              ;特权级0
    1070         call Sys_Routine_Segement:Make_Seg_Descriptor
    1071         mov ebx,esi
    1072         call Sys_Routine_Segement:Set_New_LDT_To_TCB
    1073         or cx,0x0000                    ;RPL为0
    1074         mov [es:esi+0x22],cx
    1075         mov dword[es:esi+0x24],0
    1076         
    1077         ;设置TSS特权1级栈段(暂存在TCB中)             
    1078         mov ecx,Switch_Stack_Size
    1079         mov eax,ecx
    1080         mov [es:esi+0x28],ecx
    1081         shr dword[es:esi+0x28],12       ;相当于除以4096
    1082         call Sys_Routine_Segement:allocate_memory
    1083         add eax,ecx                     ;得到最高地址
    1084         mov [es:esi+0x2c],eax           ;登记线性基地址
    1085         mov ebx,0x000fffff
    1086         sub ebx,[es:esi+0x28]           
    1087         mov ecx,0x00c0b600              ;特权级1
    1088         call Sys_Routine_Segement:Make_Seg_Descriptor
    1089         mov ebx,esi
    1090         call Sys_Routine_Segement:Set_New_LDT_To_TCB
    1091         or cx,0x0001                    ;RPL为1
    1092         mov [es:esi+0x30],cx
    1093         mov dword[es:esi+0x32],0
    1094         
    1095         ;设置TSS特权2级栈段(暂存在TCB中)                 
    1096         mov ecx,Switch_Stack_Size
    1097         mov eax,ecx
    1098         mov [es:esi+0x36],ecx
    1099         shr dword[es:esi+0x36],12       ;相当于除以4096
    1100         call Sys_Routine_Segement:allocate_memory
    1101         add eax,ecx                     ;得到最高地址
    1102         mov [es:esi+0x3a],eax           ;登记线性基地址
    1103         mov ebx,0x000fffff
    1104         sub ebx,[es:esi+0x36]           
    1105         mov ecx,0x00c0d600              ;特权级2
    1106         call Sys_Routine_Segement:Make_Seg_Descriptor
    1107         mov ebx,esi
    1108         call Sys_Routine_Segement:Set_New_LDT_To_TCB
    1109         or cx,0x0002                    ;RPL为2
    1110         mov [es:esi+0x3e],cx
    1111         mov dword[es:esi+0x40],0
    1112         
    1113         ;在GDT中存入LDT信息
    1114         mov eax,[es:esi+0x0c]
    1115         movzx ebx,word[es:esi+0x0a]
    1116         mov ecx,0x00408200              ;LDT描述符,特权级0级
    1117         call Sys_Routine_Segement:Make_Seg_Descriptor
    1118         call Sys_Routine_Segement:Set_New_GDT
    1119         mov [es:esi+0x10],cx            ;在TCB放入LDT选择子
    1120         
    1121         ;在TCB中登记TSS的信息
    1122         mov ecx,104                     ;创建一个最小尺寸的TSS
    1123         mov [es:esi+0x12],cx
    1124         dec word[es:esi+0x12]           ;记得-1,要的是段界限
    1125         call Sys_Routine_Segement:allocate_memory
    1126         mov [es:esi+0x14],ecx           ;TSS基地址
    1127         
    1128         ;构建TSS信息表
    1129         mov dword[es:ecx+0x00],0        ;没有前一个任务
    1130         
    1131         mov edx,[es:esi+0x24]           ;0栈段
    1132         mov [es:ecx+4],edx
    1133         mov dx,[es:esi+0x22]
    1134         mov [es:ecx+8],dx
    1135         
    1136         mov edx,[es:esi+0x32]           ;1栈段
    1137         mov [es:ecx+12],edx
    1138         mov dx,[es:esi+0x30]
    1139         mov [es:ecx+16],dx
    1140         
    1141         mov edx,[es:esi+0x40]           ;2栈段
    1142         mov [es:ecx+20],edx
    1143         mov dx,[es:esi+0x3e]
    1144         mov [es:ecx+24],dx
    1145         
    1146         mov edx,[es:esi+0x10]           ;LDT选择子
    1147         mov [es:ecx+96],edx
    1148         
    1149         mov dx,[es:esi+0x12]            ;I/O偏移
    1150         mov [es:ecx+102],dx             ;是102不是104
    1151         
    1152         mov word[es:ecx+100],0          ;T=0
    1153         
    1154         mov edi,[es:esi+0x06]           ;用户程序的线性基地址
    1155         
    1156         mov edx,[es:edi+0x10]           ;EIP
    1157         mov [es:ecx+32],edx
    1158         
    1159         mov edx,[es:edi+0x14]           ;CS
    1160         mov [es:ecx+76],dx
    1161         
    1162         mov edx,[es:edi+0x08]           ;SS
    1163         mov [es:ecx+80],dx
    1164         
    1165         mov edx,[es:edi+0x04]           ;DS(是指向用户头部,而不是用户程序的数据区) 
    1166         mov [es:ecx+84],dx
    1167         
    1168         mov word[es:ecx+72],0           ;ES
    1169         mov word[es:ecx+88],0           ;FS
    1170         mov word[es:ecx+92],0           ;GS
    1171         
    1172         pushfd
    1173         pop edx
    1174         mov [es:ecx+36],edx             ;EFLAGS 
    1175         
    1176         ;在GDT中存入TSS信息
    1177         mov eax,[es:esi+0x14]
    1178         movzx ebx,word[es:esi+0x12]
    1179         mov ecx,0x00408900
    1180         call Sys_Routine_Segement:Make_Seg_Descriptor
    1181         call Sys_Routine_Segement:Set_New_GDT
    1182         mov [es:esi+0x18],cx
    1183         
    1184         pop es
    1185         pop ds
    1186         popad
    1187         ret 8                           ;相当于是stdcall,过程清栈
    1188         ;---------------------------------------------------------------------
    1189     start:
    1190         mov eax,Core_Data_Segement
    1191         mov ds,eax
    1192         
    1193         mov ebx,message_1
    1194         call Sys_Routine_Segement:put_string
    1195 
    1196         _@load:
    1197         mov ebx,message_7
    1198         call Sys_Routine_Segement:put_string
    1199         ;----------------------------安装门------------------------------------
    1200         mov edi,salt
    1201         mov ecx,salt_items_sum
    1202         _set_gate:
    1203             push ecx
    1204             mov eax,[edi+256]
    1205             mov bx,[edi+260]        ;选择子
    1206             mov cx,0xec00           ;门是特权级是3的门,那么任何程序都能调用
    1207             or cx,[edi+262]         ;加上参数个数
    1208             
    1209             call Sys_Routine_Segement:Make_Gate_Descriptor
    1210             call Sys_Routine_Segement:Set_New_GDT
    1211             mov [edi+260],cx        ;回填选择子
    1212             add edi,salt_length
    1213             pop ecx
    1214         loop _set_gate
    1215         ;----------------------------------------------------------------------
    1216         mov ebx,do_status
    1217         call far [salt_1+256]
    1218         mov ebx,message_6
    1219         call far [salt_1+256]
    1220         mov ebx,message_In_Gate
    1221         call far [salt_1+256]           ;调用门显示字符信息(忽略偏移地址(前4字节))
    1222         
    1223         mov ebx,message_4
    1224         call far [salt_1+256]
    1225         mov ebx,message_2
    1226         call far [salt_1+256]
    1227         
    1228         mov eax,All_4GB_Segment
    1229         mov es,eax
    1230         
    1231         mov ecx,104
    1232         call Sys_Routine_Segement:allocate_memory
    1233         mov [prgman_tss],ecx            ;保留线性基地址
    1234         
    1235         mov word[es:ecx+100],0          ;TI=0
    1236         mov word[es:ecx+96],0           ;任务允许没有自己的LDT
    1237         mov dword[es:ecx+28],0          ;设置CR3  
    1238         mov word[es:ecx+0],0            ;没有前一个任务
    1239         mov word[es:ecx+102],103        ;任务管理器不需要I/O映射,要大于等于界限
    1240         
    1241         mov eax,ecx
    1242         mov ebx,103                     ;TSS段界限
    1243         mov ecx,0x00408900
    1244         call Sys_Routine_Segement:Make_Seg_Descriptor
    1245         call Sys_Routine_Segement:Set_New_GDT
    1246         mov [prgman_tss+0x04],cx
    1247         
    1248         ltr cx                          ;启动任务
    1249         ;------------------安装用户管理程序的临时返回任务门--------------------     
    1250         mov eax,0x0000                  ;TSS不需要偏移地址
    1251         mov bx,[prgman_tss+0x04]        ;TSS的选择子
    1252         mov cx,0xe500
    1253         
    1254         call Sys_Routine_Segement:Make_Gate_Descriptor      
    1255         call Sys_Routine_Segement:Set_New_GDT
    1256         mov [salt_tp],cx                ;填入临时中转任务门选择子,注意不需要加260了
    1257         ;----------------------------------------------------------------------
    1258         mov ebx,prgman_msg1
    1259         call Sys_Routine_Segement:put_string
    1260         
    1261         
    1262         ;----------------------用户管理程序----------------------------
    1263         mov ecx,0x46                    ;TCB链大小
    1264         call Sys_Routine_Segement:allocate_memory
    1265         call Sys_Routine_Segement:append_to_tcb
    1266         
    1267         push User_Program_AddressA
    1268         push ecx
    1269         call load_program
    1270         
    1271         jmp far [es:ecx+0x14]           ;打开程序A,跳转方式打开
    1272         
    1273         mov eax,Core_Data_Segement
    1274         mov ds,eax
    1275         
    1276         mov ebx,prgman_msg3
    1277         call Sys_Routine_Segement:put_string
    1278         mov ebx,core_stop
    1279         call Sys_Routine_Segement:put_string
    1280         
    1281         cli
    1282         hlt
    1283         ;----------------------------------------------------------------------
    1284 ;=========================================================================
    1285 SECTION core_trail
    1286 ;----------------------------------------------------------------
    1287 Program_end:

    用户程序改动挺大的。

     1     ;==============================用户程序A=======================================
     2 SECTION header vstart=0
     3 
     4         program_length      dd program_end              ;程序总长度#0x00
     5          
     6         head_len            dd header_end               ;程序头部的长度#0x04
     7 
     8         stack_seg           dd 0                        ;用于接收堆栈段选择子#0x08
     9         stack_len           dd 1                        ;程序建议的堆栈大小#0x0c
    10                                                         ;以4KB为单位
    11                                                   
    12         prgentry            dd start                    ;程序入口#0x10 
    13         code_seg            dd section.code.start       ;代码段位置#0x14
    14         code_len            dd code_end                 ;代码段长度#0x18
    15 
    16         data_seg            dd section.data.start       ;数据段位置#0x1c
    17         data_len            dd data_end                 ;数据段长度#0x20
    18 ;-------------------------------------------------------------------------------
    19         TpBack:             dd 0                        ;管理程序任务门的偏移地址没用,直接填充就可以了
    20                                                         ;#0x24
    21                             dw 0                        ;管理程序任务门的选择子
    22                                                         ;#0x28  
    23                             
    24         LoadPos:            dd 0                        ;用户程序想加载程序的位置,直接填充就可以了,任务门用不到
    25                                                         ;#0x2a
    26                             dw 0                        ;另一个用户程序任务门的选择子
    27                                                         ;#0x2e
    28                             
    29         Users_LDT_Add       dw 0                        ;用户程序自己的LDT段界限
    30                                                         ;#0x30
    31                             dd 0                        ;用户程序自己的LDT基地址
    32                                                         ;#0x32
    33 ;-------------------------------------------------------------------------------
    34         ;符号地址检索表
    35         salt_items          dd (u_salt_end-salt)/256    ;#0x36
    36          
    37         salt:                                           ;#0x3a
    38         Printf:             db  '@Printf'
    39                      times 256-($-Printf) db 0
    40                      
    41         TerminateProgram:   db  '@TerminateProgram'
    42                      times 256-($-TerminateProgram) db 0
    43                      
    44         ReadHarddisk:       db  '@ReadHarddisk'
    45                      times 256-($-ReadHarddisk) db 0
    46                      
    47         Fopen:              db  '@Fopen'
    48                      times 256-($-Fopen) db 0    
    49                  
    50 u_salt_end:                                         
    51 header_end: 
    52 ;===============================================================================
    53 SECTION data align=16 vstart=0    
    54                          
    55         message_1       db  0x0d,0x0a
    56                         db  '[USER TASKA]: Hi! I am task PhilipA',0x0d,0x0a,0
    57                           
    58         message_2       db  '[USER TASKA]: Now I will load PhilipB',0x0d,0x0a,0
    59 data_end:
    60 ;===============================================================================
    61       [bits 32]
    62 ;===============================================================================
    63 SECTION code align=16 vstart=0
    64 start:
    65         User_Program_AddressB   equ 80          ;用户程序所在逻辑扇区
    66         mov eax,ds
    67         mov fs,eax
    68      
    69         mov eax,[data_seg]
    70         mov ds,eax
    71      
    72         mov ebx,message_1
    73         call far [fs:Printf]
    74         
    75         mov ebx,message_2
    76         call far [fs:Printf]
    77         
    78         push User_Program_AddressB
    79         call far [fs:Fopen]
    80         
    81         jmp far [fs:LoadPos]                 ;直接加载想要加载的用户程序B
    82         
    83         jmp far [fs:TpBack]                  ;回到任务管理程序
    84 code_end:
    85 
    86 ;===============================================================================
    87 SECTION trail
    88 ;-------------------------------------------------------------------------------
    89 program_end:
     1 ;==============================用户程序B=======================================
     2 SECTION header vstart=0
     3 
     4         program_length      dd program_end              ;程序总长度#0x00
     5          
     6         head_len            dd header_end               ;程序头部的长度#0x04
     7 
     8         stack_seg           dd 0                        ;用于接收堆栈段选择子#0x08
     9         stack_len           dd 1                        ;程序建议的堆栈大小#0x0c
    10                                                         ;以4KB为单位
    11                                                   
    12         prgentry            dd start                    ;程序入口#0x10 
    13         code_seg            dd section.code.start       ;代码段位置#0x14
    14         code_len            dd code_end                 ;代码段长度#0x18
    15 
    16         data_seg            dd section.data.start       ;数据段位置#0x1c
    17         data_len            dd data_end                 ;数据段长度#0x20
    18 ;-------------------------------------------------------------------------------
    19         TpBack:             dd 0                        ;管理程序任务门的偏移地址没用,直接填充就可以了
    20                                                         ;#0x24
    21                             dw 0                        ;管理程序任务门的选择子
    22                                                         ;#0x28  
    23                             
    24         LoadPos:            dd 0                        ;用户程序想加载程序的位置,直接填充就可以了,任务门用不到
    25                                                         ;#0x2a
    26                             dw 0                        ;另一个用户程序任务门的选择子
    27                                                         ;#0x2e
    28                             
    29         Users_LDT_Add       dw 0                        ;用户程序自己的LDT段界限
    30                                                         ;#0x30
    31                             dd 0                        ;用户程序自己的LDT基地址
    32                                                         ;#0x32
    33 ;-------------------------------------------------------------------------------
    34         ;符号地址检索表
    35         salt_items          dd (u_salt_end-salt)/256    ;#0x36
    36          
    37         salt:                                           ;#0x3a
    38         Printf:             db  '@Printf'
    39                      times 256-($-Printf) db 0
    40                      
    41         TerminateProgram:   db  '@TerminateProgram'
    42                      times 256-($-TerminateProgram) db 0
    43                      
    44         ReadHarddisk:       db  '@ReadHarddisk'
    45                      times 256-($-ReadHarddisk) db 0
    46                      
    47         Fopen:              db  '@Fopen'
    48                      times 256-($-Fopen) db 0    
    49 u_salt_end:                                         
    50 header_end: 
    51 ;===============================================================================
    52 SECTION data align=16 vstart=0    
    53                          
    54         message_1       db  '[USER TASKB]: Hi! I am PhilipB',0x0d,0x0a
    55                         db  '[USER TASKB]: Now I will back to program manager',0x0d,0x0a,0
    56 data_end:
    57 
    58 ;===============================================================================
    59       [bits 32]
    60 ;===============================================================================
    61 SECTION code align=16 vstart=0
    62 start:
    63         mov eax,ds
    64         mov fs,eax
    65      
    66         mov eax,[data_seg]
    67         mov ds,eax
    68      
    69         mov ebx,message_1
    70         call far [fs:Printf]
    71         jmp far [fs:TpBack]                  ;直接回任务管理程序
    72 code_end:
    73 
    74 ;===============================================================================
    75 SECTION trail
    76 ;-------------------------------------------------------------------------------
    77 program_end:

    4. 习题3

      习题三本来是书上没有的,但是我自己加上去了,其实我还是对任务门的机制很感兴趣的,所以我把第一题写了一个新的。

       1 ;===============================内核程序=================================
       2         ;定义内核所要用到的选择子
       3         All_4GB_Segment         equ 0x0008      ;4GB的全内存区域
       4         Stack_Segement          equ 0x0018      ;内核栈区
       5         Print_Segement          equ 0x0020      ;显存映射区
       6         Sys_Routine_Segement    equ 0x0028      ;公用例程段
       7         Core_Data_Segement      equ 0x0030      ;内核数据区
       8         Core_Code_Segement      equ 0x0038      ;内核代码段
       9         ;----------------------------------------------------------------
      10         User_Program_AddressA   equ 50          ;用户程序所在逻辑扇区
      11         User_Program_AddressB   equ 80          ;用户程序所在逻辑扇区
      12         Switch_Stack_Size       equ 4096        ;切换栈段的大小
      13 ;=============================内核程序头部===============================
      14 SECTION header vstart=0
      15         Program_Length          dd  Program_end                 ;内核总长度
      16         Sys_Routine_Seg         dd  section.Sys_Routine.start   ;公用例程段线性地址
      17         Core_Data_Seg           dd  section.Core_Data.start     ;内核数据区线性地址
      18         Core_Code_Seg           dd  section.Core_Code.start     ;内核代码区线性地址
      19         Code_Entry              dd  start                       ;注意偏移地址一定是32位的
      20                                 dw  Core_Code_Segement
      21     ;----------------------------------------------------------------
      22                             [bits 32]
      23 ;=========================================================================
      24 ;============================公用例程区===================================
      25 ;=========================================================================
      26 SECTION Sys_Routine align=16 vstart=0
      27     ReadHarddisk:                           ;push1:28位磁盘号(esi)
      28                                             ;push2:应用程序数据段选择子(ax->ds)
      29                                             ;push3: 偏移地址(ebx)
      30                                             ;push4: 应用程序代码段选择子(dx)
      31         pushad
      32         push ds
      33         push es
      34         
      35         mov ebp,esp
      36         
      37         mov esi,[ebp+15*4]
      38         movzx eax,word[ebp+14*4]
      39         mov ebx,[ebp+13*4]
      40         movzx edx,word[ebp+12*4]
      41         
      42         arpl ax,dx
      43         mov ds,ax
      44         
      45         mov dx,0x1f2
      46         mov al,0x01     ;读一个扇区                                
      47         out dx,al
      48         
      49         inc edx         ;0-7位
      50         mov eax,esi
      51         out dx,al
      52         
      53         inc edx         ;8-15位
      54         mov al,ah
      55         out dx,al
      56         
      57         inc edx         ;16-23位
      58         shr eax,16
      59         out dx,al
      60         
      61         inc edx         ;24-28位,主硬盘,LBA模式
      62         mov al,ah
      63         and al,0x0f
      64         or al,0xe0
      65         out dx,al
      66         
      67         inc edx
      68         mov al,0x20
      69         out dx,al
      70         
      71         _wait:
      72             in al,dx
      73             and al,0x88
      74             cmp al,0x08
      75             jne _wait
      76         
      77         mov dx,0x1f0
      78         mov ecx,256
      79         
      80         _read:
      81             in ax,dx
      82             mov [ebx],ax
      83             add ebx,2
      84             loop _read
      85         
      86         pop es
      87         pop ds
      88         popad
      89         retf 16     ;4个数据
      90     ;----------------------------------------------------------------
      91     put_string:                                                 ;ebx:偏移地址
      92         pushad
      93         push ds
      94         push es
      95         
      96         _print:
      97             mov cl,[ebx]
      98             cmp cl,0
      99             je _exit
     100             call put_char
     101             inc ebx
     102             jmp _print
     103         _exit:
     104             pop es
     105             pop ds
     106             popad
     107             retf            ;段间返回
     108         ;-------------------------------------------------------------- 
     109         put_char:           ;cl就是要显示的字符
     110             push ebx
     111             push es
     112             push ds
     113             
     114             mov dx,0x3d4
     115             mov al,0x0e     ;高8位
     116             out dx,al
     117             mov dx,0x3d5
     118             in al,dx
     119             mov ah,al       ;先把高8位存起来
     120             mov dx,0x3d4
     121             mov al,0x0f     ;低8位
     122             out dx,al
     123             mov dx,0x3d5
     124             in al,dx        ;现在ax就是当前光标的位置
     125             
     126             _judge:
     127                 cmp cl,0x0a
     128                 je _set_0x0a
     129                 cmp cl,0x0d
     130                 je _set_0x0d
     131             _print_visible:
     132                 mov bx,ax
     133                 mov eax,Print_Segement
     134                 mov es,eax
     135                 shl bx,1    ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍
     136                 mov [es:bx],cl          ;注意这里是屏幕!
     137                 mov byte[es:bx+1],0x07      
     138                 add bx,2
     139                 shr bx,1
     140                 jmp _roll_screen
     141             _set_0x0d:      ;回车
     142                 mov bl,80
     143                 div bl
     144                 mul bl
     145                 mov bx,ax
     146                 jmp _set_cursor
     147             _set_0x0a:      ;换行
     148                 mov bx,ax
     149                 add bx,80
     150                 jmp _roll_screen
     151             _roll_screen:
     152                 cmp bx,2000
     153                 jl _set_cursor
     154                 mov eax,Print_Segement
     155                 mov ds,eax
     156                 mov es,eax
     157                 
     158                 cld
     159                 mov edi,0x00
     160                 mov esi,0xa0
     161                 mov ecx,1920
     162                 rep movsw
     163             _cls:
     164                 mov bx,3840
     165                 mov ecx,80
     166                 _print_blank:
     167                     mov word[es:bx],0x0720
     168                     add bx,2
     169                     loop _print_blank   
     170                 mov bx,1920 ;别总是忘了光标的位置!
     171             _set_cursor:        ;改变后的光标位置在bx上
     172             mov dx,0x3d4
     173             mov al,0x0f     ;低8位
     174             out dx,al
     175             
     176             mov al,bl
     177             mov dx,0x3d5
     178             out dx,al
     179             
     180             mov dx,0x3d4
     181             mov al,0x0e     ;高8位
     182             out dx,al
     183             
     184             mov al,bh
     185             mov dx,0x3d5
     186             out dx,al
     187             
     188             pop ds
     189             pop es
     190             pop ebx
     191             ret
     192     ;----------------------------------------------------------------       
     193     allocate_memory:                            ;简易内存分配策略
     194                                                 ;输入ecx:想要分配的总字节数
     195                                                 ;输出ecx:分配的线性基地址
     196         push ds
     197         push eax
     198         push ebx
     199         call Cal_User_Mem
     200             
     201         mov eax,Core_Data_Segement
     202         mov ds,eax
     203         mov eax,[ram_alloc]
     204         mov edx,eax                             ;edx暂存一下eax
     205         add eax,ecx
     206         
     207         cmp eax,edx                             ;发现新分配的现地址比原来的还小,说明已经溢出
     208         jge _alloc
     209             mov ebx,mem_alloc_fail
     210             call Sys_Routine_Segement:put_string
     211             mov ecx,0                       ;分配为0说明已经分配失败
     212             jmp _exit1
     213         _alloc:
     214             
     215         mov ebx,eax
     216         and ebx,0xfffffffc
     217         add ebx,4                           ;强行向上取整
     218         test eax,0x00000003
     219         cmovnz eax,ebx
     220         mov ecx,[ram_alloc]                 ;要返回要分配的初始地址
     221         mov [ram_alloc],eax                 ;下一次分配的线性基地址
     222             
     223         _exit1:
     224         pop ebx
     225         pop eax
     226         pop ds
     227         
     228         retf
     229     ;----------------------------------------------------------------
     230     recycled_memory_and_gdt:
     231         mov eax,[ram_recycled]
     232         sub [ram_alloc],eax
     233         mov dword[ram_recycled],0               ;因为我们还没学到多任务,先这样简单地清零
     234         
     235         sgdt [pgdt_base_tmp]
     236         sub word[pgdt_base_tmp],16              ;应用程序的LDT,TSS
     237         lgdt [pgdt_base_tmp]                    ;重新加载内核
     238         retf
     239     ;----------------------------------------------------------------
     240     Cal_User_Mem:                               ;输入ecx:应用程序用到的内存(字节)
     241         add [ram_recycled],ecx
     242         ret
     243     ;----------------------------------------------------------------
     244     Make_Seg_Descriptor:                        ;构造段描述符
     245                                             ;输入:
     246                                             ;eax:线性基地址
     247                                             ;ebx:段界限
     248                                             ;ecx:属性
     249                                             ;输出:
     250                                             ;eax:段描述符低32位
     251                                             ;edx:段描述符高32位
     252         mov edx,eax
     253         and edx,0xffff0000
     254         rol edx,8
     255         bswap edx
     256         or edx,ecx
     257         
     258         shl eax,16
     259         or ax,bx
     260         and ebx,0x000f0000
     261         or edx,ebx
     262         retf                
     263     ;----------------------------------------------------------------       
     264     Make_Gate_Descriptor:                   ;构造门描述符
     265                                             ;输入:
     266                                             ;eax:段内偏移地址
     267                                             ;bx: 段的选择子
     268                                             ;cx: 段的属性
     269                                             ;输出:
     270                                             ;eax:门描述符低32位
     271                                             ;edx:门描述符高32位
     272         push ebx
     273         push ecx
     274         
     275         mov edx,eax
     276         and edx,0xffff0000                  ;要高16位
     277         or dx,cx
     278         
     279         shl ebx,16
     280         and eax,0x0000ffff
     281         or eax,ebx
     282         
     283         pop ecx
     284         pop ebx
     285         
     286         retf                
     287     ;----------------------------------------------------------------
     288     Set_New_GDT:                            ;装载新的全局描述符
     289                                             ;输入:edx:eax描述符
     290                                             ;输出:cx选择子
     291         push ds
     292         push es
     293         
     294         mov ebx,Core_Data_Segement
     295         mov ds,ebx
     296         
     297         mov ebx,All_4GB_Segment
     298         mov es,ebx
     299         
     300         sgdt [pgdt_base_tmp]
     301         
     302         movzx ebx,word[pgdt_base_tmp]
     303         inc bx                              ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的
     304                                             ;要用到回绕特性
     305         add ebx,[pgdt_base_tmp+0x02]        ;得到pgdt的线性基地址
     306         
     307         mov [es:ebx],eax
     308         mov [es:ebx+0x04],edx               ;装载新的gdt符
     309                                             ;装载描述符要装载到实际位置上
     310         
     311         add word[pgdt_base_tmp],8           ;给gdt的段界限加上8(字节)
     312         
     313         lgdt [pgdt_base_tmp]                ;加载gdt到gdtr的位置和实际表的位置无关
     314         
     315         mov ax,[pgdt_base_tmp]              ;得到段界限
     316         xor dx,dx
     317         mov bx,8                            ;得到gdt大小
     318         div bx
     319         mov cx,ax
     320         shl cx,3                            ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级)
     321         
     322         pop es
     323         pop ds
     324         retf
     325     ;----------------------------------------------------------------
     326     Set_New_LDT_To_TCB:                     ;装载新的局部描述符
     327                                             ;输入:edx:eax描述符
     328                                             ;    : ebx:TCB线性基地址
     329                                             ;输出:cx选择子
     330         
     331         push edi
     332         push eax
     333         push ebx
     334         push edx
     335         push ds
     336         
     337         mov ecx,All_4GB_Segment
     338         mov ds,ecx
     339         
     340         mov edi,[ebx+0x0c]                  ;LDT的线性基地址
     341         movzx ecx,word[ebx+0x0a]
     342         inc cx                              ;得到实际的LDT的大小(界限还要-1)
     343         
     344         mov [edi+ecx+0x00],eax
     345         mov [edi+ecx+0x04],edx
     346         
     347         add cx,8
     348         dec cx
     349         
     350         mov [ebx+0x0a],cx
     351         
     352         mov ax,cx
     353         xor dx,dx
     354         mov cx,8
     355         div cx
     356         
     357         shl ax,3
     358         mov cx,ax
     359         or cx,0x0004                        ;LDT,第三位TI位一定是1
     360         
     361         pop ds
     362         pop edx
     363         pop ebx
     364         pop eax
     365         pop edi
     366         retf
     367     ;----------------------------------------------------------------   
     368     PrintDword:                             ;显示edx内容的一个调试函数
     369         pushad
     370         push ds
     371         
     372         mov eax,Core_Data_Segement
     373         mov ds,eax
     374         
     375         mov ebx,bin_hex
     376         mov ecx,8
     377         
     378         _query:
     379             rol edx,4
     380             mov eax,edx
     381             and eax,0x0000000f
     382             xlat
     383             
     384             push ecx
     385             mov cl,al
     386             call put_char
     387             pop ecx
     388             
     389         loop _query
     390             
     391         pop ds
     392         popad
     393         
     394         retf
     395 ;=========================================================================
     396 ;===========================内核数据区====================================
     397 ;=========================================================================
     398 SECTION Core_Data align=16 vstart=0
     399 ;-------------------------------------------------------------------------------
     400         pgdt_base_tmp:          dw  0                           ;这一章的用户程序都是从GDT中加载的
     401                                 dd  0
     402 
     403         ram_alloc:              dd  0x00100000                  ;下次分配内存时的起始地址(直接暴力从0x00100000开始分配了)
     404         ram_recycled            dd  0                           ;这里储存程序实际用的大小           
     405         salt:
     406         salt_1:                 db  '@Printf'                   ;@Printf函数(公用例程)
     407         times 256-($-salt_1)    db  0
     408                                 dd  put_string
     409                                 dw  Sys_Routine_Segement
     410                                 dw  0                           ;参数个数
     411                                 
     412         salt_2:                 db  '@ReadHarddisk'             ;@ReadHarddisk函数(公用例程)
     413         times 256-($-salt_2)    db  0
     414                                 dd  ReadHarddisk
     415                                 dw  Sys_Routine_Segement
     416                                 dw  4                           ;参数个数
     417                                 
     418         salt_3:                 db  '@PrintDwordAsHexString'    ;@PrintDwordAsHexString函数(公用例程)
     419         times 256-($-salt_3)    db  0
     420                                 dd  PrintDword
     421                                 dw  Sys_Routine_Segement
     422                                 dw  0                           ;参数个数
     423                                 
     424         salt_length:            equ $-salt_3
     425         salt_items_sum          equ ($-salt)/salt_length        ;得到项目总数
     426         
     427         salt_tp:                dw  0                           ;任务门,专门拿来给程序切换到全局空间的
     428         
     429         message_1               db  '   If you seen this message,that means we '
     430                                 db  'are now in protect mode,and the system '
     431                                 db  'core is loaded,and the video display '
     432                                 db  'routine works perfectly.',0x0d,0x0a,0
     433 
     434         message_2               db  '   Loading user program...',0
     435 
     436         do_status               db  'Done.',0x0d,0x0a,0
     437 
     438         message_3               db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
     439                                 db  '   User program terminated,control returned.'
     440                                 db  0x0d,0x0a,0x0d,0x0a,0
     441         message_4               db  '   We have been backed to kernel.',0x0d,0x0a,0
     442         message_5               db  '   The GDT and memory have benn recycled.',0
     443         message_6               db  '   From the system wide gate:',0x0d,0x0a,0
     444         message_7               db  '   Setting the gate discriptor...',0
     445         message_In_Gate         db  '   Hi!My name is Philip:',0x0d,0x0a,0
     446 
     447         bin_hex                 db '0123456789ABCDEF'
     448                                                                 ;put_hex_dword子过程用的查找表
     449         core_buf     times 2048 db 0                            ;内核用的缓冲区(2049个字节(2MB))
     450 
     451         esp_pointer             dd 0                            ;内核用来临时保存自己的栈指针
     452 
     453         cpu_brnd0               db 0x0d,0x0a,'  ',0
     454         cpu_brand      times 52 db 0
     455         cpu_brnd1               db 0x0d,0x0a,0x0d,0x0a,0  
     456         mem_alloc_fail          db  'The Program is too large to load'
     457         core_ss                 dw 0
     458         core_sp                 dd 0
     459         ;程序管理器的任务信息 
     460         prgman_tss              dd  0             ;程序管理器的TSS基地址
     461                                 dw  0             ;程序管理器的TSS描述符选择子 
     462 
     463         prgman_msg1             db  0x0d,0x0a
     464                                 db  '[PROGRAM MANAGER]: Hello! I am Program Manager,'
     465                                 db  'run at CPL=0.Now,create user task and switch '
     466                                 db  'to it by the CALL instruction...',0x0d,0x0a,0
     467                  
     468         prgman_msg2             db  0x0d,0x0a
     469                                 db  '[PROGRAM MANAGER]: I am glad to regain control.'
     470                                 db  'Now,create another user task and switch to '
     471                                 db  'it by the JMP instruction...',0x0d,0x0a,0
     472                  
     473         prgman_msg3             db  0x0d,0x0a
     474                                 db  '[PROGRAM MANAGER]: I am gain control again,',0
     475 
     476         core_msg0               db  0x0d,0x0a
     477                                 db  '[SYSTEM CORE]: Uh...This task initiated with '
     478                                 db  'CALL instruction or an exeception/ interrupt,'
     479                                 db  'should use IRETD instruction to switch back...'
     480                                 db  0x0d,0x0a,0
     481 
     482         core_msg1               db  0x0d,0x0a
     483                                 db  '[SYSTEM CORE]: Uh...This task initiated with '
     484                                 db  'JMP instruction,  should switch to Program '
     485                                 db  'Manager directly by the JMP instruction...'
     486                                 db  0x0d,0x0a,0 
     487         core_stop               db  'HALT...',0
     488          
     489         tcb_chain               dd  0                           ;任务控制块链头指针
     490 ;=========================================================================
     491 ;===========================内核代码区====================================
     492 ;=========================================================================
     493 SECTION Core_Code align=16 vstart=0     
     494     ;---------------------------------------------------------------------
     495     append_to_tcb:                      ;写入新的TCB链
     496                                         ;输入:ecx新的TCB线性基地址
     497         pushad
     498         
     499         push ds
     500         push es
     501         
     502         mov eax,All_4GB_Segment
     503         mov es,eax
     504         
     505         mov eax,Core_Data_Segement
     506         mov ds,eax
     507         
     508         mov eax,[tcb_chain]
     509         cmp eax,0x00
     510         je _notcb
     511         
     512         _search_tcb:
     513             mov edx,[tcb_chain+0x00]
     514             mov eax,[es:edx]
     515             cmp eax,0x00
     516         jne _search_tcb
     517         
     518         mov [es:edx+0x00],ecx
     519         jmp _out_tcb_search
     520         
     521         _notcb:
     522         mov [tcb_chain],ecx
     523         
     524         _out_tcb_search:
     525         pop es
     526         pop ds
     527         
     528         popad
     529         ret
     530     ;---------------------------------------------------------------------  
     531     load_program:                       ;输入push1:逻辑扇区号
     532                                         ;    push2: 线性基地址
     533         pushad
     534         push ds
     535         push es
     536         
     537         mov ebp,esp                     ;别忘了把参数传给ebp
     538         
     539         mov eax,Core_Data_Segement
     540         mov ds,eax                      ;切换到内核数据段
     541         
     542         mov eax,All_4GB_Segment
     543         mov es,eax
     544         
     545         mov edi,[ebp+11*4]              ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push
     546         
     547         mov ecx,160
     548         call Sys_Routine_Segement:allocate_memory
     549         mov [es:edi+0x0c],ecx
     550         mov word[es:edi+0x0a],0xffff    ;初始化LDT界限位0xffff
     551 
     552         mov esi,[ebp+12*4]              ;esi必须是逻辑扇区号
     553         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了)
     554         
     555         push esi
     556         push ds
     557         push ebx
     558         push cs
     559         call Sys_Routine_Segement:ReadHarddisk
     560         
     561         mov eax,[core_buf]              ;读取用户程序长度
     562         
     563         mov ebx,eax                     
     564         and ebx,0xfffffe00              ;清空低9位(强制对齐512)
     565         add ebx,512                     
     566         test eax,0x000001ff             
     567         cmovnz eax,ebx                  ;低9位不为0则使用向上取整的结果
     568         
     569         mov ecx,eax                     ;eax是整个程序的向上取整的大小
     570         call Sys_Routine_Segement:allocate_memory   
     571                                         ;先分配内存给整个程序,再分配内存给栈区
     572         mov ebx,ecx                                 
     573         mov [es:edi+0x06],ecx           ;tcb 0x06:程序加载基地址
     574         
     575         xor edx,edx
     576         mov ecx,512                     ;千万不要改掉ebx
     577         div ecx
     578         mov ecx,eax
     579         
     580         mov eax,All_4GB_Segment         ;切换到4GB段区域(平坦模式)
     581         mov ds,eax
     582         
     583         _loop_read:
     584             push esi
     585             push ds
     586             push ebx
     587             push cs
     588             call Sys_Routine_Segement:ReadHarddisk  ;esi还是User_Program_Address
     589             inc esi
     590             add ebx,512
     591         loop _loop_read
     592         
     593         mov esi,edi                     ;esi: TCB的线性基地址
     594         mov edi,[es:esi+0x06]           ;程序加载的线性基地址
     595         
     596         ;建立头部描述符
     597         mov eax,edi
     598         mov ebx,[edi+0x04]
     599         dec ebx                         ;段界限
     600         mov ecx,0x0040f200
     601         call Sys_Routine_Segement:Make_Seg_Descriptor
     602         mov ebx,esi
     603         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     604         or cx,0x0003                    ;特权级3
     605         mov [es:esi+0x44],cx            ;记得要登记头部的选择子
     606         mov [edi+0x04],cx
     607         
     608         ;建立代码段描述符
     609         mov eax,edi
     610         add eax,[edi+0x14]
     611         mov ebx,[edi+0x18]
     612         dec ebx
     613         mov ecx,0x0040f800
     614         call Sys_Routine_Segement:Make_Seg_Descriptor
     615         mov ebx,esi
     616         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     617         or cx,0x0003
     618         mov [edi+0x14],cx 
     619         
     620         ;建立数据段描述符
     621         mov eax,edi
     622         add eax,[edi+0x1c]
     623         mov ebx,[edi+0x20]
     624         dec ebx
     625         mov ecx,0x0040f200
     626         call Sys_Routine_Segement:Make_Seg_Descriptor
     627         mov ebx,esi
     628         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     629         or cx,0x0003
     630         mov [edi+0x1c],cx 
     631         
     632         ;建立栈段描述符
     633         mov ecx,[edi+0x0c]
     634         mov ebx,0x000fffff
     635         sub ebx,ecx
     636         mov eax,4096                    ;4KB粒度
     637         mul ecx
     638         mov ecx,eax
     639         call Sys_Routine_Segement:allocate_memory
     640         mov eax,ecx                     ;eax是栈段的线性基地址
     641         mov ecx,0x00c0f600
     642         call Sys_Routine_Segement:Make_Seg_Descriptor
     643         mov ebx,esi
     644         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     645         or cx,0x0003
     646         mov [edi+0x08],cx
     647         
     648         ;现在开始重定位API符号表
     649         ;---------------------------------------------------------------------
     650         mov eax,All_4GB_Segment         ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问
     651         mov es,eax
     652         mov eax,Core_Data_Segement
     653         mov ds,eax
     654         
     655         cld
     656         mov ecx,[es:edi+0x24]           ;得到用户程序符号表的条数
     657         add edi,0x28                    ;用户符号表的偏移地址是0x28
     658 
     659         _loop_U_SALT:                   
     660             push edi
     661             push ecx
     662             
     663             mov ecx,salt_items_sum
     664             mov esi,salt
     665             
     666             _loop_C_SALT:
     667                 push edi
     668                 push esi
     669                 push ecx
     670                 
     671                 mov ecx,64              ;比较256个字节
     672                 repe cmpsd
     673                 jne _re_match           ;如果成功匹配,那么esi和edi刚好会在数据区之后的
     674                 
     675                 mov eax,[esi]           ;偏移地址
     676                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区
     677                 mov ax,[esi+0x04]       ;段的选择子
     678                 
     679                 or ax,0x0002            ;把RPL改为3,代表(内核)赋予应用程序以特权级3
     680                 mov [es:edi-252],ax     ;把段的选择子填入用户程序的段选择区
     681                 
     682                 _re_match:
     683                 pop ecx
     684                 pop esi
     685                 add esi,salt_length
     686                 pop edi
     687             loop _loop_C_SALT
     688             
     689             pop ecx
     690             pop edi
     691             add edi,256
     692         loop _loop_U_SALT
     693         ;---------------------------------------------------------------------
     694         ;----------------------填入临时中转任务门选择子-----------------------
     695         mov edi,[ebp+11*4]                  
     696         mov edi,[es:edi+0x06]               ;从TCB线性基地址中获得用户程序加载的基地址
     697         
     698         mov ax,[salt_tp]
     699         mov [es:edi+0x24+776],ax            ;填充任务门选择子
     700         ;---------------------------------------------------------------------
     701         mov esi,[ebp+11*4]              ;重新获得TCB的线性基地址
     702         
     703         ;现在设置所有的特权级栈段,并且把特权级栈段放到TCB中(为了等一下设置TSS)
     704         ;设置TSS特权0级栈段(暂存在TCB中)         
     705         mov ecx,Switch_Stack_Size
     706         mov eax,ecx
     707         mov [es:esi+0x1a],ecx
     708         shr dword[es:esi+0x1a],12       ;相当于除以4096
     709         call Sys_Routine_Segement:allocate_memory
     710         add eax,ecx                     ;得到最高地址
     711         mov [es:esi+0x1e],eax           ;登记线性基地址
     712         mov ebx,0x000fffff
     713         sub ebx,[es:esi+0x1a]           
     714         mov ecx,0x00c09600              ;特权级0
     715         call Sys_Routine_Segement:Make_Seg_Descriptor
     716         mov ebx,esi
     717         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     718         or cx,0x0000                    ;RPL为0
     719         mov [es:esi+0x22],cx
     720         mov dword[es:esi+0x24],0
     721         
     722         ;设置TSS特权1级栈段(暂存在TCB中)             
     723         mov ecx,Switch_Stack_Size
     724         mov eax,ecx
     725         mov [es:esi+0x28],ecx
     726         shr dword[es:esi+0x28],12       ;相当于除以4096
     727         call Sys_Routine_Segement:allocate_memory
     728         add eax,ecx                     ;得到最高地址
     729         mov [es:esi+0x2c],eax           ;登记线性基地址
     730         mov ebx,0x000fffff
     731         sub ebx,[es:esi+0x28]           
     732         mov ecx,0x00c0b600              ;特权级1
     733         call Sys_Routine_Segement:Make_Seg_Descriptor
     734         mov ebx,esi
     735         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     736         or cx,0x0001                    ;RPL为1
     737         mov [es:esi+0x30],cx
     738         mov dword[es:esi+0x32],0
     739         
     740         ;设置TSS特权2级栈段(暂存在TCB中)                 
     741         mov ecx,Switch_Stack_Size
     742         mov eax,ecx
     743         mov [es:esi+0x36],ecx
     744         shr dword[es:esi+0x36],12       ;相当于除以4096
     745         call Sys_Routine_Segement:allocate_memory
     746         add eax,ecx                     ;得到最高地址
     747         mov [es:esi+0x3a],eax           ;登记线性基地址
     748         mov ebx,0x000fffff
     749         sub ebx,[es:esi+0x36]           
     750         mov ecx,0x00c0d600              ;特权级2
     751         call Sys_Routine_Segement:Make_Seg_Descriptor
     752         mov ebx,esi
     753         call Sys_Routine_Segement:Set_New_LDT_To_TCB
     754         or cx,0x0002                    ;RPL为2
     755         mov [es:esi+0x3e],cx
     756         mov dword[es:esi+0x40],0
     757         
     758         ;在GDT中存入LDT信息
     759         mov eax,[es:esi+0x0c]
     760         movzx ebx,word[es:esi+0x0a]
     761         mov ecx,0x00408200              ;LDT描述符,特权级0级
     762         call Sys_Routine_Segement:Make_Seg_Descriptor
     763         call Sys_Routine_Segement:Set_New_GDT
     764         mov [es:esi+0x10],cx            ;在TCB放入LDT选择子
     765         
     766         ;在TCB中登记TSS的信息
     767         mov ecx,104                     ;创建一个最小尺寸的TSS
     768         mov [es:esi+0x12],cx
     769         dec word[es:esi+0x12]           ;记得-1,要的是段界限
     770         call Sys_Routine_Segement:allocate_memory
     771         mov [es:esi+0x14],ecx           ;TSS基地址
     772         
     773         ;构建TSS信息表
     774         mov dword[es:ecx+0x00],0        ;没有前一个任务
     775         
     776         mov edx,[es:esi+0x24]           ;0栈段
     777         mov [es:ecx+4],edx
     778         mov dx,[es:esi+0x22]
     779         mov [es:ecx+8],dx
     780         
     781         mov edx,[es:esi+0x32]           ;1栈段
     782         mov [es:ecx+12],edx
     783         mov dx,[es:esi+0x30]
     784         mov [es:ecx+16],dx
     785         
     786         mov edx,[es:esi+0x40]           ;2栈段
     787         mov [es:ecx+20],edx
     788         mov dx,[es:esi+0x3e]
     789         mov [es:ecx+24],dx
     790         
     791         mov edx,[es:esi+0x10]           ;LDT选择子
     792         mov [es:ecx+96],edx
     793         
     794         mov dx,[es:esi+0x12]            ;I/O偏移
     795         mov [es:ecx+102],dx             ;是102不是104
     796         
     797         mov word[es:ecx+100],0          ;T=0
     798         
     799         mov edi,[es:esi+0x06]           ;用户程序的线性基地址
     800         
     801         mov edx,[es:edi+0x10]           ;EIP
     802         mov [es:ecx+32],edx
     803         
     804         mov edx,[es:edi+0x14]           ;CS
     805         mov [es:ecx+76],dx
     806         
     807         mov edx,[es:edi+0x08]           ;SS
     808         mov [es:ecx+80],dx
     809         
     810         mov edx,[es:edi+0x04]           ;DS(是指向用户头部,而不是用户程序的数据区) 
     811         mov [es:ecx+84],dx
     812         
     813         mov word[es:ecx+72],0           ;ES
     814         mov word[es:ecx+88],0           ;FS
     815         mov word[es:ecx+92],0           ;GS
     816         
     817         pushfd
     818         pop edx
     819         mov [es:ecx+36],edx             ;EFLAGS 
     820         
     821         ;在GDT中存入TSS信息
     822         mov eax,[es:esi+0x14]
     823         movzx ebx,word[es:esi+0x12]
     824         mov ecx,0x00408900
     825         call Sys_Routine_Segement:Make_Seg_Descriptor
     826         call Sys_Routine_Segement:Set_New_GDT
     827         mov [es:esi+0x18],cx
     828         
     829         pop es
     830         pop ds
     831         popad
     832         ret 8                           ;相当于是stdcall,过程清栈
     833         ;---------------------------------------------------------------------
     834     start:
     835         mov eax,Core_Data_Segement
     836         mov ds,eax
     837         
     838         mov ebx,message_1
     839         call Sys_Routine_Segement:put_string
     840         
     841         mov eax,0                   
     842         cpuid
     843         cmp eax,0x80000004          ;判断是否有0x80000002-0x80000004功能 
     844         jl _@load
     845         
     846         ;显示处理器品牌信息,从80486的后期版本开始引入
     847         mov eax,0x80000002
     848         cpuid
     849         mov [cpu_brand+0x00],eax
     850         mov [cpu_brand+0x04],ebx
     851         mov [cpu_brand+0x08],ecx
     852         mov [cpu_brand+0x0c],edx
     853         
     854         mov eax,0x80000003
     855         cpuid
     856         mov [cpu_brand+0x10],eax
     857         mov [cpu_brand+0x14],ebx
     858         mov [cpu_brand+0x18],ecx
     859         mov [cpu_brand+0x1c],edx
     860         
     861         mov eax,0x80000004
     862         cpuid
     863         mov [cpu_brand+0x20],eax
     864         mov [cpu_brand+0x24],ebx
     865         mov [cpu_brand+0x28],ecx
     866         mov [cpu_brand+0x2c],edx
     867         
     868         mov ebx,cpu_brnd0
     869         call Sys_Routine_Segement:put_string
     870         mov ebx,cpu_brand
     871         call Sys_Routine_Segement:put_string
     872         mov ebx,cpu_brnd1
     873         call Sys_Routine_Segement:put_string
     874 
     875         _@load:
     876         mov ebx,message_7
     877         call Sys_Routine_Segement:put_string
     878         ;----------------------------安装门------------------------------------
     879         mov edi,salt
     880         mov ecx,salt_items_sum
     881         _set_gate:
     882             push ecx
     883             mov eax,[edi+256]
     884             mov bx,[edi+260]        ;选择子
     885             mov cx,0xec00           ;门是特权级是3的门,那么任何程序都能调用
     886             or cx,[edi+262]         ;加上参数个数
     887             
     888             call Sys_Routine_Segement:Make_Gate_Descriptor
     889             call Sys_Routine_Segement:Set_New_GDT
     890             mov [edi+260],cx        ;回填选择子
     891             add edi,salt_length
     892             pop ecx
     893         loop _set_gate
     894         ;----------------------------------------------------------------------
     895         mov ebx,do_status
     896         call far [salt_1+256]
     897         mov ebx,message_6
     898         call far [salt_1+256]
     899         mov ebx,message_In_Gate
     900         call far [salt_1+256]           ;调用门显示字符信息(忽略偏移地址(前4字节))
     901         
     902         mov ebx,message_4
     903         call far [salt_1+256]
     904         mov ebx,message_2
     905         call far [salt_1+256]
     906         
     907         mov eax,All_4GB_Segment
     908         mov es,eax
     909         
     910         mov ecx,104
     911         call Sys_Routine_Segement:allocate_memory
     912         mov [prgman_tss],ecx            ;保留线性基地址
     913         
     914         mov word[es:ecx+100],0          ;TI=0
     915         mov word[es:ecx+102],103        ;任务管理器不需要I/O映射,要大于等于界限
     916         mov word[es:ecx+96],0           ;任务允许没有自己的LDT
     917         mov dword[es:ecx+28],0          ;设置CR3  
     918         mov word[es:ecx+0],0            ;没有前一个任务
     919         
     920         
     921         mov eax,ecx
     922         mov ebx,103                     ;TSS段界限
     923         mov ecx,0x00408900
     924         call Sys_Routine_Segement:Make_Seg_Descriptor
     925         call Sys_Routine_Segement:Set_New_GDT
     926         mov [prgman_tss+0x04],cx
     927         
     928         ltr cx                          ;启动任务
     929         ;------------------安装用户管理程序的临时返回任务门--------------------     
     930         mov eax,0x0000                  ;TSS不需要偏移地址
     931         mov bx,[prgman_tss+0x04]        ;TSS的选择子
     932         mov cx,0xe500
     933         
     934         call Sys_Routine_Segement:Make_Gate_Descriptor      
     935         call Sys_Routine_Segement:Set_New_GDT
     936         mov [salt_tp],cx                ;填入临时中转任务门选择子,注意不需要加260了
     937         ;----------------------------------------------------------------------
     938         mov ebx,prgman_msg1
     939         call Sys_Routine_Segement:put_string
     940         
     941         
     942         ;----------------------用户管理程序----------------------------
     943         mov ecx,0x46                    ;TCB链大小
     944         call Sys_Routine_Segement:allocate_memory
     945         call append_to_tcb
     946         
     947         push User_Program_AddressA
     948         push ecx
     949         call load_program
     950         
     951         jmp far [es:ecx+0x14]           ;初次打开程序A,一定要用跳转方式打开,不能call,否则不能返回了
     952         ;------------------------------------------------
     953         mov ebx,prgman_msg2
     954         call Sys_Routine_Segement:put_string
     955         
     956         mov ecx,0x46                    ;TCB链大小
     957         call Sys_Routine_Segement:allocate_memory
     958         call append_to_tcb
     959         
     960         push User_Program_AddressB
     961         push ecx
     962         call load_program
     963         jmp far [es:ecx+0x14]           ;初次打开程序B,一定要用跳转方式打开,不能call,否则不能返回了
     964         ;------------------------------------------------
     965         mov eax,Core_Data_Segement
     966         mov ds,eax
     967         mov eax,All_4GB_Segment
     968         mov es,eax
     969         
     970         mov ebx,prgman_msg3
     971         call Sys_Routine_Segement:put_string
     972         
     973         mov ecx,[tcb_chain]             ;任务A
     974         call far [es:ecx+0x14]
     975         ;------------------------------------------------
     976         mov eax,Core_Data_Segement
     977         mov ds,eax
     978         mov eax,All_4GB_Segment
     979         mov es,eax
     980         
     981         mov ebx,prgman_msg3
     982         call Sys_Routine_Segement:put_string
     983         
     984         mov ecx,[tcb_chain]             
     985         mov ecx,[es:ecx+0x00]           ;任务B
     986         call far [es:ecx+0x14]
     987         ;------------------------------------------------
     988         mov eax,Core_Data_Segement
     989         mov ds,eax
     990         
     991         mov ebx,prgman_msg3
     992         call Sys_Routine_Segement:put_string
     993         mov ebx,core_stop
     994         call Sys_Routine_Segement:put_string
     995         
     996         cli
     997         hlt
     998         ;----------------------------------------------------------------------
     999 ;=========================================================================
    1000 SECTION core_trail
    1001 ;----------------------------------------------------------------
    1002 Program_end:
     1 ;==============================用户程序B=======================================
     2 SECTION header vstart=0
     3 
     4         program_length   dd program_end          ;程序总长度#0x00
     5          
     6         head_len         dd header_end           ;程序头部的长度#0x04
     7 
     8         stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
     9         stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
    10                                                  ;以4KB为单位
    11                                                   
    12         prgentry         dd start                ;程序入口#0x10 
    13         code_seg         dd section.code.start   ;代码段位置#0x14
    14         code_len         dd code_end             ;代码段长度#0x18
    15 
    16         data_seg         dd section.data.start   ;数据段位置#0x1c
    17         data_len         dd data_end             ;数据段长度#0x20
    18 ;-------------------------------------------------------------------------------
    19         ;符号地址检索表
    20         salt_items       dd (u_salt_end-salt)/256 ;#0x24
    21          
    22         salt:                                     ;#0x28
    23         Printf:          db  '@Printf'
    24                      times 256-($-Printf) db 0
    25                      
    26         TerminateProgram:db  '@TerminateProgram'
    27                      times 256-($-TerminateProgram) db 0
    28                      
    29         ReadHarddisk:    db  '@ReadHarddisk'
    30                      times 256-($-ReadHarddisk) db 0
    31                  
    32 u_salt_end:
    33         TpBack:          dd 0                     ;任务门的偏移地址没用,直接填充就可以了
    34                          dw 0                     ;任务门的选择子#0x24+776
    35 header_end: 
    36 ;===============================================================================
    37 SECTION data align=16 vstart=0    
    38                          
    39         message_1       db  0x0d,0x0a
    40                         db  '[USER TASKB]: Hi! I am task B',0x0d,0x0a,0
    41                           
    42         message_2       db  0x0d,0x0a
    43                         db  '[USER TASKB]: Hi! I am task B, I have been backed',0x0d,0x0a,0
    44 data_end:
    45 
    46 ;===============================================================================
    47       [bits 32]
    48 ;===============================================================================
    49 SECTION code align=16 vstart=0
    50 start:
    51         mov eax,ds
    52         mov fs,eax
    53      
    54         mov eax,[data_seg]
    55         mov ds,eax
    56      
    57         mov ebx,message_1
    58         call far [fs:Printf]
    59         jmp far [fs:TpBack]                  ;任务切换 
    60         
    61         mov ebx,message_2
    62         call far [fs:Printf]
    63         iretd
    64 code_end:
    65 
    66 ;===============================================================================
    67 SECTION trail
    68 ;-------------------------------------------------------------------------------
    69 program_end:
     1 ;==============================用户程序A=======================================
     2 SECTION header vstart=0
     3 
     4         program_length   dd program_end          ;程序总长度#0x00
     5          
     6         head_len         dd header_end           ;程序头部的长度#0x04
     7 
     8         stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
     9         stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
    10                                                  ;以4KB为单位
    11                                                   
    12         prgentry         dd start                ;程序入口#0x10 
    13         code_seg         dd section.code.start   ;代码段位置#0x14
    14         code_len         dd code_end             ;代码段长度#0x18
    15 
    16         data_seg         dd section.data.start   ;数据段位置#0x1c
    17         data_len         dd data_end             ;数据段长度#0x20
    18 ;-------------------------------------------------------------------------------
    19         ;符号地址检索表
    20         salt_items       dd (u_salt_end-salt)/256 ;#0x24
    21          
    22         salt:                                     ;#0x28
    23         Printf:           db  '@Printf'
    24                      times 256-($-Printf) db 0
    25                      
    26         TerminateProgram:db  '@TerminateProgram'
    27                      times 256-($-TerminateProgram) db 0
    28                      
    29         ReadHarddisk:    db  '@ReadHarddisk'
    30                      times 256-($-ReadHarddisk) db 0
    31                  
    32 u_salt_end:
    33         TpBack:             dd 0                      ;任务门的偏移地址没用,直接填充就可以了
    34                          dw    0                      ;任务门的选择子#0x24+776
    35 header_end:    
    36 ;===============================================================================
    37 SECTION data align=16 vstart=0    
    38                          
    39         message_1       db  0x0d,0x0a
    40                         db  '[USER TASKA]: Hi! I am task A',0x0d,0x0a,0
    41                           
    42         message_2         db  0x0d,0x0a
    43                         db  '[USER TASKA]: Hi! I am task A, I have been backed',0x0d,0x0a,0
    44 data_end:
    45 ;===============================================================================
    46       [bits 32]
    47 ;===============================================================================
    48 SECTION code align=16 vstart=0
    49 start:
    50         mov eax,ds
    51         mov fs,eax
    52      
    53         mov eax,[data_seg]
    54         mov ds,eax
    55      
    56         mov ebx,message_1
    57         call far [fs:Printf]
    58         jmp far [fs:TpBack]                    ;任务切换 
    59         
    60         mov ebx,message_2
    61         call far [fs:Printf]
    62         iretd
    63 code_end:
    64 
    65 ;===============================================================================
    66 SECTION trail
    67 ;-------------------------------------------------------------------------------
    68 program_end:

     程序上我没有回收内存,我是觉得我自己就是做个简单的也不是很好,索性不做了。

     

     

     

     

     

     

     

     

  • 相关阅读:
    二叉树前、中、后遍历
    程序员节宜冒泡
    HashMap源码分析
    Stack源码解析
    逆袭之旅DAY24.XIA.二重进阶、双色球
    逆袭之旅DAY24.XIA.数组练习
    LY.JAVA面向对象编程.内部类
    LY.JAVA面向对象编程.修饰符
    LY.JAVA面向对象编程.包的概述、导包
    XIA.人机猜拳
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5295218.html
Copyright © 2011-2022 走看看