汇编实例解析--分页(虚拟内存),多任务机制

1.主引导程序

        一开始是实模式,这里切换到保护模式

         ; 职责:
         ; 1.完成实模式到保护模式切换
         ; 2.安装保护模式下4个基础段描述符
         ; 3.将指定位置物理磁盘程序加载到指定物理内存位置
         ; 4.为加载的程序构造&安装3个段描述符
         ; 5.将执行流程跳转到加载的程序      
         core_base_address equ 0x00040000
         core_start_sector equ 0x00000001
         ; 刚刚进入主引导程序时候
         ; 进程位于物理内存0x0000:0x7c00处,cs的内容是0x0000   
         mov ax,cs      
         mov ss,ax
         mov sp,0x7c00
         
         ; gdt区域起始地址
         mov eax,[cs:pgdt+0x7c00+0x02]      
         xor edx,edx
         mov ebx,16
         div ebx    
         
         ; ds:ebx就是gdt区域起始位置
         mov ds,eax                     
         mov ebx,edx                       
         
         ; gdt中索引1
         ; 段基地址为0x0000 0000
         ; 段内的数据是32位的
         ; 4GB尺寸
         ; 可读,可写的数据段
         mov dword [ebx+0x08],0x0000ffff
         mov dword [ebx+0x0c],0x00cf9200   
         
         ; gdt中索引2
         ; 段基地址0x0000 7c00
         ; 段尺寸512
         ; 段是只执行的代码段
         ; 引导程序作为一个任务需要在gdt中为自己安装代码段
         mov dword [ebx+0x10],0x7c0001ff
         mov dword [ebx+0x14],0x00409800    
         
         ; gdt中索引3
         ; 段基地址0x0000 7c00
         ; 段尺寸4KB
         ; 段是可读,可写的栈段
         ; 引导程序作为一个任务需要在gdt中为自己安装栈段
         mov dword [ebx+0x18],0x7c00fffe
         mov dword [ebx+0x1c],0x00cf9600
         
         ; gdt中索引4
         ; 段基地址为0x000b 8000
         ; 段尺寸0x8000
         ; 可读,可写的数据段
         ; 需要在gdt中为显存区域安装一个显存段
         mov dword [ebx+0x20],0x80007fff
         mov dword [ebx+0x24],0x0040920b    
         
         ; gdt界限=gdt尺寸-1
         mov word [cs: pgdt+0x7c00],39   
         ; gdt界限,基地址传递到处理器  
         lgdt [cs: pgdt+0x7c00]
         
         in al,0x92                         
         or al,0000_0010B
         out 0x92,al                        
         cli                               

         mov eax,cr0
         or eax,1
         mov cr0,eax  
         ; 保护模式下,0x0010:flush解释为
         ; 0x0010得到RPL为0,位于gdt中,索引为2的8字节描述符描述的段  
         ; 保护模式下,段选择子开始起作用                   
         jmp dword 0x0010:flush            
         [bits 32]               
  flush:       
         mov eax,0x0008 
         ; 保护模式下,段选择子起作用,4GB数据段选择子 
         mov ds,eax
         
         mov eax,0x0018   
         ; 栈段选择子
         mov ss,eax
         xor esp,esp   
         ; ss:esp构成初始时刻栈指针位置(位于栈区域地址高位置)
         mov esp, 0xffffffff                     
         
         mov edi,core_base_address 
         mov eax,core_start_sector
         mov ebx,edi
         ; 从磁盘扇区读取一个扇区到物理内存
         call read_hard_disk_0              
         
         ; 内核任务头4字节是内核整体尺寸
         mov eax,[edi]                    
         xor edx,edx 
         mov ecx,512
         div ecx
         or edx,edx
         jnz @1                            
         dec eax                            
   @1:
         ; eax为内核任务剩余扇区数
         or eax,eax                         
         jz setup                          
         mov ecx,eax        
         mov eax,core_start_sector
         inc eax                            
   @2:
         call read_hard_disk_0
         inc eax
         loop @2                            
         
         ; 此时内核任务【操作系统】已经全部拷贝到了物理内存
 setup:
         mov esi,[0x7c00+pgdt+0x02]   
         
         ; 操作系统的
         ; 内核任务例程段偏移
         mov eax,[edi+0x04]   
         mov ebx,[edi+0x08]                 
         sub ebx,eax
         ; 历程段界限
         dec ebx   
         ; 这样得到内核例程段在内存起始位置     
         add eax,edi                     
         mov ecx,0x00409800   
         ; 构造段描述符
         call make_gdt_descriptor
         ; 安装到gdt区域
         mov [esi+0x28],eax
         mov [esi+0x2c],edx
         
         ; 内核任务数据段
         mov eax,[edi+0x08]               
         mov ebx,[edi+0x0c]                
         sub ebx,eax
         dec ebx     
         add eax,edi              
         mov ecx,0x00409200    
         ; 构造段描述符,安装到gdt             
         call make_gdt_descriptor
         mov [esi+0x30],eax
         mov [esi+0x34],edx 
         
         ; 内核任务代码段
         mov eax,[edi+0x0c]               
         mov ebx,[edi+0x00]              
         sub ebx,eax
         dec ebx      
         add eax,edi    
         mov ecx,0x00409800             
         call make_gdt_descriptor
         mov [esi+0x38],eax
         mov [esi+0x3c],edx
         
         mov word [0x7c00+pgdt],63         
         lgdt [0x7c00+pgdt]  

         ; 借助gdt中的代码段选择子,段内偏移实现控制跳转
         jmp far [edi+0x10]  
         
; 传入:eax是磁盘起始逻辑扇区
; 传入:ds:ebx构成存放磁盘扇区起始物理内存位置
; 传出:ds:ebx磁盘扇区物理内存尾后位置
read_hard_disk_0:    
         push eax 
         push ecx
         push edx
         push eax
         
         mov dx,0x1f2
         mov al,1
         out dx,al                      

         inc dx     
         pop eax
         out dx,al                       

         inc dx                         
         mov cl,8
         shr eax,cl
         out dx,al                      

         inc dx                         
         shr eax,cl
         out dx,al                       

         inc dx                         
         shr eax,cl
         or al,0xe0       
         out dx,al

         inc dx                         
         mov al,0x20                     
         out dx,al

  .waits:
         in al,dx
         and al,0x88
         cmp al,0x08
         jnz .waits        

         mov ecx,256
         mov dx,0x1f0
  .readw:
         in ax,dx
         mov [ebx],ax
         add ebx,2
         loop .readw

         pop edx
         pop ecx
         pop eax
         ret

