0 00:00:00,000 --> 00:00:06,800 1 00:00:06,840 --> 00:00:11,240 第三部分是关于GCC内联汇编 2 00:00:11,280 --> 00:00:12,520 GCC内联汇编比较特殊 3 00:00:12,560 --> 00:00:15,240 它是说我们怎么在C语言的开发环境中 4 00:00:15,280 --> 00:00:16,120 来使用汇编代码 5 00:00:16,160 --> 00:00:17,400 来写汇编代码 6 00:00:17,440 --> 00:00:19,760 使得C和混编来混在一起来使用 7 00:00:19,800 --> 00:00:22,680 称之为内联汇编 8 00:00:22,720 --> 00:00:26,040 这个实际上是GCC很有特点的地方 9 00:00:26,080 --> 00:00:27,200 有了内联汇编之后 10 00:00:27,240 --> 00:00:31,440 可以直接很简洁的在C里面插入汇编代码 11 00:00:31,480 --> 00:00:33,360 来完成相应的功能 12 00:00:33,400 --> 00:00:36,720 那为什么要在C里面插汇编代码呢 13 00:00:36,760 --> 00:00:40,680 大家想一想 其实是由于我们的C语言 14 00:00:40,720 --> 00:00:44,440 不足以完成所有的CPU的指令 15 00:00:44,480 --> 00:00:46,080 特别是有一些特权指令 16 00:00:46,120 --> 00:00:49,920 它没有对应的C语言的对照 17 00:00:49,960 --> 00:00:50,720 比如说我们刚才说的 18 00:00:50,760 --> 00:00:55,200 加载全局描述符表 LGDT 19 00:00:55,240 --> 00:00:59,920 这么一条机器指令 你没法用C语言表述 20 00:00:59,960 --> 00:01:01,680 你只能用汇编来表述 21 00:01:01,720 --> 00:01:06,800 在这种情况下 内联汇编会发生很大的作用 22 00:01:06,840 --> 00:01:10,160 它可以很简单的在C语言里面写出一句话 23 00:01:10,200 --> 00:01:12,240 来把这个功能给完成 24 00:01:12,280 --> 00:01:14,960 这使得我们写的代码更加清楚 25 00:01:15,000 --> 00:01:17,720 更加便于理解 26 00:01:17,760 --> 00:01:20,800 好 我们接下来就来看看它是如何工作的 27 00:01:20,840 --> 00:01:22,200 这也是我们需要去了解的 28 00:01:22,240 --> 00:01:24,240 因为我们在uCore中有这种代码存在 29 00:01:24,280 --> 00:01:25,480 所以说我们希望大家能够 30 00:01:25,520 --> 00:01:27,360 通过对这部分的了解 31 00:01:27,400 --> 00:01:28,400 能够读懂这个代码 32 00:01:28,440 --> 00:01:35,800 从而可以更好的理解uCore的整个执行过程 33 00:01:35,840 --> 00:01:37,840 这是一个例子 34 00:01:37,880 --> 00:01:39,960 这是汇编代码 这是内联汇编 35 00:01:40,000 --> 00:01:42,480 你可以看出来其实它们很像 36 00:01:42,520 --> 00:01:48,880 这是汇编代码 把这个值赋给EAX的寄存器 37 00:01:48,920 --> 00:01:51,840 那么对于我们来说 内联汇编怎么写呢 38 00:01:51,880 --> 00:01:55,200 加了一个asm代表内联汇编的关键字 39 00:01:55,240 --> 00:01:59,560 然后把刚才这段汇编括起来 40 00:01:59,600 --> 00:02:01,960 括起来之后用引号表示字符串 41 00:02:02,000 --> 00:02:04,640 还需要注意它这个标示里面 42 00:02:04,680 --> 00:02:06,960 一个百分号变成两个百分号 43 00:02:07,000 --> 00:02:10,760 这是一些语法上的一些表示而已 44 00:02:10,800 --> 00:02:11,960 那其实还是很直接的对应 45 00:02:12,000 --> 00:02:15,760 这是一种简单的对应方式 好理解 46 00:02:15,800 --> 00:02:19,720 我们再看看 它的一个完整的格式是这样的 47 00:02:19,760 --> 00:02:21,440 asm关键字 然后用一个括号 48 00:02:21,480 --> 00:02:25,440 这里面这一段就是刚才说的字符串 49 00:02:25,480 --> 00:02:28,680 还有三个约束表示 50 00:02:28,720 --> 00:02:32,080 对于输出操作数 输入操作数 51 00:02:32,120 --> 00:02:33,640 还有一个clobber 52 00:02:33,680 --> 00:02:37,120 一个我们暂时可以忽略的一个表述 53 00:02:37,160 --> 00:02:39,680 这三个表述都是可选的 54 00:02:39,720 --> 00:02:41,360 你可以像刚才那样什么都不加 55 00:02:41,400 --> 00:02:42,560 但是你如果要建立一定约束 56 00:02:42,600 --> 00:02:45,960 比如说我希望把某一个变量 57 00:02:46,000 --> 00:02:47,520 用某一个寄存器来表示 58 00:02:47,560 --> 00:02:49,320 那么可以在里面加一定的约束 59 00:02:49,360 --> 00:02:51,120 使得它完成现在的功能 60 00:02:51,160 --> 00:02:54,040 我们会以例子来展示它大致的一个过程 61 00:02:54,080 --> 00:02:58,400 好 这是一段代码片段 大家可以看看 62 00:02:58,440 --> 00:03:01,840 这是取于我们uCore的代码 63 00:03:01,880 --> 00:03:05,440 比如这是一个CR0的内存变量 64 00:03:05,480 --> 00:03:07,840 我们希望把刚才说的CR0寄存器的 65 00:03:07,880 --> 00:03:11,000 某一个bit给置位 66 00:03:11,040 --> 00:03:14,720 怎么来做呢 CR0寄存器没法用C语言来表示 67 00:03:14,760 --> 00:03:18,040 不得不用内联汇编来表示它 68 00:03:18,080 --> 00:03:18,880 这里面写了两句话 69 00:03:18,920 --> 00:03:20,720 比如说在这里面 70 00:03:20,760 --> 00:03:22,080 首先把CR0的内容 71 00:03:22,120 --> 00:03:24,640 读到这个%0寄存器里面去 72 00:03:24,680 --> 00:03:26,240 然后这个寄存器的内容 73 00:03:26,280 --> 00:03:28,160 最终会赋给CR0这个变量 74 00:03:28,200 --> 00:03:33,200 这个=r 是大致这个含义 75 00:03:33,240 --> 00:03:35,480 既然你把CR0寄存器的内容 76 00:03:35,520 --> 00:03:37,080 读给了CR0这个变量 77 00:03:37,120 --> 00:03:38,680 那我们可以对变量进行操作 78 00:03:38,720 --> 00:03:40,960 把某一个位置成1 79 00:03:41,000 --> 00:03:43,520 或了一下 或等于 置成1 80 00:03:43,560 --> 00:03:47,040 之后我们再把内存里的内容 81 00:03:47,080 --> 00:03:48,880 写回到CR0寄存器里面去 82 00:03:48,920 --> 00:03:51,040 就靠这条指令来完成 83 00:03:51,080 --> 00:03:54,440 可以看到 正好倒过来了 84 00:03:54,480 --> 00:03:57,120 是要把CR0给一个寄存器 85 00:03:57,160 --> 00:04:03,000 它会把这个寄存器的值赋给CR0寄存器 86 00:04:03,040 --> 00:04:04,640 那我们生成汇编 87 00:04:04,680 --> 00:04:06,680 如果我们把这段代码生成汇编 88 00:04:06,720 --> 00:04:10,880 可以看到是不是我们刚才想达到的意思 89 00:04:10,920 --> 00:04:12,800 CR0寄存器赋给ebx 90 00:04:12,840 --> 00:04:18,760 ebx给到一个 这实际上是代表一个局部变量 91 00:04:18,800 --> 00:04:23,080 然后这一步就是一个或操作 92 00:04:23,120 --> 00:04:26,080 来完成了对某一个bit的置1 93 00:04:26,120 --> 00:04:31,160 再把这个局部变量里面的值赋给EAX寄存器 94 00:04:31,200 --> 00:04:34,560 最后把EAX赋给CR0 95 00:04:34,600 --> 00:04:40,520 那就完成了这两个内联汇编的功能 96 00:04:40,560 --> 00:04:44,600 可以看出来 这段代码比这段代码要复杂 97 00:04:44,640 --> 00:04:46,200 因为这里面加了相应的约束之后 98 00:04:46,240 --> 00:04:48,120 用我们的GCC编译器自动的 99 00:04:48,160 --> 00:04:51,000 帮我们把关系给建立好 100 00:04:51,040 --> 00:04:53,400 基于你刚才所谓的约束的规则 101 00:04:53,440 --> 00:04:54,640 所以可以看出来 102 00:04:54,680 --> 00:04:56,680 有了这么一个表示之后 103 00:04:56,720 --> 00:04:58,640 我们可以更简洁的来完成 104 00:04:58,680 --> 00:05:04,760 一些汇编才能完成的功能 105 00:05:04,800 --> 00:05:09,440 前面已经讲到了 这里面一些关键字 106 00:05:09,480 --> 00:05:13,160 volatile 是不需要做进一步的优化 调整顺序 107 00:05:13,200 --> 00:05:16,920 那这个%0 代表的是第一个用到的寄存器 108 00:05:16,960 --> 00:05:19,960 这个r代表是任意寄存器的意思 109 00:05:20,000 --> 00:05:21,480 这里面都有相应的含义 110 00:05:21,520 --> 00:05:23,040 来建立相应的约束 111 00:05:23,080 --> 00:05:24,480 也意味着我们说用EBX 112 00:05:24,520 --> 00:05:27,440 或者EAX来完成对CR0的传递 113 00:05:27,480 --> 00:05:31,600 都是可以的 因为它这个值是r 114 00:05:31,640 --> 00:05:33,200 这是一个约束的表示 115 00:05:33,240 --> 00:05:35,720 那我们其实可以看一个更复杂的例子 116 00:05:35,760 --> 00:05:37,680 这里有一系列的局部变量 117 00:05:37,720 --> 00:05:41,280 然后会执行一个int 80的指令 118 00:05:41,320 --> 00:05:42,960 这个int 80也是一个特殊的指令 119 00:05:43,000 --> 00:05:46,720 产生我们说的软中断 120 00:05:46,760 --> 00:05:52,600 这个过程需要把这些arg1赋给bx 121 00:05:52,640 --> 00:05:56,320 arg2赋给cx arg3赋给dx 122 00:05:56,360 --> 00:06:00,000 arg4赋给什么呢 ESI 123 00:06:00,040 --> 00:06:01,480 不同的字符代表什么意思 124 00:06:01,520 --> 00:06:04,120 这边有一个约束的约定 125 00:06:04,160 --> 00:06:06,480 所以这条内联汇编 126 00:06:06,520 --> 00:06:08,400 其实等同于这么一条指令 127 00:06:08,440 --> 00:06:10,920 它最终是会把相应的 128 00:06:10,960 --> 00:06:14,520 这些局部变量赋给相应的寄存器 129 00:06:14,560 --> 00:06:17,880 然后调用int 80这么一个指令 130 00:06:17,920 --> 00:06:19,320 产生一个软中断 131 00:06:19,360 --> 00:06:21,520 最后这个软中断返回的值 132 00:06:21,560 --> 00:06:26,240 会再赋给EBP 133 00:06:26,280 --> 00:06:34,160 最后是把它赋给了这个地方 134 00:06:34,200 --> 00:06:35,440 我们基本上就简单的 135 00:06:35,480 --> 00:06:38,360 给大家介绍了一下内联汇编 136 00:06:38,400 --> 00:06:40,680 如果大家需要去进一步了解内联汇编的话 137 00:06:40,720 --> 00:06:43,360 也可以看一些相关的文档 138 00:06:43,400 --> 00:06:44,520 这方面网上文档也比较多 139 00:06:44,560 --> 00:06:45,760 大家可以去了解一下 140 00:06:45,800 --> 00:06:47,080 这里面最主要的目标是 141 00:06:47,120 --> 00:06:50,400 希望大家能够通过对这个的一个初步了解 142 00:06:50,440 --> 00:06:52,840 能够知道uCore中出现的内联汇编 143 00:06:52,880 --> 00:06:54,000 大致是什么含义 144 00:06:54,040 --> 00:06:58,560 这是我们说去了解的一个最主要的目标 145 00:06:58,600 --> 00:06:59,320 146 00:06:59,360 --> 00:06:59,880 147 00:06:59,920 --> 00:06:59,920