鱼C论坛

 找回密码
 立即注册
查看: 2605|回复: 0

[学习笔记] X86汇编语言-从实模式到保护模式—笔记(25)-第13章 程序的动态加载和执行(3)

[复制链接]
发表于 2017-12-1 19:11:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 兰陵月 于 2017-12-5 21:52 编辑
  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:
复制代码

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 18:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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