; 依据传入构造8位edx:eax描述符
make_gdt_descriptor:    
         mov edx,eax
         shl eax,16                   
         or ax,bx                       
         
         and edx,0xffff0000     
         rol edx,8
         bswap edx                      
         xor bx,bx
         or edx,ebx                     
         or edx,ecx   
         ret
      
         pgdt             dw 0
                          dd 0x00007e00    
         times 510-($-$$) db 0
                          db 0x55,0xaa

2.内核

        保护模式下,开启分页机制(虚拟内存管理),多任务机制,基于调用门调用例程,特权级

         core_code_seg_sel     equ  0x38    
         core_data_seg_sel     equ  0x30     
         sys_routine_seg_sel   equ  0x28   
         video_ram_seg_sel     equ  0x20    
         core_stack_seg_sel    equ  0x18    
         mem_0_4_gb_seg_sel    equ  0x08   
         core_length      dd core_end  
         ; 这里的偏移信息将帮助引导程序为这些段构建描述符
         ; 并将描述符安装到gdt中     
         sys_routine_seg  dd section.sys_routine.start
         core_data_seg    dd section.core_data.start
         core_code_seg    dd section.core_code.start
         core_entry       dd start         
                          dw core_code_seg_sel
         [bits 32]
SECTION sys_routine vstart=0 
; ds:ebx待显示字符串起始位置             
put_string:    
         push ecx
  .getc:
         mov cl,[ebx]
         or cl,cl
         jz .exit
         call put_char
         inc ebx
         jmp .getc
  .exit:
         pop ecx
         retf                             
put_char:          
         pushad
         mov dx,0x3d4
         mov al,0x0e
         ; 向0x3d4写入0x0e
         out dx,al
         inc dx           
         ; 从0x3d5读出1字节到al                 
         in al,dx              
         ; 这是16位光标位置高8位            
         mov ah,al

         dec dx                 
         mov al,0x0f
         ; 向0x3d4写入0x0f           
         out dx,al
         inc dx                            
         ; 从0x3d5读出1字节到al
         in al,dx              
         ; 16位光标位置存储到bx            
         mov bx,ax                          
         
         ; 如果c1是0x0d
         cmp cl,0x0d                       
         jnz .put_0a
         mov ax,bx
         mov bl,80
         div bl
         mul bl
         ; 将bx设置为光标所在行行首的位置
         mov bx,ax
         ; 设置光标位置实现回车
         jmp .set_cursor

  .put_0a:
         ; c1是否为0x0a
         cmp cl,0x0a                       
         jnz .put_other
         ; 将光标位置设置为下一行对应位置
         add bx,80
         ; 每次光标位置前进时,都要经过滚屏处理。
         ; 滚屏处理是先判断光标位置是否超出一屏范围。
         ; 超出时,滚屏,然后将光标设置为最后一行行首。
         ; 没超出,则什么都不做。
         jmp .roll_screen

  .put_other:                    
         ; 寄存器入栈          
         push es
         mov eax,video_ram_seg_sel          
         ; 显存段
         mov es,eax
         ; 光标位置*2=对应位置字符在显存位置
         shl bx,1
         mov [es:bx],cl
         pop es
         
         ; 恢复光标位置。将字符设置到当前光标位置。设置后,
         ; 前进当前光标位置
         shr bx,1
         inc bx

  .roll_screen:
         ; 要设置的光标位置是否超出一屏幕
         cmp bx,2000                     
         ; 没超出,滚屏啥也不做   
         jl .set_cursor
        
         ; 真正的滚屏
         ; 将屏幕每行依次移动到前面一行。空出一行。显示空白。
         ; 光标设置为最后一行行首
         push ds
         push es
         mov eax,video_ram_seg_sel
         mov ds,eax
         mov es,eax
         cld
         mov esi,0xa0                      
         mov edi,0x00                      
         mov ecx,1920
         rep movsd
         mov bx,3840                       
         mov ecx,80                         
  .cls:
         mov word[es:bx],0x0720
         add bx,2
         loop .cls
         pop es
         pop ds
         mov bx,1920

  .set_cursor:
         mov dx,0x3d4
         mov al,0x0e
         ; 向0x3d4写入0x0e
         out dx,al
         inc dx           
         mov al,bh
         ; 向0x3d5写入1字节,这是16位光标位置高8位
         out dx,al
         dec dx                           
         mov al,0x0f
         out dx,al
         inc dx                           
         mov al,bl
         ; 向0x3d5写入1字节,这是16位光标位置低8位
         out dx,al
         ; 一系列寄存器恢复
         popad
         ; 流程恢复
         ret                                

; eax存储28位逻辑扇区
; ds:ebx用于接受磁盘扇区物理内存位置
; 过程结束后ds:ebx指向区域尾后位置
read_hard_disk_0:                           
         push eax 
         push ecx
         push edx
         push eax
         
         mov dx,0x1f2
         mov al,1
         ; 向0x1f2写入1,这时本次磁盘传输的块个数
         ; 磁盘块是512字节
         out dx,al                          
       
         inc dx                            
         pop eax
         ; 向0x1f3写入1字节,这是28位逻辑扇区最低8位
         out dx,al                         

         inc dx                            
         mov cl,8
         shr eax,cl
         ; 向0x1f4写入1字节,这是28位逻辑扇区次低8位
         out dx,al                         

         inc dx                             
         shr eax,cl
         ; 向0x1f5写入1字节,这是28位逻辑扇区次次低8位
         out dx,al                          

         inc dx                             
         shr eax,cl
         or al,0xe0                         
         ; 向0x1f6写入1字节。低4位是28位逻辑扇区高4位。高4位1110
         ; 用于解释逻辑扇区号,写入对象是主盘
         out dx,al

         inc dx                            
         mov al,0x20                       
         ; 向0x1f7写入0x20
         ; 这样磁盘开始数据传输工作
         out dx,al

  .waits:
         ; 从0x1f7读取1字节
         in al,dx
         and al,0x88
         cmp al,0x08
         ; 持续等,直到磁盘不忙且准备好了数据传输
         jnz .waits                         
       
         mov ecx,256                        
         mov dx,0x1f0
  .readw:
         ; 从0x1f0读取2字节
         ; 这实际上是顺序读取2字节内容。两字节来自磁盘已经准备好的数据。
         in ax,dx
         ; 将数据顺序写入物理内存ds:ebx
         mov [ebx],ax
         add ebx,2
         loop .readw

         ; 寄存器恢复
         pop edx
         pop ecx
         pop eax
         ; 过程返回
         retf                              

