请选择 进入手机版 | 继续访问电脑版

4Ameta

 找回密码
 立即注册
搜索
查看: 66|回复: 0

Learn x86 protected mode assembly language-c13.asm

[复制链接]

26

主题

26

帖子

119

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119
发表于 2022-9-12 21:07:46 | 显示全部楼层 |阅读模式
  1.          ;代码清单13-3
  2.          ;文件名:c13.asm
  3.          ;文件说明:用户程序
  4.          ;创建日期:2011-10-30 15:19   
  5.          
  6. ;===============================================================================
  7. SECTION header vstart=0

  8.          program_length   dd program_end          ;程序总长度#0x00
  9.          
  10.          head_len         dd header_end           ;程序头部的长度#0x04

  11.          stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
  12.          stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
  13.                                                   ;以4KB为单位
  14.                                                   
  15.          prgentry         dd start                ;程序入口#0x10
  16.          code_seg         dd section.code.start   ;代码段位置#0x14
  17.          code_len         dd code_end             ;代码段长度#0x18

  18.          data_seg         dd section.data.start   ;数据段位置#0x1c
  19.          data_len         dd data_end             ;数据段长度#0x20
  20.             
  21. ;-------------------------------------------------------------------------------
  22.          ;符号地址检索表
  23.          salt_items       dd (header_end-salt)/256 ;#0x24
  24.          
  25.          salt:                                     ;#0x28
  26.          PrintString      db  '@PrintString'
  27.                      times 256-($-PrintString) db 0
  28.                      
  29.          TerminateProgram db  '@TerminateProgram'
  30.                      times 256-($-TerminateProgram) db 0
  31.                      
  32.          ReadDiskData     db  '@ReadDiskData'
  33.                      times 256-($-ReadDiskData) db 0
  34.                  
  35. header_end:

  36. ;===============================================================================
  37. SECTION data vstart=0   
  38.                         
  39.          buffer times 1024 db  0         ;缓冲区

  40.          message_1         db  0x0d,0x0a,0x0d,0x0a
  41.                            db  '**********User program is runing**********'
  42.                            db  0x0d,0x0a,0
  43.          message_2         db  '  Disk data:',0x0d,0x0a,0

  44. data_end:

  45. ;===============================================================================
  46.       [bits 32]
  47. ;===============================================================================
  48. SECTION code vstart=0
  49. start:
  50.          mov eax,ds
  51.          mov fs,eax
  52.      
  53.          mov eax,[stack_seg]
  54.          mov ss,eax
  55.          mov esp,0
  56.      
  57.          mov eax,[data_seg]
  58.          mov ds,eax
  59.      
  60.          mov ebx,message_1
  61.          call far [fs:PrintString]
  62.      
  63.          mov eax,100                         ;逻辑扇区号100
  64.          mov ebx,buffer                      ;缓冲区偏移地址
  65.          call far [fs:ReadDiskData]          ;段间调用
  66.      
  67.          mov ebx,message_2
  68.          call far [fs:PrintString]
  69.      
  70.          mov ebx,buffer
  71.          call far [fs:PrintString]           ;too.
  72.      
  73.          jmp far [fs:TerminateProgram]       ;将控制权返回到系统
  74.       
  75. code_end:

  76. ;===============================================================================
  77. SECTION trail
  78. ;-------------------------------------------------------------------------------
  79. program_end:
复制代码



  1.          ;代码清单13-3
  2.          ;文件名:c13.asm
  3.          ;文件说明:用户程序
  4.          ;创建日期:2011-10-30 15:19   
  5.          
  6. ;===============================================================================
  7. SECTION header vstart=0

  8.          program_length   dd program_end          ;程序总长度#0x00
  9.          
  10.          head_len         dd header_end           ;程序头部的长度#0x04

  11.          stack_seg        dd 0                    ;用于接收堆栈段选择子#0x08
  12.          stack_len        dd 1                    ;程序建议的堆栈大小#0x0c
  13.                                                   ;以4KB为单位
  14.                                                   
  15.          prgentry         dd start                ;程序入口#0x10
  16.          code_seg         dd section.code.start   ;代码段位置#0x14
  17.          code_len         dd code_end             ;代码段长度#0x18

  18.          data_seg         dd section.data.start   ;数据段位置#0x1c
  19.          data_len         dd data_end             ;数据段长度#0x20
  20.             
  21. ;-------------------------------------------------------------------------------
  22.          ;符号地址检索表
  23.          salt_items       dd (header_end-salt)/256 ;#0x24
  24.          
  25.          salt:                                     ;#0x28
  26.          PrintString      db  '@PrintString'
  27.                      times 256-($-PrintString) db 0
  28.                      
  29.          TerminateProgram db  '@TerminateProgram'
  30.                      times 256-($-TerminateProgram) db 0
  31.                      
  32.          ReadDiskData     db  '@ReadDiskData'
  33.                      times 256-($-ReadDiskData) db 0
  34.                  
  35. header_end:

  36. ;===============================================================================
  37. SECTION data vstart=0   
  38.                         
  39.          buffer times 1024 db  0         ;缓冲区

  40.          message_1         db  0x0d,0x0a,0x0d,0x0a
  41.                            db  '**********User program is runing**********'
  42.                            db  0x0d,0x0a,0
  43.          message_2         db  '  Disk data:',0x0d,0x0a,0

  44. data_end:

  45. ;===============================================================================
  46.       [bits 32]
  47. ;===============================================================================
  48. SECTION code vstart=0
  49. start:
  50.          mov eax,ds
  51.          mov fs,eax
  52.      
  53.          mov eax,[stack_seg]
  54.          mov ss,eax
  55.          mov esp,0
  56.      
  57.          mov eax,[data_seg]
  58.          mov ds,eax
  59.      
  60.          mov ebx,message_1
  61.          call far [fs:PrintString]
  62.      
  63.          mov eax,100                         ;逻辑扇区号100
  64.          mov ebx,buffer                      ;缓冲区偏移地址
  65.          call far [fs:ReadDiskData]          ;段间调用
  66.      
  67.          mov ebx,message_2
  68.          call far [fs:PrintString]
  69.      
  70.          mov ebx,buffer
  71.          call far [fs:PrintString]           ;too.
  72.      
  73.          jmp far [fs:TerminateProgram]       ;将控制权返回到系统
  74.       
  75. code_end:

  76. ;===============================================================================
  77. SECTION trail
  78. ;-------------------------------------------------------------------------------
  79. program_end:
复制代码




  1.          ;代码清单13-2
  2.          ;文件名:c13_core.asm
  3.          ;文件说明:保护模式微型核心程序
  4.          ;创建日期:2011-10-26 12:11

  5.          ;以下常量定义部分。内核的大部分内容都应当固定
  6.          core_code_seg_sel     equ  0x38    ;内核代码段选择子
  7.          core_data_seg_sel     equ  0x30    ;内核数据段选择子
  8.          sys_routine_seg_sel   equ  0x28    ;系统公共例程代码段的选择子
  9.          video_ram_seg_sel     equ  0x20    ;视频显示缓冲区的段选择子
  10.          core_stack_seg_sel    equ  0x18    ;内核堆栈段选择子
  11.          mem_0_4_gb_seg_sel    equ  0x08    ;整个0-4GB内存的段的选择子

  12. ;-------------------------------------------------------------------------------
  13.          ;以下是系统核心的头部,用于加载核心程序
  14.          core_length      dd core_end       ;核心程序总长度#00

  15.          sys_routine_seg  dd section.sys_routine.start
  16.                                             ;系统公用例程段位置#04

  17.          core_data_seg    dd section.core_data.start
  18.                                             ;核心数据段位置#08

  19.          core_code_seg    dd section.core_code.start
  20.                                             ;核心代码段位置#0c


  21.          core_entry       dd start          ;核心代码段入口点#10
  22.                           dw core_code_seg_sel

  23. ;===============================================================================
  24.          [bits 32]
  25. ;===============================================================================
  26. SECTION sys_routine vstart=0                ;系统公共例程代码段
  27. ;-------------------------------------------------------------------------------
  28.          ;字符串显示例程
  29. put_string:                                 ;显示0终止的字符串并移动光标
  30.                                             ;输入:DS:EBX=串地址
  31.          push ecx
  32.   .getc:
  33.          mov cl,[ebx]
  34.          or cl,cl
  35.          jz .exit
  36.          call put_char
  37.          inc ebx
  38.          jmp .getc

  39.   .exit:
  40.          pop ecx
  41.          retf                               ;段间返回

  42. ;-------------------------------------------------------------------------------
  43. put_char:                                   ;在当前光标处显示一个字符,并推进
  44.                                             ;光标。仅用于段内调用
  45.                                             ;输入:CL=字符ASCII码
  46.          pushad

  47.          ;以下取当前光标位置
  48.          mov dx,0x3d4
  49.          mov al,0x0e
  50.          out dx,al
  51.          inc dx                             ;0x3d5
  52.          in al,dx                           ;高字
  53.          mov ah,al

  54.          dec dx                             ;0x3d4
  55.          mov al,0x0f
  56.          out dx,al
  57.          inc dx                             ;0x3d5
  58.          in al,dx                           ;低字
  59.          mov bx,ax                          ;BX=代表光标位置的16位数

  60.          cmp cl,0x0d                        ;回车符?
  61.          jnz .put_0a
  62.          mov ax,bx
  63.          mov bl,80
  64.          div bl
  65.          mul bl
  66.          mov bx,ax
  67.          jmp .set_cursor

  68.   .put_0a:
  69.          cmp cl,0x0a                        ;换行符?
  70.          jnz .put_other
  71.          add bx,80
  72.          jmp .roll_screen

  73.   .put_other:                               ;正常显示字符
  74.          push es
  75.          mov eax,video_ram_seg_sel          ;0xb8000段的选择子
  76.          mov es,eax
  77.          shl bx,1
  78.          mov [es:bx],cl
  79.          pop es

  80.          ;以下将光标位置推进一个字符
  81.          shr bx,1
  82.          inc bx

  83.   .roll_screen:
  84.          cmp bx,2000                        ;光标超出屏幕?滚屏
  85.          jl .set_cursor

  86.          push ds
  87.          push es
  88.          mov eax,video_ram_seg_sel
  89.          mov ds,eax
  90.          mov es,eax
  91.          cld
  92.          mov esi,0xa0                       ;小心!32位模式下movsb/w/d
  93.          mov edi,0x00                       ;使用的是esi/edi/ecx
  94.          mov ecx,1920
  95.          rep movsd
  96.          mov bx,3840                        ;清除屏幕最底一行
  97.          mov ecx,80                         ;32位程序应该使用ECX
  98.   .cls:
  99.          mov word[es:bx],0x0720
  100.          add bx,2
  101.          loop .cls

  102.          pop es
  103.          pop ds

  104.          mov bx,1920

  105.   .set_cursor:
  106.          mov dx,0x3d4
  107.          mov al,0x0e
  108.          out dx,al
  109.          inc dx                             ;0x3d5
  110.          mov al,bh
  111.          out dx,al
  112.          dec dx                             ;0x3d4
  113.          mov al,0x0f
  114.          out dx,al
  115.          inc dx                             ;0x3d5
  116.          mov al,bl
  117.          out dx,al

  118.          popad
  119.          ret                                

  120. ;-------------------------------------------------------------------------------
  121. read_hard_disk_0:                           ;从硬盘读取一个逻辑扇区
  122.                                             ;EAX=逻辑扇区号
  123.                                             ;DS:EBX=目标缓冲区地址
  124.                                             ;返回:EBX=EBX+512
  125.          push eax
  126.          push ecx
  127.          push edx
  128.       
  129.          push eax
  130.          
  131.          mov dx,0x1f2
  132.          mov al,1
  133.          out dx,al                          ;读取的扇区数

  134.          inc dx                             ;0x1f3
  135.          pop eax
  136.          out dx,al                          ;LBA地址7~0

  137.          inc dx                             ;0x1f4
  138.          mov cl,8
  139.          shr eax,cl
  140.          out dx,al                          ;LBA地址15~8

  141.          inc dx                             ;0x1f5
  142.          shr eax,cl
  143.          out dx,al                          ;LBA地址23~16

  144.          inc dx                             ;0x1f6
  145.          shr eax,cl
  146.          or al,0xe0                         ;第一硬盘  LBA地址27~24
  147.          out dx,al

  148.          inc dx                             ;0x1f7
  149.          mov al,0x20                        ;读命令
  150.          out dx,al

  151.   .waits:
  152.          in al,dx
  153.          and al,0x88
  154.          cmp al,0x08
  155.          jnz .waits                         ;不忙,且硬盘已准备好数据传输

  156.          mov ecx,256                        ;总共要读取的字数
  157.          mov dx,0x1f0
  158.   .readw:
  159.          in ax,dx
  160.          mov [ebx],ax
  161.          add ebx,2
  162.          loop .readw

  163.          pop edx
  164.          pop ecx
  165.          pop eax
  166.       
  167.          retf                               ;段间返回

  168. ;-------------------------------------------------------------------------------
  169. ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助
  170. put_hex_dword:                              ;在当前光标处以十六进制形式显示
  171.                                             ;一个双字并推进光标
  172.                                             ;输入:EDX=要转换并显示的数字
  173.                                             ;输出:无
  174.          pushad
  175.          push ds
  176.       
  177.          mov ax,core_data_seg_sel           ;切换到核心数据段
  178.          mov ds,ax
  179.       
  180.          mov ebx,bin_hex                    ;指向核心数据段内的转换表
  181.          mov ecx,8
  182.   .xlt:   
  183.          rol edx,4
  184.          mov eax,edx
  185.          and eax,0x0000000f
  186.          xlat
  187.       
  188.          push ecx
  189.          mov cl,al                           
  190.          call put_char
  191.          pop ecx
  192.       
  193.          loop .xlt
  194.       
  195.          pop ds
  196.          popad
  197.          retf
  198.       
  199. ;-------------------------------------------------------------------------------
  200. allocate_memory:                            ;分配内存
  201.                                             ;输入:ECX=希望分配的字节数
  202.                                             ;输出:ECX=起始线性地址
  203.          push ds
  204.          push eax
  205.          push ebx
  206.       
  207.          mov eax,core_data_seg_sel
  208.          mov ds,eax
  209.       
  210.          mov eax,[ram_alloc]
  211.          add eax,ecx                        ;下一次分配时的起始地址
  212.       
  213.          ;这里应当有检测可用内存数量的指令
  214.          
  215.          mov ecx,[ram_alloc]                ;返回分配的起始地址

  216.          mov ebx,eax
  217.          and ebx,0xfffffffc
  218.          add ebx,4                          ;强制对齐
  219.          test eax,0x00000003                ;下次分配的起始地址最好是4字节对齐
  220.          cmovnz eax,ebx                     ;如果没有对齐,则强制对齐
  221.          mov [ram_alloc],eax                ;下次从该地址分配内存
  222.                                             ;cmovcc指令可以避免控制转移
  223.          pop ebx
  224.          pop eax
  225.          pop ds

  226.          retf

  227. ;-------------------------------------------------------------------------------
  228. set_up_gdt_descriptor:                      ;在GDT内安装一个新的描述符
  229.                                             ;输入:EDX:EAX=描述符
  230.                                             ;输出:CX=描述符的选择子
  231.          push eax
  232.          push ebx
  233.          push edx
  234.       
  235.          push ds
  236.          push es
  237.       
  238.          mov ebx,core_data_seg_sel          ;切换到核心数据段
  239.          mov ds,ebx

  240.          sgdt [pgdt]                        ;以便开始处理GDT

  241.          mov ebx,mem_0_4_gb_seg_sel
  242.          mov es,ebx

  243.          movzx ebx,word [pgdt]              ;GDT界限
  244.          inc bx                             ;GDT总字节数,也是下一个描述符偏移
  245.          add ebx,[pgdt+2]                   ;下一个描述符的线性地址
  246.       
  247.          mov [es:ebx],eax
  248.          mov [es:ebx+4],edx
  249.       
  250.          add word [pgdt],8                  ;增加一个描述符的大小   
  251.       
  252.          lgdt [pgdt]                        ;对GDT的更改生效
  253.       
  254.          mov ax,[pgdt]                      ;得到GDT界限值
  255.          xor dx,dx
  256.          mov bx,8
  257.          div bx                             ;除以8,去掉余数
  258.          mov cx,ax                          
  259.          shl cx,3                           ;将索引号移到正确位置

  260.          pop es
  261.          pop ds

  262.          pop edx
  263.          pop ebx
  264.          pop eax
  265.       
  266.          retf
  267. ;-------------------------------------------------------------------------------
  268. make_seg_descriptor:                        ;构造存储器和系统的段描述符
  269.                                             ;输入:EAX=线性基地址
  270.                                             ;      EBX=段界限
  271.                                             ;      ECX=属性。各属性位都在原始
  272.                                             ;          位置,无关的位清零
  273.                                             ;返回:EDX:EAX=描述符
  274.          mov edx,eax
  275.          shl eax,16
  276.          or ax,bx                           ;描述符前32位(EAX)构造完毕

  277.          and edx,0xffff0000                 ;清除基地址中无关的位
  278.          rol edx,8
  279.          bswap edx                          ;装配基址的31~24和23~16  (80486+)

  280.          xor bx,bx
  281.          or edx,ebx                         ;装配段界限的高4位

  282.          or edx,ecx                         ;装配属性

  283.          retf

  284. ;===============================================================================
  285. SECTION core_data vstart=0                  ;系统核心的数据段
  286. ;-------------------------------------------------------------------------------
  287.          pgdt             dw  0             ;用于设置和修改GDT
  288.                           dd  0

  289.          ram_alloc        dd  0x00100000    ;下次分配内存时的起始地址

  290.          ;符号地址检索表
  291.          salt:
  292.          salt_1           db  '@PrintString'
  293.                      times 256-($-salt_1) db 0
  294.                           dd  put_string
  295.                           dw  sys_routine_seg_sel

  296.          salt_2           db  '@ReadDiskData'
  297.                      times 256-($-salt_2) db 0
  298.                           dd  read_hard_disk_0
  299.                           dw  sys_routine_seg_sel

  300.          salt_3           db  '@PrintDwordAsHexString'
  301.                      times 256-($-salt_3) db 0
  302.                           dd  put_hex_dword
  303.                           dw  sys_routine_seg_sel

  304.          salt_4           db  '@TerminateProgram'
  305.                      times 256-($-salt_4) db 0
  306.                           dd  return_point
  307.                           dw  core_code_seg_sel

  308.          salt_item_len   equ $-salt_4
  309.          salt_items      equ ($-salt)/salt_item_len

  310.          message_1        db  '  If you seen this message,that means we '
  311.                           db  'are now in protect mode,and the system '
  312.                           db  'core is loaded,and the video display '
  313.                           db  'routine works perfectly.',0x0d,0x0a,0

  314.          message_5        db  '  Loading user program...',0
  315.          
  316.          do_status        db  'Done.',0x0d,0x0a,0
  317.          
  318.          message_6        db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
  319.                           db  '  User program terminated,control returned.',0

  320.          bin_hex          db '0123456789ABCDEF'
  321.                                             ;put_hex_dword子过程用的查找表
  322.          core_buf   times 2048 db 0         ;内核用的缓冲区

  323.          esp_pointer      dd 0              ;内核用来临时保存自己的栈指针     

  324.          cpu_brnd0        db 0x0d,0x0a,'  ',0
  325.          cpu_brand  times 52 db 0
  326.          cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0

  327. ;===============================================================================
  328. SECTION core_code vstart=0
  329. ;-------------------------------------------------------------------------------
  330. load_relocate_program:                      ;加载并重定位用户程序
  331.                                             ;输入:ESI=起始逻辑扇区号
  332.                                             ;返回:AX=指向用户程序头部的选择子
  333.          push ebx
  334.          push ecx
  335.          push edx
  336.          push esi
  337.          push edi
  338.       
  339.          push ds
  340.          push es
  341.       
  342.          mov eax,core_data_seg_sel
  343.          mov ds,eax                         ;切换DS到内核数据段
  344.       
  345.          mov eax,esi                        ;读取程序头部数据
  346.          mov ebx,core_buf                        
  347.          call sys_routine_seg_sel:read_hard_disk_0

  348.          ;以下判断整个程序有多大
  349.          mov eax,[core_buf]                 ;程序尺寸
  350.          mov ebx,eax
  351.          and ebx,0xfffffe00                 ;使之512字节对齐(能被512整除的数,
  352.          add ebx,512                        ;低9位都为0
  353.          test eax,0x000001ff                ;程序的大小正好是512的倍数吗?
  354.          cmovnz eax,ebx                     ;不是。使用凑整的结果
  355.       
  356.          mov ecx,eax                        ;实际需要申请的内存数量
  357.          call sys_routine_seg_sel:allocate_memory
  358.          mov ebx,ecx                        ;ebx -> 申请到的内存首地址
  359.          push ebx                           ;保存该首地址
  360.          xor edx,edx
  361.          mov ecx,512
  362.          div ecx
  363.          mov ecx,eax                        ;总扇区数
  364.       
  365.          mov eax,mem_0_4_gb_seg_sel         ;切换DS到0-4GB的段
  366.          mov ds,eax

  367.          mov eax,esi                        ;起始扇区号
  368.   .b1:
  369.          call sys_routine_seg_sel:read_hard_disk_0
  370.          inc eax
  371.          loop .b1                           ;循环读,直到读完整个用户程序

  372.          ;建立程序头部段描述符
  373.          pop edi                            ;恢复程序装载的首地址
  374.          mov eax,edi                        ;程序头部起始线性地址
  375.          mov ebx,[edi+0x04]                 ;段长度
  376.          dec ebx                            ;段界限
  377.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  378.          call sys_routine_seg_sel:make_seg_descriptor
  379.          call sys_routine_seg_sel:set_up_gdt_descriptor
  380.          mov [edi+0x04],cx                  

  381.          ;建立程序代码段描述符
  382.          mov eax,edi
  383.          add eax,[edi+0x14]                 ;代码起始线性地址
  384.          mov ebx,[edi+0x18]                 ;段长度
  385.          dec ebx                            ;段界限
  386.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  387.          call sys_routine_seg_sel:make_seg_descriptor
  388.          call sys_routine_seg_sel:set_up_gdt_descriptor
  389.          mov [edi+0x14],cx

  390.          ;建立程序数据段描述符
  391.          mov eax,edi
  392.          add eax,[edi+0x1c]                 ;数据段起始线性地址
  393.          mov ebx,[edi+0x20]                 ;段长度
  394.          dec ebx                            ;段界限
  395.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  396.          call sys_routine_seg_sel:make_seg_descriptor
  397.          call sys_routine_seg_sel:set_up_gdt_descriptor
  398.          mov [edi+0x1c],cx

  399.          ;建立程序堆栈段描述符
  400.          mov ecx,[edi+0x0c]                 ;4KB的倍率
  401.          mov ebx,0x000fffff
  402.          sub ebx,ecx                        ;得到段界限
  403.          mov eax,4096                        
  404.          mul dword [edi+0x0c]                        
  405.          mov ecx,eax                        ;准备为堆栈分配内存
  406.          call sys_routine_seg_sel:allocate_memory
  407.          add eax,ecx                        ;得到堆栈的高端物理地址
  408.          mov ecx,0x00c09600                 ;4KB粒度的堆栈段描述符
  409.          call sys_routine_seg_sel:make_seg_descriptor
  410.          call sys_routine_seg_sel:set_up_gdt_descriptor
  411.          mov [edi+0x08],cx

  412.          ;重定位SALT
  413.          mov eax,[edi+0x04]
  414.          mov es,eax                         ;es -> 用户程序头部
  415.          mov eax,core_data_seg_sel
  416.          mov ds,eax
  417.       
  418.          cld

  419.          mov ecx,[es:0x24]                  ;用户程序的SALT条目数
  420.          mov edi,0x28                       ;用户程序内的SALT位于头部内0x2c处
  421.   .b2:
  422.          push ecx
  423.          push edi
  424.       
  425.          mov ecx,salt_items
  426.          mov esi,salt
  427.   .b3:
  428.          push edi
  429.          push esi
  430.          push ecx

  431.          mov ecx,64                         ;检索表中,每条目的比较次数
  432.          repe cmpsd                         ;每次比较4字节
  433.          jnz .b4
  434.          mov eax,[esi]                      ;若匹配,esi恰好指向其后的地址数据
  435.          mov [es:edi-256],eax               ;将字符串改写成偏移地址
  436.          mov ax,[esi+4]
  437.          mov [es:edi-252],ax                ;以及段选择子
  438.   .b4:
  439.       
  440.          pop ecx
  441.          pop esi
  442.          add esi,salt_item_len
  443.          pop edi                            ;从头比较
  444.          loop .b3
  445.       
  446.          pop edi
  447.          add edi,256
  448.          pop ecx
  449.          loop .b2

  450.          mov ax,[es:0x04]

  451.          pop es                             ;恢复到调用此过程前的es段
  452.          pop ds                             ;恢复到调用此过程前的ds段
  453.       
  454.          pop edi
  455.          pop esi
  456.          pop edx
  457.          pop ecx
  458.          pop ebx
  459.       
  460.          ret
  461.       
  462. ;-------------------------------------------------------------------------------
  463. start:
  464.          mov ecx,core_data_seg_sel           ;使ds指向核心数据段
  465.          mov ds,ecx

  466.          mov ebx,message_1
  467.          call sys_routine_seg_sel:put_string
  468.                                          
  469.          ;显示处理器品牌信息
  470.          mov eax,0x80000002
  471.          cpuid
  472.          mov [cpu_brand + 0x00],eax
  473.          mov [cpu_brand + 0x04],ebx
  474.          mov [cpu_brand + 0x08],ecx
  475.          mov [cpu_brand + 0x0c],edx
  476.       
  477.          mov eax,0x80000003
  478.          cpuid
  479.          mov [cpu_brand + 0x10],eax
  480.          mov [cpu_brand + 0x14],ebx
  481.          mov [cpu_brand + 0x18],ecx
  482.          mov [cpu_brand + 0x1c],edx

  483.          mov eax,0x80000004
  484.          cpuid
  485.          mov [cpu_brand + 0x20],eax
  486.          mov [cpu_brand + 0x24],ebx
  487.          mov [cpu_brand + 0x28],ecx
  488.          mov [cpu_brand + 0x2c],edx

  489.          mov ebx,cpu_brnd0
  490.          call sys_routine_seg_sel:put_string
  491.          mov ebx,cpu_brand
  492.          call sys_routine_seg_sel:put_string
  493.          mov ebx,cpu_brnd1
  494.          call sys_routine_seg_sel:put_string

  495.          mov ebx,message_5
  496.          call sys_routine_seg_sel:put_string
  497.          mov esi,50                          ;用户程序位于逻辑50扇区
  498.          call load_relocate_program
  499.       
  500.          mov ebx,do_status
  501.          call sys_routine_seg_sel:put_string
  502.       
  503.          mov [esp_pointer],esp               ;临时保存堆栈指针
  504.       
  505.          mov ds,ax
  506.       
  507.          jmp far [0x10]                      ;控制权交给用户程序(入口点)
  508.                                              ;堆栈可能切换

  509. return_point:                                ;用户程序返回点
  510.          mov eax,core_data_seg_sel           ;使ds指向核心数据段
  511.          mov ds,eax

  512.          mov eax,core_stack_seg_sel          ;切换回内核自己的堆栈
  513.          mov ss,eax
  514.          mov esp,[esp_pointer]

  515.          mov ebx,message_6
  516.          call sys_routine_seg_sel:put_string

  517.          ;这里可以放置清除用户程序各种描述符的指令
  518.          ;也可以加载并启动其它程序
  519.       
  520.          hlt
  521.             
  522. ;===============================================================================
  523. SECTION core_trail
  524. ;-------------------------------------------------------------------------------
  525. core_end:
复制代码



  1.          ;代码清单13-2
  2.          ;文件名:c13_core.asm
  3.          ;文件说明:保护模式微型核心程序
  4.          ;创建日期:2011-10-26 12:11

  5.          ;以下常量定义部分。内核的大部分内容都应当固定
  6.          core_code_seg_sel     equ  0x38    ;内核代码段选择子
  7.          core_data_seg_sel     equ  0x30    ;内核数据段选择子
  8.          sys_routine_seg_sel   equ  0x28    ;系统公共例程代码段的选择子
  9.          video_ram_seg_sel     equ  0x20    ;视频显示缓冲区的段选择子
  10.          core_stack_seg_sel    equ  0x18    ;内核堆栈段选择子
  11.          mem_0_4_gb_seg_sel    equ  0x08    ;整个0-4GB内存的段的选择子

  12. ;-------------------------------------------------------------------------------
  13.          ;以下是系统核心的头部,用于加载核心程序
  14.          core_length      dd core_end       ;核心程序总长度#00

  15.          sys_routine_seg  dd section.sys_routine.start
  16.                                             ;系统公用例程段位置#04

  17.          core_data_seg    dd section.core_data.start
  18.                                             ;核心数据段位置#08

  19.          core_code_seg    dd section.core_code.start
  20.                                             ;核心代码段位置#0c


  21.          core_entry       dd start          ;核心代码段入口点#10
  22.                           dw core_code_seg_sel

  23. ;===============================================================================
  24.          [bits 32]
  25. ;===============================================================================
  26. SECTION sys_routine vstart=0                ;系统公共例程代码段
  27. ;-------------------------------------------------------------------------------
  28.          ;字符串显示例程
  29. put_string:                                 ;显示0终止的字符串并移动光标
  30.                                             ;输入:DS:EBX=串地址
  31.          push ecx
  32.   .getc:
  33.          mov cl,[ebx]
  34.          or cl,cl
  35.          jz .exit
  36.          call put_char
  37.          inc ebx
  38.          jmp .getc

  39.   .exit:
  40.          pop ecx
  41.          retf                               ;段间返回

  42. ;-------------------------------------------------------------------------------
  43. put_char:                                   ;在当前光标处显示一个字符,并推进
  44.                                             ;光标。仅用于段内调用
  45.                                             ;输入:CL=字符ASCII码
  46.          pushad

  47.          ;以下取当前光标位置
  48.          mov dx,0x3d4
  49.          mov al,0x0e
  50.          out dx,al
  51.          inc dx                             ;0x3d5
  52.          in al,dx                           ;高字
  53.          mov ah,al

  54.          dec dx                             ;0x3d4
  55.          mov al,0x0f
  56.          out dx,al
  57.          inc dx                             ;0x3d5
  58.          in al,dx                           ;低字
  59.          mov bx,ax                          ;BX=代表光标位置的16位数

  60.          cmp cl,0x0d                        ;回车符?
  61.          jnz .put_0a
  62.          mov ax,bx
  63.          mov bl,80
  64.          div bl
  65.          mul bl
  66.          mov bx,ax
  67.          jmp .set_cursor

  68.   .put_0a:
  69.          cmp cl,0x0a                        ;换行符?
  70.          jnz .put_other
  71.          add bx,80
  72.          jmp .roll_screen

  73.   .put_other:                               ;正常显示字符
  74.          push es
  75.          mov eax,video_ram_seg_sel          ;0xb8000段的选择子
  76.          mov es,eax
  77.          shl bx,1
  78.          mov [es:bx],cl
  79.          pop es

  80.          ;以下将光标位置推进一个字符
  81.          shr bx,1
  82.          inc bx

  83.   .roll_screen:
  84.          cmp bx,2000                        ;光标超出屏幕?滚屏
  85.          jl .set_cursor

  86.          push ds
  87.          push es
  88.          mov eax,video_ram_seg_sel
  89.          mov ds,eax
  90.          mov es,eax
  91.          cld
  92.          mov esi,0xa0                       ;小心!32位模式下movsb/w/d
  93.          mov edi,0x00                       ;使用的是esi/edi/ecx
  94.          mov ecx,1920
  95.          rep movsd
  96.          mov bx,3840                        ;清除屏幕最底一行
  97.          mov ecx,80                         ;32位程序应该使用ECX
  98.   .cls:
  99.          mov word[es:bx],0x0720
  100.          add bx,2
  101.          loop .cls

  102.          pop es
  103.          pop ds

  104.          mov bx,1920

  105.   .set_cursor:
  106.          mov dx,0x3d4
  107.          mov al,0x0e
  108.          out dx,al
  109.          inc dx                             ;0x3d5
  110.          mov al,bh
  111.          out dx,al
  112.          dec dx                             ;0x3d4
  113.          mov al,0x0f
  114.          out dx,al
  115.          inc dx                             ;0x3d5
  116.          mov al,bl
  117.          out dx,al

  118.          popad
  119.          ret                                

  120. ;-------------------------------------------------------------------------------
  121. read_hard_disk_0:                           ;从硬盘读取一个逻辑扇区
  122.                                             ;EAX=逻辑扇区号
  123.                                             ;DS:EBX=目标缓冲区地址
  124.                                             ;返回:EBX=EBX+512
  125.          push eax
  126.          push ecx
  127.          push edx
  128.       
  129.          push eax
  130.          
  131.          mov dx,0x1f2
  132.          mov al,1
  133.          out dx,al                          ;读取的扇区数

  134.          inc dx                             ;0x1f3
  135.          pop eax
  136.          out dx,al                          ;LBA地址7~0

  137.          inc dx                             ;0x1f4
  138.          mov cl,8
  139.          shr eax,cl
  140.          out dx,al                          ;LBA地址15~8

  141.          inc dx                             ;0x1f5
  142.          shr eax,cl
  143.          out dx,al                          ;LBA地址23~16

  144.          inc dx                             ;0x1f6
  145.          shr eax,cl
  146.          or al,0xe0                         ;第一硬盘  LBA地址27~24
  147.          out dx,al

  148.          inc dx                             ;0x1f7
  149.          mov al,0x20                        ;读命令
  150.          out dx,al

  151.   .waits:
  152.          in al,dx
  153.          and al,0x88
  154.          cmp al,0x08
  155.          jnz .waits                         ;不忙,且硬盘已准备好数据传输

  156.          mov ecx,256                        ;总共要读取的字数
  157.          mov dx,0x1f0
  158.   .readw:
  159.          in ax,dx
  160.          mov [ebx],ax
  161.          add ebx,2
  162.          loop .readw

  163.          pop edx
  164.          pop ecx
  165.          pop eax
  166.       
  167.          retf                               ;段间返回

  168. ;-------------------------------------------------------------------------------
  169. ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助
  170. put_hex_dword:                              ;在当前光标处以十六进制形式显示
  171.                                             ;一个双字并推进光标
  172.                                             ;输入:EDX=要转换并显示的数字
  173.                                             ;输出:无
  174.          pushad
  175.          push ds
  176.       
  177.          mov ax,core_data_seg_sel           ;切换到核心数据段
  178.          mov ds,ax
  179.       
  180.          mov ebx,bin_hex                    ;指向核心数据段内的转换表
  181.          mov ecx,8
  182.   .xlt:   
  183.          rol edx,4
  184.          mov eax,edx
  185.          and eax,0x0000000f
  186.          xlat
  187.       
  188.          push ecx
  189.          mov cl,al                           
  190.          call put_char
  191.          pop ecx
  192.       
  193.          loop .xlt
  194.       
  195.          pop ds
  196.          popad
  197.          retf
  198.       
  199. ;-------------------------------------------------------------------------------
  200. allocate_memory:                            ;分配内存
  201.                                             ;输入:ECX=希望分配的字节数
  202.                                             ;输出:ECX=起始线性地址
  203.          push ds
  204.          push eax
  205.          push ebx
  206.       
  207.          mov eax,core_data_seg_sel
  208.          mov ds,eax
  209.       
  210.          mov eax,[ram_alloc]
  211.          add eax,ecx                        ;下一次分配时的起始地址
  212.       
  213.          ;这里应当有检测可用内存数量的指令
  214.          
  215.          mov ecx,[ram_alloc]                ;返回分配的起始地址

  216.          mov ebx,eax
  217.          and ebx,0xfffffffc
  218.          add ebx,4                          ;强制对齐
  219.          test eax,0x00000003                ;下次分配的起始地址最好是4字节对齐
  220.          cmovnz eax,ebx                     ;如果没有对齐,则强制对齐
  221.          mov [ram_alloc],eax                ;下次从该地址分配内存
  222.                                             ;cmovcc指令可以避免控制转移
  223.          pop ebx
  224.          pop eax
  225.          pop ds

  226.          retf

  227. ;-------------------------------------------------------------------------------
  228. set_up_gdt_descriptor:                      ;在GDT内安装一个新的描述符
  229.                                             ;输入:EDX:EAX=描述符
  230.                                             ;输出:CX=描述符的选择子
  231.          push eax
  232.          push ebx
  233.          push edx
  234.       
  235.          push ds
  236.          push es
  237.       
  238.          mov ebx,core_data_seg_sel          ;切换到核心数据段
  239.          mov ds,ebx

  240.          sgdt [pgdt]                        ;以便开始处理GDT

  241.          mov ebx,mem_0_4_gb_seg_sel
  242.          mov es,ebx

  243.          movzx ebx,word [pgdt]              ;GDT界限
  244.          inc bx                             ;GDT总字节数,也是下一个描述符偏移
  245.          add ebx,[pgdt+2]                   ;下一个描述符的线性地址
  246.       
  247.          mov [es:ebx],eax
  248.          mov [es:ebx+4],edx
  249.       
  250.          add word [pgdt],8                  ;增加一个描述符的大小   
  251.       
  252.          lgdt [pgdt]                        ;对GDT的更改生效
  253.       
  254.          mov ax,[pgdt]                      ;得到GDT界限值
  255.          xor dx,dx
  256.          mov bx,8
  257.          div bx                             ;除以8,去掉余数
  258.          mov cx,ax                          
  259.          shl cx,3                           ;将索引号移到正确位置

  260.          pop es
  261.          pop ds

  262.          pop edx
  263.          pop ebx
  264.          pop eax
  265.       
  266.          retf
  267. ;-------------------------------------------------------------------------------
  268. make_seg_descriptor:                        ;构造存储器和系统的段描述符
  269.                                             ;输入:EAX=线性基地址
  270.                                             ;      EBX=段界限
  271.                                             ;      ECX=属性。各属性位都在原始
  272.                                             ;          位置,无关的位清零
  273.                                             ;返回:EDX:EAX=描述符
  274.          mov edx,eax
  275.          shl eax,16
  276.          or ax,bx                           ;描述符前32位(EAX)构造完毕

  277.          and edx,0xffff0000                 ;清除基地址中无关的位
  278.          rol edx,8
  279.          bswap edx                          ;装配基址的31~24和23~16  (80486+)

  280.          xor bx,bx
  281.          or edx,ebx                         ;装配段界限的高4位

  282.          or edx,ecx                         ;装配属性

  283.          retf

  284. ;===============================================================================
  285. SECTION core_data vstart=0                  ;系统核心的数据段
  286. ;-------------------------------------------------------------------------------
  287.          pgdt             dw  0             ;用于设置和修改GDT
  288.                           dd  0

  289.          ram_alloc        dd  0x00100000    ;下次分配内存时的起始地址

  290.          ;符号地址检索表
  291.          salt:
  292.          salt_1           db  '@PrintString'
  293.                      times 256-($-salt_1) db 0
  294.                           dd  put_string
  295.                           dw  sys_routine_seg_sel

  296.          salt_2           db  '@ReadDiskData'
  297.                      times 256-($-salt_2) db 0
  298.                           dd  read_hard_disk_0
  299.                           dw  sys_routine_seg_sel

  300.          salt_3           db  '@PrintDwordAsHexString'
  301.                      times 256-($-salt_3) db 0
  302.                           dd  put_hex_dword
  303.                           dw  sys_routine_seg_sel

  304.          salt_4           db  '@TerminateProgram'
  305.                      times 256-($-salt_4) db 0
  306.                           dd  return_point
  307.                           dw  core_code_seg_sel

  308.          salt_item_len   equ $-salt_4
  309.          salt_items      equ ($-salt)/salt_item_len

  310.          message_1        db  '  If you seen this message,that means we '
  311.                           db  'are now in protect mode,and the system '
  312.                           db  'core is loaded,and the video display '
  313.                           db  'routine works perfectly.',0x0d,0x0a,0

  314.          message_5        db  '  Loading user program...',0
  315.          
  316.          do_status        db  'Done.',0x0d,0x0a,0
  317.          
  318.          message_6        db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a
  319.                           db  '  User program terminated,control returned.',0

  320.          bin_hex          db '0123456789ABCDEF'
  321.                                             ;put_hex_dword子过程用的查找表
  322.          core_buf   times 2048 db 0         ;内核用的缓冲区

  323.          esp_pointer      dd 0              ;内核用来临时保存自己的栈指针     

  324.          cpu_brnd0        db 0x0d,0x0a,'  ',0
  325.          cpu_brand  times 52 db 0
  326.          cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0

  327. ;===============================================================================
  328. SECTION core_code vstart=0
  329. ;-------------------------------------------------------------------------------
  330. load_relocate_program:                      ;加载并重定位用户程序
  331.                                             ;输入:ESI=起始逻辑扇区号
  332.                                             ;返回:AX=指向用户程序头部的选择子
  333.          push ebx
  334.          push ecx
  335.          push edx
  336.          push esi
  337.          push edi
  338.       
  339.          push ds
  340.          push es
  341.       
  342.          mov eax,core_data_seg_sel
  343.          mov ds,eax                         ;切换DS到内核数据段
  344.       
  345.          mov eax,esi                        ;读取程序头部数据
  346.          mov ebx,core_buf                        
  347.          call sys_routine_seg_sel:read_hard_disk_0

  348.          ;以下判断整个程序有多大
  349.          mov eax,[core_buf]                 ;程序尺寸
  350.          mov ebx,eax
  351.          and ebx,0xfffffe00                 ;使之512字节对齐(能被512整除的数,
  352.          add ebx,512                        ;低9位都为0
  353.          test eax,0x000001ff                ;程序的大小正好是512的倍数吗?
  354.          cmovnz eax,ebx                     ;不是。使用凑整的结果
  355.       
  356.          mov ecx,eax                        ;实际需要申请的内存数量
  357.          call sys_routine_seg_sel:allocate_memory
  358.          mov ebx,ecx                        ;ebx -> 申请到的内存首地址
  359.          push ebx                           ;保存该首地址
  360.          xor edx,edx
  361.          mov ecx,512
  362.          div ecx
  363.          mov ecx,eax                        ;总扇区数
  364.       
  365.          mov eax,mem_0_4_gb_seg_sel         ;切换DS到0-4GB的段
  366.          mov ds,eax

  367.          mov eax,esi                        ;起始扇区号
  368.   .b1:
  369.          call sys_routine_seg_sel:read_hard_disk_0
  370.          inc eax
  371.          loop .b1                           ;循环读,直到读完整个用户程序

  372.          ;建立程序头部段描述符
  373.          pop edi                            ;恢复程序装载的首地址
  374.          mov eax,edi                        ;程序头部起始线性地址
  375.          mov ebx,[edi+0x04]                 ;段长度
  376.          dec ebx                            ;段界限
  377.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  378.          call sys_routine_seg_sel:make_seg_descriptor
  379.          call sys_routine_seg_sel:set_up_gdt_descriptor
  380.          mov [edi+0x04],cx                  

  381.          ;建立程序代码段描述符
  382.          mov eax,edi
  383.          add eax,[edi+0x14]                 ;代码起始线性地址
  384.          mov ebx,[edi+0x18]                 ;段长度
  385.          dec ebx                            ;段界限
  386.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  387.          call sys_routine_seg_sel:make_seg_descriptor
  388.          call sys_routine_seg_sel:set_up_gdt_descriptor
  389.          mov [edi+0x14],cx

  390.          ;建立程序数据段描述符
  391.          mov eax,edi
  392.          add eax,[edi+0x1c]                 ;数据段起始线性地址
  393.          mov ebx,[edi+0x20]                 ;段长度
  394.          dec ebx                            ;段界限
  395.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  396.          call sys_routine_seg_sel:make_seg_descriptor
  397.          call sys_routine_seg_sel:set_up_gdt_descriptor
  398.          mov [edi+0x1c],cx

  399.          ;建立程序堆栈段描述符
  400.          mov ecx,[edi+0x0c]                 ;4KB的倍率
  401.          mov ebx,0x000fffff
  402.          sub ebx,ecx                        ;得到段界限
  403.          mov eax,4096                        
  404.          mul dword [edi+0x0c]                        
  405.          mov ecx,eax                        ;准备为堆栈分配内存
  406.          call sys_routine_seg_sel:allocate_memory
  407.          add eax,ecx                        ;得到堆栈的高端物理地址
  408.          mov ecx,0x00c09600                 ;4KB粒度的堆栈段描述符
  409.          call sys_routine_seg_sel:make_seg_descriptor
  410.          call sys_routine_seg_sel:set_up_gdt_descriptor
  411.          mov [edi+0x08],cx

  412.          ;重定位SALT
  413.          mov eax,[edi+0x04]
  414.          mov es,eax                         ;es -> 用户程序头部
  415.          mov eax,core_data_seg_sel
  416.          mov ds,eax
  417.       
  418.          cld

  419.          mov ecx,[es:0x24]                  ;用户程序的SALT条目数
  420.          mov edi,0x28                       ;用户程序内的SALT位于头部内0x2c处
  421.   .b2:
  422.          push ecx
  423.          push edi
  424.       
  425.          mov ecx,salt_items
  426.          mov esi,salt
  427.   .b3:
  428.          push edi
  429.          push esi
  430.          push ecx

  431.          mov ecx,64                         ;检索表中,每条目的比较次数
  432.          repe cmpsd                         ;每次比较4字节
  433.          jnz .b4
  434.          mov eax,[esi]                      ;若匹配,esi恰好指向其后的地址数据
  435.          mov [es:edi-256],eax               ;将字符串改写成偏移地址
  436.          mov ax,[esi+4]
  437.          mov [es:edi-252],ax                ;以及段选择子
  438.   .b4:
  439.       
  440.          pop ecx
  441.          pop esi
  442.          add esi,salt_item_len
  443.          pop edi                            ;从头比较
  444.          loop .b3
  445.       
  446.          pop edi
  447.          add edi,256
  448.          pop ecx
  449.          loop .b2

  450.          mov ax,[es:0x04]

  451.          pop es                             ;恢复到调用此过程前的es段
  452.          pop ds                             ;恢复到调用此过程前的ds段
  453.       
  454.          pop edi
  455.          pop esi
  456.          pop edx
  457.          pop ecx
  458.          pop ebx
  459.       
  460.          ret
  461.       
  462. ;-------------------------------------------------------------------------------
  463. start:
  464.          mov ecx,core_data_seg_sel           ;使ds指向核心数据段
  465.          mov ds,ecx

  466.          mov ebx,message_1
  467.          call sys_routine_seg_sel:put_string
  468.                                          
  469.          ;显示处理器品牌信息
  470.          mov eax,0x80000002
  471.          cpuid
  472.          mov [cpu_brand + 0x00],eax
  473.          mov [cpu_brand + 0x04],ebx
  474.          mov [cpu_brand + 0x08],ecx
  475.          mov [cpu_brand + 0x0c],edx
  476.       
  477.          mov eax,0x80000003
  478.          cpuid
  479.          mov [cpu_brand + 0x10],eax
  480.          mov [cpu_brand + 0x14],ebx
  481.          mov [cpu_brand + 0x18],ecx
  482.          mov [cpu_brand + 0x1c],edx

  483.          mov eax,0x80000004
  484.          cpuid
  485.          mov [cpu_brand + 0x20],eax
  486.          mov [cpu_brand + 0x24],ebx
  487.          mov [cpu_brand + 0x28],ecx
  488.          mov [cpu_brand + 0x2c],edx

  489.          mov ebx,cpu_brnd0
  490.          call sys_routine_seg_sel:put_string
  491.          mov ebx,cpu_brand
  492.          call sys_routine_seg_sel:put_string
  493.          mov ebx,cpu_brnd1
  494.          call sys_routine_seg_sel:put_string

  495.          mov ebx,message_5
  496.          call sys_routine_seg_sel:put_string
  497.          mov esi,50                          ;用户程序位于逻辑50扇区
  498.          call load_relocate_program
  499.       
  500.          mov ebx,do_status
  501.          call sys_routine_seg_sel:put_string
  502.       
  503.          mov [esp_pointer],esp               ;临时保存堆栈指针
  504.       
  505.          mov ds,ax
  506.       
  507.          jmp far [0x10]                      ;控制权交给用户程序(入口点)
  508.                                              ;堆栈可能切换

  509. return_point:                                ;用户程序返回点
  510.          mov eax,core_data_seg_sel           ;使ds指向核心数据段
  511.          mov ds,eax

  512.          mov eax,core_stack_seg_sel          ;切换回内核自己的堆栈
  513.          mov ss,eax
  514.          mov esp,[esp_pointer]

  515.          mov ebx,message_6
  516.          call sys_routine_seg_sel:put_string

  517.          ;这里可以放置清除用户程序各种描述符的指令
  518.          ;也可以加载并启动其它程序
  519.       
  520.          hlt
  521.             
  522. ;===============================================================================
  523. SECTION core_trail
  524. ;-------------------------------------------------------------------------------
  525. core_end:
复制代码



  1.          ;代码清单13-1
  2.          ;文件名:c13_mbr.asm
  3.          ;文件说明:硬盘主引导扇区代码
  4.          ;创建日期:2011-10-28 22:35        ;设置堆栈段和栈指针
  5.          
  6.          core_base_address equ 0x00040000   ;常数,内核加载的起始内存地址
  7.          core_start_sector equ 0x00000001   ;常数,内核的起始逻辑扇区号
  8.          
  9.          mov ax,cs      
  10.          mov ss,ax
  11.          mov sp,0x7c00
  12.       
  13.          ;计算GDT所在的逻辑段地址
  14.          mov eax,[cs:pgdt+0x7c00+0x02]      ;GDT的32位物理地址
  15.          xor edx,edx
  16.          mov ebx,16
  17.          div ebx                            ;分解成16位逻辑地址

  18.          mov ds,eax                         ;令DS指向该段以进行操作
  19.          mov ebx,edx                        ;段内起始偏移地址

  20.          ;跳过0#号描述符的槽位
  21.          ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
  22.          mov dword [ebx+0x08],0x0000ffff    ;基地址为0,段界限为0xFFFFF
  23.          mov dword [ebx+0x0c],0x00cf9200    ;粒度为4KB,存储器段描述符

  24.          ;创建保护模式下初始代码段描述符
  25.          mov dword [ebx+0x10],0x7c0001ff    ;基地址为0x00007c00,界限0x1FF
  26.          mov dword [ebx+0x14],0x00409800    ;粒度为1个字节,代码段描述符

  27.          ;建立保护模式下的堆栈段描述符      ;基地址为0x00007C00,界限0xFFFFE
  28.          mov dword [ebx+0x18],0x7c00fffe    ;粒度为4KB
  29.          mov dword [ebx+0x1c],0x00cf9600
  30.          
  31.          ;建立保护模式下的显示缓冲区描述符   
  32.          mov dword [ebx+0x20],0x80007fff    ;基地址为0x000B8000,界限0x07FFF
  33.          mov dword [ebx+0x24],0x0040920b    ;粒度为字节
  34.          
  35.          ;初始化描述符表寄存器GDTR
  36.          mov word [cs: pgdt+0x7c00],39      ;描述符表的界限   

  37.          lgdt [cs: pgdt+0x7c00]
  38.       
  39.          in al,0x92                         ;南桥芯片内的端口
  40.          or al,0000_0010B
  41.          out 0x92,al                        ;打开A20

  42.          cli                                ;中断机制尚未工作

  43.          mov eax,cr0
  44.          or eax,1
  45.          mov cr0,eax                        ;设置PE位
  46.       
  47.          ;以下进入保护模式... ...
  48.          jmp dword 0x0010:flush             ;16位的描述符选择子:32位偏移
  49.                                             ;清流水线并串行化处理器
  50.          [bits 32]               
  51.   flush:                                 
  52.          mov eax,0x0008                     ;加载数据段(0..4GB)选择子
  53.          mov ds,eax
  54.       
  55.          mov eax,0x0018                     ;加载堆栈段选择子
  56.          mov ss,eax
  57.          xor esp,esp                        ;堆栈指针 <- 0
  58.          
  59.          ;以下加载系统核心程序
  60.          mov edi,core_base_address
  61.       
  62.          mov eax,core_start_sector
  63.          mov ebx,edi                        ;起始地址
  64.          call read_hard_disk_0              ;以下读取程序的起始部分(一个扇区)
  65.       
  66.          ;以下判断整个程序有多大
  67.          mov eax,[edi]                      ;核心程序尺寸
  68.          xor edx,edx
  69.          mov ecx,512                        ;512字节每扇区
  70.          div ecx

  71.          or edx,edx
  72.          jnz @1                             ;未除尽,因此结果比实际扇区数少1
  73.          dec eax                            ;已经读了一个扇区,扇区总数减1
  74.    @1:
  75.          or eax,eax                         ;考虑实际长度≤512个字节的情况
  76.          jz setup                           ;EAX=0 ?

  77.          ;读取剩余的扇区
  78.          mov ecx,eax                        ;32位模式下的LOOP使用ECX
  79.          mov eax,core_start_sector
  80.          inc eax                            ;从下一个逻辑扇区接着读
  81.    @2:
  82.          call read_hard_disk_0
  83.          inc eax
  84.          loop @2                            ;循环读,直到读完整个内核

  85. setup:
  86.          mov esi,[0x7c00+pgdt+0x02]         ;不可以在代码段内寻址pgdt,但可以
  87.                                             ;通过4GB的段来访问
  88.          ;建立公用例程段描述符
  89.          mov eax,[edi+0x04]                 ;公用例程代码段起始汇编地址
  90.          mov ebx,[edi+0x08]                 ;核心数据段汇编地址
  91.          sub ebx,eax
  92.          dec ebx                            ;公用例程段界限
  93.          add eax,edi                        ;公用例程段基地址
  94.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  95.          call make_gdt_descriptor
  96.          mov [esi+0x28],eax
  97.          mov [esi+0x2c],edx
  98.       
  99.          ;建立核心数据段描述符
  100.          mov eax,[edi+0x08]                 ;核心数据段起始汇编地址
  101.          mov ebx,[edi+0x0c]                 ;核心代码段汇编地址
  102.          sub ebx,eax
  103.          dec ebx                            ;核心数据段界限
  104.          add eax,edi                        ;核心数据段基地址
  105.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  106.          call make_gdt_descriptor
  107.          mov [esi+0x30],eax
  108.          mov [esi+0x34],edx
  109.       
  110.          ;建立核心代码段描述符
  111.          mov eax,[edi+0x0c]                 ;核心代码段起始汇编地址
  112.          mov ebx,[edi+0x00]                 ;程序总长度
  113.          sub ebx,eax
  114.          dec ebx                            ;核心代码段界限
  115.          add eax,edi                        ;核心代码段基地址
  116.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  117.          call make_gdt_descriptor
  118.          mov [esi+0x38],eax
  119.          mov [esi+0x3c],edx

  120.          mov word [0x7c00+pgdt],63          ;描述符表的界限
  121.                                        
  122.          lgdt [0x7c00+pgdt]                  

  123.          jmp far [edi+0x10]  
  124.       
  125. ;-------------------------------------------------------------------------------
  126. read_hard_disk_0:                        ;从硬盘读取一个逻辑扇区
  127.                                          ;EAX=逻辑扇区号
  128.                                          ;DS:EBX=目标缓冲区地址
  129.                                          ;返回:EBX=EBX+512
  130.          push eax
  131.          push ecx
  132.          push edx
  133.       
  134.          push eax
  135.          
  136.          mov dx,0x1f2
  137.          mov al,1
  138.          out dx,al                       ;读取的扇区数

  139.          inc dx                          ;0x1f3
  140.          pop eax
  141.          out dx,al                       ;LBA地址7~0

  142.          inc dx                          ;0x1f4
  143.          mov cl,8
  144.          shr eax,cl
  145.          out dx,al                       ;LBA地址15~8

  146.          inc dx                          ;0x1f5
  147.          shr eax,cl
  148.          out dx,al                       ;LBA地址23~16

  149.          inc dx                          ;0x1f6
  150.          shr eax,cl
  151.          or al,0xe0                      ;第一硬盘  LBA地址27~24
  152.          out dx,al

  153.          inc dx                          ;0x1f7
  154.          mov al,0x20                     ;读命令
  155.          out dx,al

  156.   .waits:
  157.          in al,dx
  158.          and al,0x88
  159.          cmp al,0x08
  160.          jnz .waits                      ;不忙,且硬盘已准备好数据传输

  161.          mov ecx,256                     ;总共要读取的字数
  162.          mov dx,0x1f0
  163.   .readw:
  164.          in ax,dx
  165.          mov [ebx],ax
  166.          add ebx,2
  167.          loop .readw

  168.          pop edx
  169.          pop ecx
  170.          pop eax
  171.       
  172.          ret

  173. ;-------------------------------------------------------------------------------
  174. make_gdt_descriptor:                     ;构造描述符
  175.                                          ;输入:EAX=线性基地址
  176.                                          ;      EBX=段界限
  177.                                          ;      ECX=属性(各属性位都在原始
  178.                                          ;      位置,其它没用到的位置0)
  179.                                          ;返回:EDX:EAX=完整的描述符
  180.          mov edx,eax
  181.          shl eax,16                     
  182.          or ax,bx                        ;描述符前32位(EAX)构造完毕
  183.       
  184.          and edx,0xffff0000              ;清除基地址中无关的位
  185.          rol edx,8
  186.          bswap edx                       ;装配基址的31~24和23~16  (80486+)
  187.       
  188.          xor bx,bx
  189.          or edx,ebx                      ;装配段界限的高4位
  190.       
  191.          or edx,ecx                      ;装配属性
  192.       
  193.          ret
  194.       
  195. ;-------------------------------------------------------------------------------
  196.          pgdt             dw 0
  197.                           dd 0x00007e00      ;GDT的物理地址
  198. ;-------------------------------------------------------------------------------                             
  199.          times 510-($-$$) db 0
  200.                           db 0x55,0xaa
复制代码



  1.          ;代码清单13-1
  2.          ;文件名:c13_mbr.asm
  3.          ;文件说明:硬盘主引导扇区代码
  4.          ;创建日期:2011-10-28 22:35        ;设置堆栈段和栈指针
  5.          
  6.          core_base_address equ 0x00040000   ;常数,内核加载的起始内存地址
  7.          core_start_sector equ 0x00000001   ;常数,内核的起始逻辑扇区号
  8.          
  9.          mov ax,cs      
  10.          mov ss,ax
  11.          mov sp,0x7c00
  12.       
  13.          ;计算GDT所在的逻辑段地址
  14.          mov eax,[cs:pgdt+0x7c00+0x02]      ;GDT的32位物理地址
  15.          xor edx,edx
  16.          mov ebx,16
  17.          div ebx                            ;分解成16位逻辑地址

  18.          mov ds,eax                         ;令DS指向该段以进行操作
  19.          mov ebx,edx                        ;段内起始偏移地址

  20.          ;跳过0#号描述符的槽位
  21.          ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
  22.          mov dword [ebx+0x08],0x0000ffff    ;基地址为0,段界限为0xFFFFF
  23.          mov dword [ebx+0x0c],0x00cf9200    ;粒度为4KB,存储器段描述符

  24.          ;创建保护模式下初始代码段描述符
  25.          mov dword [ebx+0x10],0x7c0001ff    ;基地址为0x00007c00,界限0x1FF
  26.          mov dword [ebx+0x14],0x00409800    ;粒度为1个字节,代码段描述符

  27.          ;建立保护模式下的堆栈段描述符      ;基地址为0x00007C00,界限0xFFFFE
  28.          mov dword [ebx+0x18],0x7c00fffe    ;粒度为4KB
  29.          mov dword [ebx+0x1c],0x00cf9600
  30.          
  31.          ;建立保护模式下的显示缓冲区描述符   
  32.          mov dword [ebx+0x20],0x80007fff    ;基地址为0x000B8000,界限0x07FFF
  33.          mov dword [ebx+0x24],0x0040920b    ;粒度为字节
  34.          
  35.          ;初始化描述符表寄存器GDTR
  36.          mov word [cs: pgdt+0x7c00],39      ;描述符表的界限   

  37.          lgdt [cs: pgdt+0x7c00]
  38.       
  39.          in al,0x92                         ;南桥芯片内的端口
  40.          or al,0000_0010B
  41.          out 0x92,al                        ;打开A20

  42.          cli                                ;中断机制尚未工作

  43.          mov eax,cr0
  44.          or eax,1
  45.          mov cr0,eax                        ;设置PE位
  46.       
  47.          ;以下进入保护模式... ...
  48.          jmp dword 0x0010:flush             ;16位的描述符选择子:32位偏移
  49.                                             ;清流水线并串行化处理器
  50.          [bits 32]               
  51.   flush:                                 
  52.          mov eax,0x0008                     ;加载数据段(0..4GB)选择子
  53.          mov ds,eax
  54.       
  55.          mov eax,0x0018                     ;加载堆栈段选择子
  56.          mov ss,eax
  57.          xor esp,esp                        ;堆栈指针 <- 0
  58.          
  59.          ;以下加载系统核心程序
  60.          mov edi,core_base_address
  61.       
  62.          mov eax,core_start_sector
  63.          mov ebx,edi                        ;起始地址
  64.          call read_hard_disk_0              ;以下读取程序的起始部分(一个扇区)
  65.       
  66.          ;以下判断整个程序有多大
  67.          mov eax,[edi]                      ;核心程序尺寸
  68.          xor edx,edx
  69.          mov ecx,512                        ;512字节每扇区
  70.          div ecx

  71.          or edx,edx
  72.          jnz @1                             ;未除尽,因此结果比实际扇区数少1
  73.          dec eax                            ;已经读了一个扇区,扇区总数减1
  74.    @1:
  75.          or eax,eax                         ;考虑实际长度≤512个字节的情况
  76.          jz setup                           ;EAX=0 ?

  77.          ;读取剩余的扇区
  78.          mov ecx,eax                        ;32位模式下的LOOP使用ECX
  79.          mov eax,core_start_sector
  80.          inc eax                            ;从下一个逻辑扇区接着读
  81.    @2:
  82.          call read_hard_disk_0
  83.          inc eax
  84.          loop @2                            ;循环读,直到读完整个内核

  85. setup:
  86.          mov esi,[0x7c00+pgdt+0x02]         ;不可以在代码段内寻址pgdt,但可以
  87.                                             ;通过4GB的段来访问
  88.          ;建立公用例程段描述符
  89.          mov eax,[edi+0x04]                 ;公用例程代码段起始汇编地址
  90.          mov ebx,[edi+0x08]                 ;核心数据段汇编地址
  91.          sub ebx,eax
  92.          dec ebx                            ;公用例程段界限
  93.          add eax,edi                        ;公用例程段基地址
  94.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  95.          call make_gdt_descriptor
  96.          mov [esi+0x28],eax
  97.          mov [esi+0x2c],edx
  98.       
  99.          ;建立核心数据段描述符
  100.          mov eax,[edi+0x08]                 ;核心数据段起始汇编地址
  101.          mov ebx,[edi+0x0c]                 ;核心代码段汇编地址
  102.          sub ebx,eax
  103.          dec ebx                            ;核心数据段界限
  104.          add eax,edi                        ;核心数据段基地址
  105.          mov ecx,0x00409200                 ;字节粒度的数据段描述符
  106.          call make_gdt_descriptor
  107.          mov [esi+0x30],eax
  108.          mov [esi+0x34],edx
  109.       
  110.          ;建立核心代码段描述符
  111.          mov eax,[edi+0x0c]                 ;核心代码段起始汇编地址
  112.          mov ebx,[edi+0x00]                 ;程序总长度
  113.          sub ebx,eax
  114.          dec ebx                            ;核心代码段界限
  115.          add eax,edi                        ;核心代码段基地址
  116.          mov ecx,0x00409800                 ;字节粒度的代码段描述符
  117.          call make_gdt_descriptor
  118.          mov [esi+0x38],eax
  119.          mov [esi+0x3c],edx

  120.          mov word [0x7c00+pgdt],63          ;描述符表的界限
  121.                                        
  122.          lgdt [0x7c00+pgdt]                  

  123.          jmp far [edi+0x10]  
  124.       
  125. ;-------------------------------------------------------------------------------
  126. read_hard_disk_0:                        ;从硬盘读取一个逻辑扇区
  127.                                          ;EAX=逻辑扇区号
  128.                                          ;DS:EBX=目标缓冲区地址
  129.                                          ;返回:EBX=EBX+512
  130.          push eax
  131.          push ecx
  132.          push edx
  133.       
  134.          push eax
  135.          
  136.          mov dx,0x1f2
  137.          mov al,1
  138.          out dx,al                       ;读取的扇区数

  139.          inc dx                          ;0x1f3
  140.          pop eax
  141.          out dx,al                       ;LBA地址7~0

  142.          inc dx                          ;0x1f4
  143.          mov cl,8
  144.          shr eax,cl
  145.          out dx,al                       ;LBA地址15~8

  146.          inc dx                          ;0x1f5
  147.          shr eax,cl
  148.          out dx,al                       ;LBA地址23~16

  149.          inc dx                          ;0x1f6
  150.          shr eax,cl
  151.          or al,0xe0                      ;第一硬盘  LBA地址27~24
  152.          out dx,al

  153.          inc dx                          ;0x1f7
  154.          mov al,0x20                     ;读命令
  155.          out dx,al

  156.   .waits:
  157.          in al,dx
  158.          and al,0x88
  159.          cmp al,0x08
  160.          jnz .waits                      ;不忙,且硬盘已准备好数据传输

  161.          mov ecx,256                     ;总共要读取的字数
  162.          mov dx,0x1f0
  163.   .readw:
  164.          in ax,dx
  165.          mov [ebx],ax
  166.          add ebx,2
  167.          loop .readw

  168.          pop edx
  169.          pop ecx
  170.          pop eax
  171.       
  172.          ret

  173. ;-------------------------------------------------------------------------------
  174. make_gdt_descriptor:                     ;构造描述符
  175.                                          ;输入:EAX=线性基地址
  176.                                          ;      EBX=段界限
  177.                                          ;      ECX=属性(各属性位都在原始
  178.                                          ;      位置,其它没用到的位置0)
  179.                                          ;返回:EDX:EAX=完整的描述符
  180.          mov edx,eax
  181.          shl eax,16                     
  182.          or ax,bx                        ;描述符前32位(EAX)构造完毕
  183.       
  184.          and edx,0xffff0000              ;清除基地址中无关的位
  185.          rol edx,8
  186.          bswap edx                       ;装配基址的31~24和23~16  (80486+)
  187.       
  188.          xor bx,bx
  189.          or edx,ebx                      ;装配段界限的高4位
  190.       
  191.          or edx,ecx                      ;装配属性
  192.       
  193.          ret
  194.       
  195. ;-------------------------------------------------------------------------------
  196.          pgdt             dw 0
  197.                           dd 0x00007e00      ;GDT的物理地址
  198. ;-------------------------------------------------------------------------------                             
  199.          times 510-($-$$) db 0
  200.                           db 0x55,0xaa
复制代码



  1. The Intel386 Processor (1985)
  2.   The Intel386 processor was the first 32-bit processor in the IA-32 architecture family. It introduced 32-bit registers for use both to hold operands and for addressing. The lower half of each 32-bit Intel386 register retains the properties of the 16-bit registers of earlier generations, permitting backward compatibility. The processor also provides a virtual-8086 mode that allows for even greater efficiency when executing programs created for 8086/8088 processors.    [END]
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|4ameta

GMT+8, 2022-10-3 00:56 , Processed in 0.038794 second(s), 17 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表