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:

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

 

 

 

 

 

 

 

 

posted @ 2016-03-19 15:01  PhiliAI  阅读(1400)  评论(1编辑  收藏  举报