; 格式化显示
; 将edx中存储的32位数值按16进制输出显示
put_hex_dword:
         ; 一系列寄存器入栈                             
         pushad
         ; 寄存器入栈
         push ds
      
         mov ax,core_data_seg_sel   
         ; 内核数据段        
         mov ds,ax
      
         mov ebx,bin_hex                   
         mov ecx,8
  .xlt:    
         rol edx,4
         mov eax,edx
         ; 仅仅保留最低4位
         and eax,0x0000000f
         xlat
      
         push ecx
         ; al是对应的字符
         mov cl,al                           
         call put_char
         pop ecx
       
         loop .xlt
      
         pop ds
         popad
         retf

; 将构造好的edx:eax写入gdt。
; cx带回新写入描述符的选择子    
set_up_gdt_descriptor:                      
         ; 寄存器入栈
         push eax
         push ebx
         push edx
         push ds
         push es

         mov ebx,core_data_seg_sel
         mov ds,ebx
         ; 得到处理器在用的gdt的界限,基地址
         sgdt [pgdt]                        

         mov ebx,mem_0_4_gb_seg_sel
         mov es,ebx
         ; 这是gdt界限
         movzx ebx,word [pgdt]
         ; 这是gdt尺寸              
         inc bx       
         ; 这是gdt区域尾后位置                     
         add ebx,[pgdt+2]                  
         ; 向gdt尾部写入8字节,就是新的描述符
         mov [es:ebx],eax
         mov [es:ebx+4],edx

         add word [pgdt],8 
         ; 再次通知处理器新的gdt界限,基地址                
         lgdt [pgdt]                        

         mov ax,[pgdt]                    
         xor dx,dx
         mov bx,8
         div bx                             
         mov cx,ax
         ; 这是新写入描述符的选择子
         shl cx,3                          

         pop es
         pop ds
         pop edx
         pop ebx
         pop eax
         retf

; 段基地址31~24 G D/B L AVL 段界限19~16 P DPL S TYPE 段基地址23~16 
; 段基地址15~0 段界限15~0
; 传入:eax段基地址,ebx段界限,ecx属性信息
; 传出:edx:eax 8位描述符    段基地址15~0 段界限15~0
make_seg_descriptor:    
         ; edx                  
         mov edx,eax
         ; 高16位是原来低16位
         shl eax,16
         ; 低16位是ebx低16位
         or ax,bx           
         ; eax已经全部设置完毕                
         
         ; edx高16位保存--段基地址31~24
         and edx,0xffff0000  
         ; 高8位是高16位低8位,低8位是高16位高8位。其余是0               
         rol edx,8
         ; 高8位是高16位高8位,低8位是高16位低8位
         bswap edx  


         xor bx,bx
         ; ebx本身只有低20位有值,上面又把低16位置0,所以这里ebx只有低20位的高4位有效
         ; 这个or把这个高4位设置到edx
         or edx,ebx                  
         ; 把edx剩余部分用属性位填充      
         or edx,ecx                        
         retf

; 通过调用门执行控制转移相比通过段选择子:段内偏移方式
; 可以在描述符中额外指定特权级,参数个数等信息

; 构造门描述符
; 段内偏移31~16 P DPL 0 TYPE(1100) 000 参数个数(5位) 
; 例程所在代码段选择子 段内偏移15~0
; edx:eax
; eax:32位段偏移
; ebx:16位例程所在代码段选择子
; cx:16位属性位
make_gate_descriptor:                       
         push ebx
         push ecx
      
         mov edx,eax
         ; edx保留eax高16位
         and edx,0xffff0000
         ; edx低16位完全按照cx来               
         or dx,cx               
         ; eax保留低16位        
         and eax,0x0000ffff
         ; ebx用高16位保存原来低16位内容                 
         shl ebx,16                     
         ; 高16位存储代码段选择子,低16位存储段内偏移低16位     
         or eax,ebx                        
         pop ecx
         pop ebx
         retf                                   

; 物理页分配
; 分配的物理页基地址应该传送出来
; eax返回时包含了刚刚分配的物理页的起始物理地址
allocate_a_4k_page:     
         push ebx
         push ecx
         push edx
         push ds
         mov eax,core_data_seg_sel
         mov ds,eax
         xor eax,eax

  .b1:
         ; ds:page_bit_map提供区域起始位置
         ; 从该位置开始第eax位内容传入CF,eax位被设置为1
         bts [page_bit_map],eax
         ; cf为0,则跳转
         jnc .b2
         ; 前进1位
         inc eax
         ; 比较
         cmp eax,page_map_len*8
         ; 小于,跳转到b1
         jl .b1
         
         ; 这样表示搜索了所有可用物理页,找不到空闲页
         mov ebx,message_3
         call sys_routine_seg_sel:put_string
         hlt                                
         
  .b2:
         ; 这意味着完成了一个4k页的分配
         ; eax返回时包含了刚刚分配的物理页的起始物理地址
         shl eax,12                         
         pop ds
         pop edx
         pop ecx
         pop ebx
         ret

