在上一篇文章中,老李已经教大家将控制权从汇编语言
转移到C 语言
,但是我们的活动范围依然受限于512
字节的引导扇区。今天老李就带领大家突破这512
字节的限制,真正的解放生产力。
前置知识
如果你一路跟着老李走过来,那么这些前置知识你应该已经掌握了。忘记也没关系,回过头再去看看就 ok 了。
在上一篇文章中,老李已经教大家将控制权从汇编语言
转移到C 语言
,但是我们的活动范围依然受限于512
字节的引导扇区。今天老李就带领大家突破这512
字节的限制,真正的解放生产力。
如果你一路跟着老李走过来,那么这些前置知识你应该已经掌握了。忘记也没关系,回过头再去看看就 ok 了。
汇编语言的基础已经讲了很多,也带领大家进入了保护模式。想必大家在学习进入保护模式这一章时就发现我们已经涉及了很多数据结构
,全局描述符表
、段描述符
、GDTR
等。如果可以使用C 语言
,将他们和struct
对应起来那将会减少很多的工作量。今天老李就教大家如何从汇编语言
过渡到C 语言
。
保护模式实际上很简单,只是概念比较多。今天老李就带领大家进入保护模式,然后在实操中逐一攻克每个概念。
进入保护模式总共分三步。
A20
地址线。GDT
。保护模式
。下面我们就来详细讲解一下这三个步骤。
众所周知,8086
处理器有20
根地址线,可访问的最大内存地址是0xfffff
,即1MB
。32
位处理器具有32
根地址线,可访问的最大内存地址是0xffffffff
,即4GB
。在32
位处理器刚面世的时候并没有太多针对32
位处理器开发的软件,更多的是为8086
开发的软件,计算机制造商为了能够兼容这些软件,便想出了一个办法,在计算机启动后将第21
根地址线,即A20
,置为0
。这样,当地址超过0xfffff
后,由于第21
根地址线为0
,所以地址又会绕回到0x00000
开始。从外表上看就好像是一个16
位的处理器。
中断就是打断CPU
当前的执行流程,让CPU
去处理一下别的事情。当然,CPU
也可以选择拒绝。
中断按中断源可以分为内部中断
和外部中断
。
内部中断可以由中断指令int
来触发,也可以是因为指令执行中出现了错误而触发,例如运算结果溢出会触发溢出中断;除法指令的除数为0
会触发除法出错中断。
通常我们封装过程是为了方便调用,避免写重复的代码。过程调用时通常需要通过传递参数来控制过程的执行,今天我们来讲一讲参数传递时的一些规范和需要注意的地方。
先来看一个例子:
.code16
movw $0x7c00, %sp
callw set_cursor
jmp .
# 目的: 设置光标位置为 0
#
# 输入: 无
#
# 输出: 无
set_cursor:
movw $0x3d4, %dx
movb $0xe, %al
outb %al, %dx
movw $0x3d5, %dx
movb $0, %al
outb %al, %dx
movw $0x3d4, %dx
movb $0xf, %al
outb %al, %dx
movw $0x3d5, %dx
movb $0, %al
outb %al, %dx
retw
.org 510
.word 0xAA55
前两篇文章中我们学习了如何控制屏幕光标,如何从硬盘读取数据。这种常用的功能我们希望将它封装成过程调用,类似于高级语言中的函数,这样当我们控制光标或者从硬盘读取数据时就不需要每次都写大段的重复代码了。
在CPU
中,执行的指令通过cs:ip
来确定。过程调用实际上就是通过call
或lcall
指令来修改ip
或cs:ip
来达到跳转到另一段指令中执行的目的。
call
指令通过修改ip
来实现过程调用,因为只修改ip
,所以被调例程与原例程在同一个代码段内,也称为近调用。处理器在执行call
指令时先将call
后面的第一条指令的偏移地址压栈,再通过操作数计算出新的ip
替换当前ip
。
今天我们来学习如何从硬盘读取数据。主要从编程的角度来学习关于硬盘的知识,即学习如何通过端口控制硬盘。
从存储数据的介质上区分,硬盘可以分为机械硬盘和固态硬盘,机械硬盘采用磁性碟片来存储数据,固态硬盘通过闪存颗粒存储数据。从编程的角度看,固态硬盘是兼容机械硬盘的,所以我们以机械硬盘为例,简要介绍一下硬盘。
机械硬盘主要由磁盘盘片、磁头、主轴与传动轴等组成,数据存放在磁盘盘片中。每个盘片分为上下两面,每面由一个磁头(Head)
进行读写。磁头统一固定在同一个支架上,由步进电动机控制,同时在盘片的中心和边缘之间来回移动。当盘片高速旋转时,磁头每步进一次,都会从它所在的位置开始,绕着圆心“画”出一个看不见的圆圈,这就是磁道(Track)
。磁道是数据记录的轨迹。因为所有磁头都是联动的,故每个盘面上的同一条磁道又可以形成一个虚拟的圆柱,称为柱面(Cylinder)
。
I/O
接口用于CPU
与外部I/O
设备进行信息交换。例如与键盘、鼠标、打印机和显示等设备交互。I/O
接口电路与总线系统看似很复杂,但是落实到汇编语言代码上实则是很简单的。下面简要介绍一下I/O
接口技术。
在计算机中,CPU
与外设并不是直接相连的,在它们中间设有I/O
接口电路。CPU
通过数据总线、地址总线和控制总线与I/O
接口电路相连,以实现与外设交换数据信息、状态信息和控制信息。外设的状态信息通过接口电路的状态端口经由数据总线进入CPU
,而CPU
向外设发出的控制信号也是经由数据总线,通过接口电路的控制端口来实现的。
上一篇文章中我们实现了数字各个位的分解并打印在屏幕上。他需要我们知道数字有多少个位,并且提前预留出内存空间保存每个位,这显然不是一个完美的解决方案。现在我们来学习一下堆栈,并使用堆栈来保存分解出的每个位,实现可以分解任意位的数字。
先解释一下堆栈
。实际上这个堆栈
和堆(heap)
并没有关系,只是一个纯粹的栈(stack)
,可能是堆栈
这样叫起来更上口一点吧。
堆栈段
和其他段一样,只是一段普通的内存空间,只是我们限制了对这部分内存空间操作的行为。我们只允许通过push(压栈)
和pop(出栈)
这两个指令来操作堆栈段的内存空间,以此来实现一些算法。使用堆栈段
之前需要先初始化段基址(ss)
和栈顶指针(sp)
,例如将ss
初始化为0x0000
,sp
初始化为0x7c00
。此时堆栈段的逻辑地址为0x0000:0x0000
到0x0000:0x7c00
,对应的物理地址为0x00000
到0x07c00
。
上一篇文章中我们学习了指令的寻址方式,实际上是借具体的代码总结了一下寻址方式。这篇文章我们将学习更多的指令,通过实际代码的讲解,找到写汇编语言代码的感觉。下面先来讲一下串操作指令。
含义:通过执行一条字符串操作指令,对存储器中某一个连续的内存中存放的一串字或字节均进行同样的操作,称为串操作。字符串操作指令简称为串操作指令。