; ds:ebx是要划分虚拟内存区域起始位置
; 为4kb虚拟区域分配了物理页,且在页目录表-页表中进行了虚拟页-物理页的登记&注册
; 依据ds:ebx线性地址找到页目录表中对应项
; 1.对应项存在,找到对应项指向的页表
; 寻找并分配物理页,在页表对应项登记物理页
; 2.对应项不存在,寻找并分配物理页,物理页初始化
; 在页目录表对应项登记页表。
; 寻找并分配物理页,在页表对应项登记物理页
alloc_inst_a_page:   
         ; 寄存器入栈
         push eax
         push ebx
         push esi
         push ds
         
         mov eax,mem_0_4_gb_seg_sel
         mov ds,eax
         
         ; ds:ebx是要划分虚拟内存区域起始位置
         mov esi,ebx
         ; 高10位保留,其余位设置为0
         and esi,0xffc00000
         ; 20位全0 高10位 00
         shr esi,20         
         ; 20位全1 高10位 00               
         or esi,0xfffff000                 
         
         ; ds:esi
         ; 高10位 0b 1111 1111 11,作为索引访问页目录表,得到物理地址指向页目录表,但被看成页表
         ; 中间10位 0b 1111 1111 11,作为索引访问页表,得到物理地址指向页目录表,但被看成物理页
         ; 第12位作为页内偏移。取出的4字节是以esi原始高10位为索引得到的页目录表里的项
         ; esi原始高10位代表了这个线性地址关联的页目录表的项。
         test dword [esi],0x00000001        
         ; 项最低位不是1,跳转到b1
         jnz .b1                          
         
         ; 项最低位是0,表示原始esi线性地址关联的页表不存在于物理内存。
         ; 准备4kb物理页用作传入esi对应线性地址关联的页表
         ; eax返回时将包含刚刚分配的物理页的起始物理地址
         call allocate_a_4k_page           
         ; 低3位设置为111,其余位保持不变
         or eax,0x00000007
         ; ds:esi在页目录表里找到传入esi对应线性地址关联索引项
         ; 设置索引项的内容,以便索引项可以指向关联页表
         mov [esi],eax                     
          
  .b1:
         mov esi,ebx
         ; 高10位全0 高22位
         shr esi,10
         ; 高10位全0 高10位 低12位全0
         and esi,0x003ff000          
         ;  高10位全1 高10位 低12位全0       
         or esi,0xffc00000                  
        
         ; ebx是要分配虚拟内存区域起始地址(虚拟地址) 
         ; 高10位全0 中间10位 低12位全0
         and ebx,0x003ff000
         ; 高10位全0 10位全0 中间10位 00 
         shr ebx,10                     
         ; 高10位全1 高10位 中间10位 00   
         or esi,ebx                    
         ; 分配4kb物理页,物理页起始地址存在于eax   
         call allocate_a_4k_page           
         or eax,0x00000007
         ; ds:esi
         ; 高10位全1作为索引项访问"页目录表",得到物理地址指向页目录表(当作页表)
         ; 次高10位是线性地址高10位作为索引项,访问"页表",得到物理地址指向线性地址关联页表(当作物理页)
         ; 低12位是线性地址中间10位+00,访问"物理页",得到4字节,这4字节是线性地址在页表里面的项
         ; 项的内容是线性地址所在的物理页起始位置(含属性位)
         ; 这样就为4kb虚拟区域分配了物理页,且在页目录表-页表中进行了虚拟页-物理页的登记&注册
         ; 这样此后,分配的虚拟区域就可被使用和访问了
         mov [esi],eax 
          
         pop ds
         pop esi
         pop ebx
         pop eax
         retf  


; 分配一个物理页
; 将页目录表内容拷贝到新的物理页
; 返回新物理页起始地址eax
create_copy_cur_pdir:  
         ; 寄存器入栈                    
         push ds
         push es
         push esi
         push edi
         push ebx
         push ecx
         
         mov ebx,mem_0_4_gb_seg_sel
         mov ds,ebx
         mov es,ebx
       
         ; 分配一个物理页,以便作为新的页目录表
         call allocate_a_4k_page            
         mov ebx,eax
         or ebx,0x00000007
         ; 0xffff fff8
         ; 0x1111 1111 11线性地址对应页目录表中最后一项,该项中物理地址指向是页目录表
         ; 0x1111 1111 11线性地址对应页表中最后一项,该项中物理地址指向的是页目录表
         ; 0xff8,线性地址对应物理页中偏移为0xff8处,0xff8/4=0x1111 1111 10=2^10 - 1 - 1=1022
         ; 如果物理页都是4字节的项,这个偏移指向倒数第2项
         ; 倒数第二项设置为指向刚刚分配的4kb物理页
         mov [0xfffffff8],ebx
         
         mov esi,0xfffff000                 
         mov edi,0xffffe000                 
         mov ecx,1024                      
         cld
         ; 每次拷贝4字节
         ; ds:esi--->es:edi
         ; 0xfffff000是在用的页目录表起始线性地址
         ; 0xffffe000是刚刚新分配4kb物理页的起始线性地址
         repe movsd 
         ; 上述完成了将在用的页目录表1024项逐个拷贝到新的4kb物理页
         
         ; 寄存器恢复
         pop ecx
         pop ebx
         pop edi
         pop esi
         pop es
         pop ds
         
         retf

; 将控制从用户任务返回到内核任务        
terminate_current_task:                    
         mov eax,core_data_seg_sel
         mov ds,eax

         pushfd
         pop edx
 
         test dx,0100_0000_0000_0000B      
         jnz .b1   
         ; 通过jmp进入用户任务的,通过jmp返回到内核任务                         
         jmp far [program_man_tss]         
  .b1: 
         ; 通过call 用户任务tss基地址 进入用户任务的,通过iretd返回到内核任务
         iretd
sys_routine_end:

SECTION core_data vstart=0                  
         pgdt             dw  0             
                          dd  0

         page_bit_map     db  0xff,0xff,0xff,0xff,0xff,0x55,0x55,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
         page_map_len     equ $-page_bit_map
                          
         salt:
         salt_1           db  '@PrintString'
                     times 256-($-salt_1) db 0
                          dd  put_string
                          dw  sys_routine_seg_sel

         salt_2           db  '@ReadDiskData'
                     times 256-($-salt_2) db 0
                          dd  read_hard_disk_0
                          dw  sys_routine_seg_sel

         salt_3           db  '@PrintDwordAsHexString'
                     times 256-($-salt_3) db 0
                          dd  put_hex_dword
                          dw  sys_routine_seg_sel

         salt_4           db  '@TerminateProgram'
                     times 256-($-salt_4) db 0
                          dd  terminate_current_task
                          dw  sys_routine_seg_sel

         salt_item_len   equ $-salt_4
         salt_items      equ ($-salt)/salt_item_len

         message_0        db  '  Working in system core,protect mode.'
                          db  0x0d,0x0a,0

         message_1        db  '  Paging is enabled.System core is mapped to'
                          db  ' address 0x80000000.',0x0d,0x0a,0
         
         message_2        db  0x0d,0x0a
                          db  '  System wide CALL-GATE mounted.',0x0d,0x0a,0
         
         message_3        db  '********No more pages********',0
         
         message_4        db  0x0d,0x0a,'  Task switching...@_@',0x0d,0x0a,0
         
         message_5        db  0x0d,0x0a,'  Processor HALT.',0
         
        
         bin_hex          db '0123456789ABCDEF'
         core_buf   times 512 db 0         

         cpu_brnd0        db 0x0d,0x0a,'  ',0
         cpu_brand  times 52 db 0
         cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0
         
         ; 指向tcb链表首个元素线性地址
         tcb_chain        dd  0
         core_next_laddr  dd  0x80100000           
         program_man_tss  dd  0   
                          ; 存储内核tss段选择子          
                          dw  0

core_data_end:
               
SECTION core_code vstart=0
; 在用户任务ldt区域安装新的描述符
; cx是安装描述符的选择子
fill_descriptor_in_ldt:     
         push eax
         push edx
         push edi
         push ds

         mov ecx,mem_0_4_gb_seg_sel
         mov ds,ecx
         ; ldt基地址
         mov edi,[ebx+0x0c]                
         
         xor ecx,ecx
         ; ldt界限
         mov cx,[ebx+0x0a]                 
         inc cx                           
         
         mov [edi+ecx+0x00],eax
         mov [edi+ecx+0x04],edx           

         add cx,8                           
         dec cx                            
         mov [ebx+0x0a],cx                

         mov ax,cx
         xor dx,dx
         mov cx,8
         div cx
         
         mov cx,ax
         shl cx,3                           
         or cx,0000_0000_0000_0100B        

         pop ds
         pop edi
         pop edx
         pop eax
         ret
; 为用户任务划分虚拟空间
; 将用户任务从磁盘加载到物理内存
; 为用户任务构建段安装到用户任务的ldt
; 为用户任务完成符号重定位
; 为用户任务分配tss,构建段,安装到gdt
; 为用户任务tss各个字段填充有意义的值
; 填充tss中还涉及3个特权级栈的空间分配,描述符构建,ldt中安装
; 为用户任务分配新的物理页作为用户任务的页目录表
load_relocate_program:                      
         ; EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI 
         pushad
         push ds
         push es
      
         mov ebp,esp                       
         mov ecx,mem_0_4_gb_seg_sel
         mov es,ecx

         ; 内核页目录表前512项设置为0x0000 0000
         mov ebx,0xfffff000
         xor esi,esi
  .b1:
         mov dword [es:ebx+esi*4],0x00000000
         inc esi
         cmp esi,512
         jl .b1
       
         mov eax,core_data_seg_sel
         mov ds,eax                        
         mov eax,[ebp+12*4]                
         mov ebx,core_buf                
         call sys_routine_seg_sel:read_hard_disk_0
         
         ; 用户任务尺寸
         mov eax,[core_buf]               
         mov ebx,eax
         and ebx,0xfffff000                
         add ebx,0x1000                        
         test eax,0x00000fff               
         ; 保证eax中的尺寸是4kb的倍数
         mov eax,ebx                    
         mov ecx,eax


         ; 双层循环
         ; 外层循环控制用户任务虚拟空间4kb可用区域的分配
         ; 内存循环控制连续拷贝磁盘扇区内容到4kb虚拟区域
         shr ecx,12                         
         mov eax,mem_0_4_gb_seg_sel
         mov ds,eax
         mov eax,[ebp+12*4]
         mov esi,[ebp+11*4]   
  .b2:
         ; 从用户任务虚拟区域分配可用4kb
         mov ebx,[es:esi+0x06]   
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         
         ; 连续读取8个扇区,共计4kb
         push ecx
         mov ecx,8
  .b3:
         call sys_routine_seg_sel:read_hard_disk_0
         inc eax
         loop .b3

         pop ecx
         loop .b2
         

         ; 现在用户任务位于内存---占据物理页
         ; 访问通过用户任务线性地址访问
         mov eax,core_data_seg_sel         
         mov ds,eax

         mov ebx,[core_next_laddr]  
         call sys_routine_seg_sel:alloc_inst_a_page
         ; 在内核空间分配用户任务tss
         add dword [core_next_laddr],4096
         
         ; TCB对象结构
         ; 0x44      2字节头部选择子
         ; 0x40      2特权级栈的初始ESP
         ; 0x3e      2特权级栈选择子
         ; 0x3a      2特权级栈基地址
         ; 0x36      2特权级栈以4KB为单位的长度
         ; 0x32      1特权级栈的初始ESP
         ; 0x30      1特权级栈选择子
         ; 0x2c      1特权级栈基地址
         ; 0x28      1特权级栈以4kb为单位的长度
         ; 0x24      0特权级栈的初始ESP
         ; 0x22      0特权级栈选择子
         ; 0x1e      0特权级栈基地址
         ; 0x1a      0特权级栈以4kb为单位的长度
         ; 0x18      tss选择子
         ; 0x14      tss基地址
         ; 0x12      tss界限值
         ; 0x10      ldt选择子
         ; 0x0c      ldt基地址
         ; 0x0a      ldt当前界限值
         ; 0x06      程序加载基地址
         ; 0x04      任务状态
         ; 0x00      下一个tcb基地址
         
         ; 在用户任务tcb中记录任务tss基地址
         mov [es:esi+0x14],ebx    
         ; 在用户任务tcb中记录任务tss界限值
         mov word [es:esi+0x12],103         

         ; 在用户任务局部空间分配任务的ldt 
         mov ebx,[es:esi+0x06]             
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         ; 在用户任务tcb中记录任务ldt基地址
         mov [es:esi+0x0c],ebx              
         
         ; TSS格式
         ; I/OMapAddr       Reserved      100
         ; Reserved         LDT_Sector    96
         ; Reserved         GS            92
         ; Reserved         FS            88
         ; Reserved         DS            84
         ; Reserved         SS            80
         ; Reserved         CS            76
         ; Reserved         ES            72
         ; EDI                            68
         ; ESI                            64
         ; EBP                            60
         ; ESP                            56
         ; EBX                            52
         ; EDX                            48
         ; ECX                            44
         ; EAX                            40
         ; EFLAGS                         36
         ; EIP                            32
         ; CR3(PDBR)                      28
         ; Reserved         SS2           24
         ; ESP2                           20
         ; Reserved         SS1           16
         ; ESP1                           12
         ; Reserved         SS0           8
         ; ESP0                           4
         ; Reserved         Pre_Task_Tss  0 
         ; 在用户任务tss对象里设置cs   

         ; 为用户任务代码段构造描述符
         ; 在用户任务tss中记录代码段选择子
         mov eax,0x00000000
         mov ebx,0x000fffff
         ; 4kb粒度 32位尺寸 段在内存 特权级3 非系统段 代码段                 
         mov ecx,0x00c0f800    
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi   
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B        
         mov ebx,[es:esi+0x14]    
         mov [es:ebx+76],cx                

         ; 为用户任务数据段构造描述符,在用户任务tss中记录数据段选择子
         mov eax,0x00000000
         mov ebx,0x000fffff  
         ; 4kb粒度 32位尺寸 段在内存 特权级3 非系统段 可读可写数据段               
         mov ecx,0x00c0f200        
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi  
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         
         mov ebx,[es:esi+0x14] 
         mov [es:ebx+84],cx                 
         mov [es:ebx+72],cx   
         mov [es:ebx+88],cx  
         mov [es:ebx+92],cx                
         
         ; 在用户任务虚拟区域分配4kb用于栈段
         mov ebx,[es:esi+0x06]             
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         ; 在用户任务tss中记录栈段选择子,栈的esp
         mov ebx,[es:esi+0x14]   
         mov [es:ebx+80],cx                 
         mov edx,[es:esi+0x06]  
         mov [es:ebx+56],edx               
         
         ; 在用户任务虚拟空间分配4kb可用空间用于特权级0栈段
         ; 为特权级0栈段构建描述符,安装于用户任务ldt
         ; 在用户任务tss中记录特权级0栈段选择子,栈的esp
         mov ebx,[es:esi+0x06]             
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         mov eax,0x00000000
         mov ebx,0x000fffff
         ; 4kb计量 32位 段位于内存 特权级0 非系统段 可读可写数据段
         mov ecx,0x00c09200                 
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0000B         
         mov ebx,[es:esi+0x14]  
         mov [es:ebx+8],cx                 
         mov edx,[es:esi+0x06]  
         mov [es:ebx+4],edx                
         
         ; 在用户任务虚拟空间分配4kb可用空间用于特权级1栈段
         ; 为特权级1栈段构建描述符,安装于用户任务ldt
         ; 在用户任务tss中记录特权级1栈段选择子,栈的esp
         mov ebx,[es:esi+0x06]             
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         mov eax,0x00000000
         mov ebx,0x000fffff
         ; 4kb计量 32位 段在内存 特权级1 非系统段 可读可写数据段
         mov ecx,0x00c0b200                 
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                       
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0001B        
         mov ebx,[es:esi+0x14]
         mov [es:ebx+16],cx                
         mov edx,[es:esi+0x06]             
         mov [es:ebx+12],edx               
         
         ; 在用户任务虚拟空间分配4kb可用空间用于特权级1栈段
         ; 为特权级1栈段构建描述符,安装于用户任务ldt
         ; 在用户任务tss中记录特权级1栈段选择子,栈的esp
         mov ebx,[es:esi+0x06]              
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         mov eax,0x00000000
         mov ebx,0x000fffff
         ; 4kb计量 32位 段在内存 特权级2 非系统段 可读可写数据段
         mov ecx,0x00c0d200                
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                       
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0010B        
         mov ebx,[es:esi+0x14]    
         mov [es:ebx+24],cx                 
         mov edx,[es:esi+0x06]    
         mov [es:ebx+20],edx      

         mov eax,mem_0_4_gb_seg_sel       
         mov es,eax                         
         mov eax,core_data_seg_sel
         mov ds,eax
       
         ; 双层循环,完成用户任务内每个符号的重新定位
         ; 每个符号定位后,门选择子里请求特权级是3
         cld
         mov ecx,[es:0x0c]                 
         mov edi,[es:0x08]                 
  .b4:
         push ecx
         push edi
         mov ecx,salt_items
         mov esi,salt
  .b5:
         push edi
         push esi
         push ecx
         
         mov ecx,64                         
         repe cmpsd               
         jnz .b6
         ; 这里是匹配了
         ; ds:esi指向内核符号4字节段内偏移
         mov eax,[esi] 
         ; 设置用户任务符号头4字节                    
         mov [es:edi-256],eax             
         ; 取得内核符号的门选择子 
         mov ax,[esi+4]
         or ax,0000000000000011B            
         ; 用户任务中符号里门选择子的请求特权级是3
         mov [es:edi-252],ax               
  .b6:
         pop ecx
         pop esi
         add esi,salt_item_len
         pop edi                           
         loop .b5
      
         pop edi
         add edi,256
         pop ecx
         loop .b4

         mov esi,[ebp+11*4]   
         ; ldt基地址         
         mov eax,[es:esi+0x0c]
         ; ldt当前界限             
         movzx ebx,word [es:esi+0x0a]      
         ; 字节计量 32位 段在内存 0特权级 系统段 可读可写数据段
         mov ecx,0x00408200
         ; 将ldt段安装到gdt          
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         ; tcb对象中设置ldt选择子
         mov [es:esi+0x10],cx              
         
         ; tss基地址
         mov ebx,[es:esi+0x14]   
         ; 在用户任务tss对象里设置用户任务ldt选择子
         mov [es:ebx+96],cx                
         
         ; 在用户任务tss对象里设置前一任务选择子为0x0000
         mov word [es:ebx+0],0            
         
         ; tss界限值
         mov dx,[es:esi+0x12]
         ; 用户任务tss中设置i/o map信息
         mov [es:ebx+102],dx               
         mov word [es:ebx+100],0           
         
         ; 用户任务入口点偏移
         mov eax,[es:0x04]   
         ; 在用户任务tss中设置eip---入口点偏移
         mov [es:ebx+32],eax                
         
         ; pushfd:然后将32位标志寄存器EFLAGS压入堆栈
         pushfd
         ; 出栈32位标志寄存器内容存储到edx
         pop edx
         ; 在用户任务tss中记录eflags
         mov [es:ebx+36],edx               
         
         ; 为用户任务tss构建段描述符,安装到gdt
         mov eax,[es:esi+0x14]  
         movzx ebx,word [es:esi+0x12]  
         ; 字节计量 32位 段在内存 0特权级 系统段 代码段且已经访问     
         mov ecx,0x00408900   
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         ; 在用户任务tcb对象记录用户任务tss选择子
         mov [es:esi+0x18],cx               
         
         ; 分配4kb物理页
         ; 将页目录表拷贝到新的物理页
         call sys_routine_seg_sel:create_copy_cur_pdir
         mov ebx,[es:esi+0x14]    
         ; 在用户任务tss中记录cr3---用户任务页目录表起始物理地址 
         mov dword [es:ebx+28],eax  
         pop es                            
         pop ds                            
         popad
         ret 8                              

; es:ecx是tcb对象起始线性地址   
; 链表尾部插入,先找到链表尾部tcb,然后将传入tcb插入到尾部
append_to_tcb_link:   
         push eax
         push edx
         push ds
         push es
         
         mov eax,core_data_seg_sel
         mov ds,eax
         mov eax,mem_0_4_gb_seg_sel
         mov es,eax
         ; tcb偏移0x00处设置
         mov dword [es: ecx+0x00],0
         mov eax,[tcb_chain]              
         or eax,eax                         
         jz .notcb 
         
  .searc:
         mov edx,eax
         mov eax,[es: edx+0x00]
         or eax,eax               
         ; 不是0,继续搜索下一个tcb对象
         jnz .searc
         mov [es: edx+0x00],ecx
         jmp .retpc
         
  .notcb:       
         mov [tcb_chain],ecx               
         
  .retpc:
         pop es
         pop ds
         pop edx
         pop eax
         ret
         
start:
         mov ecx,core_data_seg_sel    
         mov ds,ecx
         mov ecx,mem_0_4_gb_seg_sel
         mov es,ecx
         
         mov ebx,message_0                    
         call sys_routine_seg_sel:put_string

         mov eax,0x80000002
         cpuid
         mov [cpu_brand + 0x00],eax
         mov [cpu_brand + 0x04],ebx
         mov [cpu_brand + 0x08],ecx
         mov [cpu_brand + 0x0c],edx
         
         mov eax,0x80000003
         cpuid
         mov [cpu_brand + 0x10],eax
         mov [cpu_brand + 0x14],ebx
         mov [cpu_brand + 0x18],ecx
         mov [cpu_brand + 0x1c],edx
         
         mov eax,0x80000004
         cpuid
         mov [cpu_brand + 0x20],eax
         mov [cpu_brand + 0x24],ebx
         mov [cpu_brand + 0x28],ecx
         mov [cpu_brand + 0x2c],edx
         
         mov ebx,cpu_brnd0                 
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brand
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brnd1
         call sys_routine_seg_sel:put_string
         
         ; 页目录表,页表中每项内的地址是物理地址信息。其他的地址在分页下都是虚拟地址。                      
         mov ecx,1024
         ; 划出物理地址0x00020000开始的4kb区域用作页目录表
         mov ebx,0x00020000
         xor esi,esi
  .b1:
         mov dword [es:ebx+esi],0x00000000  
         add esi,4
         loop .b1

         ; 这里将索引为1023的项赋值。其中的物理地址指向页目录表自身。         
         mov dword [es:ebx+4092],0x00020003 

         ; 这里将索引为0的项复制。其中的物理地址指向页表。
         ; 一个页表含1024项。每项指向一个4kb物理页。一个页表指向物理页尺寸之和为4mb。         
         mov dword [es:ebx+0],0x00021003         
         
         ; 设置索引0页表前256项的物理地址
         ; 也即设置了虚拟地址前1mb内线性地址的物理地址。
         ; 特点是线性地址和映射后物理地址数值一致
         mov ebx,0x00021000            
         xor eax,eax
         xor esi,esi
  .b2:       
         mov edx,eax
         or edx,0x00000003             
         mov [es:ebx+esi*4],edx
         add eax,0x1000                    
         inc esi
         cmp esi,256                        
         jl .b2
         

         ; 虚拟地址32位---》物理地址32位
         ; 高10位 中间10位 低12位
         ; 高10位作为索引从页目录表得到页表物理地址
         ; 中间10位作为索引从页表得到物理地址A
         ; 物理地址A+低12位得到最终物理地址
  .b3:               
         mov dword [es:ebx+esi*4],0x00000000
         inc esi
         cmp esi,1024
         jl .b3 
         
         ; 通知处理器页目录表物理地址
         mov eax,0x00020000                
         mov cr3,eax
        
         mov eax,cr0
         or eax,0x80000000
         ; 开启分页机制
         mov cr0,eax                      

         ; 在为内核任务构建了合适的页目录表,页表后即可开启分页机制
         
         ; 每个任务有自己的代码和数据,同时需要依赖内核例程,依赖内核存放任务控制对象
         ; 所以设计上,
         ; 任务低2GB虚拟内存映射到任务的页表,以便存储任务局部代码和数据。
         ; 任务高2GB虚拟内存映射到内核的页表,以便存储内核全局代码和数据。

         ; 下面,把内核各个段本来虚拟地址位于低2GB的,
         ; 通过修改页目标表来达到将其线性地址映射调整到高2GB的作用
         mov ebx,0xfffff000   
         mov esi,0x80000000  
         ; 0b0000 0000 0000 0000 0000 00 1000 0000 00
         shr esi,22              
         ; 0b 0000 0000 0000 0000 0000 1000 0000 0000
         shl esi,2
         ; 这样页目录表第0项和第512项指向同一个页表
         mov dword [es:ebx+esi],0x00021003  
         ; 从处理器得到gdt的界限和基地址
         sgdt [pgdt]

         mov ebx,[pgdt+2]
         ; gdt索引2的描述符(64位)最高位设置为1
         or dword [es:ebx+0x10+4],0x80000000
         ; gdt索引3的描述符(64位)最高位设置为1
         or dword [es:ebx+0x18+4],0x80000000
         ; gdt索引4的描述符(64位)最高位设置为1
         or dword [es:ebx+0x20+4],0x80000000
         ; gdt索引5的描述符(64位)最高位设置为1
         or dword [es:ebx+0x28+4],0x80000000
         ; gdt索引6的描述符(64位)最高位设置为1
         or dword [es:ebx+0x30+4],0x80000000
         ; gdt索引7的描述符(64位)最高位设置为1
         or dword [es:ebx+0x38+4],0x80000000
         add dword [pgdt+2],0x80000000  
         ; 这样原来的gdt基地址及安装在gdt内各个段的线性地址相比原来增加了0x8000 0000
         ; 成功将低2GB内线性地址在原有基础上增加了2GB
         lgdt [pgdt]
         ; 跳转,可以更新cs
         jmp core_code_seg_sel:flush       
                                             
   flush:
         mov eax,core_stack_seg_sel
         mov ss,eax
         mov eax,core_data_seg_sel
         mov ds,eax
          
         mov ebx,message_1
         call sys_routine_seg_sel:put_string
         
         mov edi,salt            
         mov ecx,salt_items                  
  .b4:
         push ecx  
         mov eax,[edi+256]  
         mov bx,[edi+260] 
         mov cx,1_11_0_1100_000_00000B
         ; 内核每个符号,在gdt中安装了一个对应的描述符---门描述符
         call sys_routine_seg_sel:make_gate_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         ; 符号本来存储段选择子的,现在调整为存储门选择子
         mov [edi+260],cx                   
         add edi,salt_item_len              
         pop ecx
         loop .b4
         
         mov ebx,message_2
         call far [salt_1+256]              
          
         mov ebx,[core_next_laddr]
         ; 要划分一块4kb可用虚拟区域需要
         ; 找到页目目录表对应项,
         ; 若项指向了页表,则寻找并分配物理页,在页表对应项注册物理页
         ; 若项未指向页表,则寻找并分配物理页作为页表,页表初始化,在页目录表对应项注册页表
         ; 再寻找并分配物理页,在页表对应项注册物理页
         call sys_routine_seg_sel:alloc_inst_a_page
         add dword [core_next_laddr],4096
         
         ; 刚刚分配的区域用于存放内核任务的tss
         ; TSS格式
         ; I/OMapAddr       Reserved      100
         ; Reserved         LDT_Sector    96
         ; Reserved         GS            92
         ; Reserved         FS            88
         ; Reserved         DS            84
         ; Reserved         SS            80
         ; Reserved         CS            76
         ; Reserved         ES            72
         ; EDI                            68
         ; ESI                            64
         ; EBP                            60
         ; ESP                            56
         ; EBX                            52
         ; EDX                            48
         ; ECX                            44
         ; EAX                            40
         ; EFLAGS                         36
         ; EIP                            32
         ; CR3(PDBR)                      28
         ; Reserved         SS2           24
         ; ESP2                           20
         ; Reserved         SS1           16
         ; ESP1                           12
         ; Reserved         SS0           8
         ; ESP0                           4
         ; Reserved         Pre_Task_Tss  0 

         ; 设置迁移任务的tss选择子,这里为0x0000
         mov word [es:ebx+0],0           
         mov eax,cr3
         ; 任务tss里设置cr3---任务页目录表
         mov dword [es:ebx+28],eax        
         ; ldt选择子  
         mov word [es:ebx+96],0
         ; 预留
         mov word [es:ebx+100],0
         ; 意思就是I/O Map区域不存在
         mov word [es:ebx+102],103    

         ; 内核tss是一个段      
         mov eax,ebx     
         mov ebx,103
         ; 字节计量 32位 段在内存 特权级0 系统段 代码段被访问过
         mov ecx,0x00408900
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [program_man_tss+4],cx        
         ltr cx

         mov ebx,[core_next_laddr]
         call sys_routine_seg_sel:alloc_inst_a_page
         add dword [core_next_laddr],4096
         ; TCB对象结构
         ; 0x44      2字节头部选择子
         ; 0x40      2特权级栈的初始ESP
         ; 0x3e      2特权级栈选择子
         ; 0x3a      2特权级栈基地址
         ; 0x36      2特权级栈以4KB为单位的长度
         ; 0x32      1特权级栈的初始ESP
         ; 0x30      1特权级栈选择子
         ; 0x2c      1特权级栈基地址
         ; 0x28      1特权级栈以4kb为单位的长度
         ; 0x24      0特权级栈的初始ESP
         ; 0x22      0特权级栈选择子
         ; 0x1e      0特权级栈基地址
         ; 0x1a      0特权级栈以4kb为单位的长度
         ; 0x18      tss选择子
         ; 0x14      tss基地址
         ; 0x12      tss界限值
         ; 0x10      ldt选择子
         ; 0x0c      ldt基地址
         ; 0x0a      ldt当前界限值
         ; 0x06      程序加载基地址
         ; 0x04      任务状态
         ; 0x00      下一个tcb基地址

         ; tcb中任务加载基地址
         mov dword [es:ebx+0x06],0
         ; 任务ldt界限值
         mov word [es:ebx+0x0a],0xffff

         ; 加入到tcb链表
         mov ecx,ebx
         call append_to_tcb_link           
         
         push dword 50                     
         push ecx     
         ; 用户任务重新定位 
         call load_relocate_program   

         mov ebx,message_4
         call sys_routine_seg_sel:put_string
         
         ; 通过用户任务tss基地址 tss选择子实现任务切换
         call far [es:ecx+0x14]   
                   
         mov ebx,message_5
         call sys_routine_seg_sel:put_string
         hlt
core_code_end:

SECTION core_trail
core_end:

; 基于页表,页目录表实现分页,分页和虚拟内存机制是一个东西
; 基于tss实现多任务切换,任务管理
; 这里的tcb对象尚未被处理器使用

 3.用户任务

         program_length   dd program_end   
         entry_point      dd start
         salt_position    dd salt_begin
         salt_items       dd (salt_end-salt_begin)/256 
         salt_begin:                                     
         PrintString      db  '@PrintString'
                     times 256-($-PrintString) db 0
         TerminateProgram db  '@TerminateProgram'
                     times 256-($-TerminateProgram) db 0
         reserved  times 256*500 db 0           
         ReadDiskData     db  '@ReadDiskData'
                     times 256-($-ReadDiskData) db 0
         PrintDwordAsHex  db  '@PrintDwordAsHexString'
                     times 256-($-PrintDwordAsHex) db 0
         salt_end:
         message_0        db  0x0d,0x0a,
                          db  '  ............User task is running with '
                          db  'paging enabled!............',0x0d,0x0a,0
         space            db  0x20,0x20,0
      [bits 32]
start:
         mov ebx,message_0
         ; 基于段内偏移+门选择子进行例程调用
         call far [PrintString]
         xor esi,esi
         mov ecx,88
  .b1: 
         mov ebx,space
         ; 基于段内偏移+门选择子进行例程调用
         call far [PrintString] 
         ; 用户任务虚拟区域起始一段内容按16进制打印
         mov edx,[esi*4]
         call far [PrintDwordAsHex]
         inc esi
         loop .b1 

         call far [TerminateProgram]             
program_end:

猜你喜欢

转载自blog.csdn.net/x13262608581/article/details/126319421