应用和硬件的关系
我们作为程序员一般很少直接操控硬件,我们一般通过 C、Java 等高级语言编写的程序起到间接控制硬件的作用。所以大家很少直接接触到硬件的指令,硬件的控制是由 Windows 操作系统 全权负责的。
你一定猜到我要说什么了,没错,我会说但是,任何事情没有绝对性,环境的不同会造成结果的偏差。虽然程序员没法直接控制硬件,并且 Windows 屏蔽了控制硬件的细节,但是 Windows 却为你开放了 系统调用功能来实现对硬件的控制。在 Windows 中,系统调用称为 API,API 就是应用调用的函数,这些函数的实体被存放在 DLL 文件中。
下面我们来看一个通过系统调用来间接控制硬件的实例
假如要在窗口中显示字符串,就可以使用 Windows API 中的 TextOut 函数。TextOut 函数的语法(C 语言)如下
BOOL TextOut{ HDC hdc, // 设备描述表的句柄 int nXStart, // 显示字符串的 x 坐标 int nYStart, // 显示字符串的 y 坐标 LPCTSTR lpString, // 指向字符串的指针 int cbString // 字符串的文字数 }那么,在处理 TextOut 函数的内容时,Windows 做了些什么呢?从结果来看,Windows 直接控制了作为硬件的显示器。但 Windows 本身也是软件,由此可见,Windows 应该向 CPU 传递了某种指令,从而通过软件控制了硬件。
Windows 提供的 TextOut 函数 API 可以向窗口和打印机输出字符。C 语言提供的 printf 函数,是用来在命令提示符中显示字符串的函数。使用 printf 函数是无法向打印机输出字符的。
支持硬件输入输出的 IN 指令和 OUT 指令Windows 控制硬件借助的是输入和输出指令。其中具有代表性的两个输入输出指令就是 IN 和 OUT指令。这些指令也是汇编语言的助记符。
可以通过 IN 和 OUT 指令来实现对数据的读入和输出,如下图所示
也就是说,IN 指令通过指定的端口号输入数据,OUT 指令则是把 CPU 寄存器中存储的数据输出到指定端口号的端口。
那么这个端口号 和 端口是什么呢?你感觉它像不像港口一样?通过标注哪个港口然后进行货物的运送和运出?
下面我们来看一下官方是如何定义端口号和端口的
还记得计算机组成原理中计算机的五大组成部分吗,再来回顾一下:运算器、控制器、存储器、输入设备和输出设备。我们今天不谈前三个,就说说后面两个输入设备和输出设备,这两个与我们本节主题息息相关。
那么问题来了,IO设备如何实现输入和输出的呢?计算机主机中,附带了用来连接显示器以及键盘等外围设备的连接器。 而连接器的内部,都连接有用来交换计算机主机同外围设备之间电流特性的 IC。如果 IC 你不明白是什么的话,可以参考作者的文章 进行了解。这些 IC 统称为 IO 控制器。
IO 是 Input/Output 的缩写。显示器、键盘等外围设备都有各自专用的 I/O 控制器。I/O 控制器中有用于临时保存输入输出数据的内存。这个内存就是 端口(port)。端口你就可以把它理解为我们上述说的 港口。IO 控制器内部的内存,也被称为寄存器,不要慌,这个寄存器和内存中的寄存器不一样。CPU 内存的寄存器是用于进行数据运算处理的,而IO中的寄存器是用于临时存储数据的。
在 I/O 设备内部的 IC 中,有多个端口。由于计算机中连接着很多外围设备,因此也就有很多 I/O 控制器。当然也会有多个端口,一个 I/O 控制器可以控制多个设备,不仅仅只能控制一个。各端口之间通过 端口号 进行区分。
端口号也被称为 I/O地址 。IN 指令和 OUT 指令在端口号指定的端口和 CPU 之间进行数据的输入和输出。这跟通过内存的地址来对内存进行读写是一样的道理。
测试输入和输出程序首先让我们利用 IN 指令和 OUT 指令,来进行一个直接控制硬件的实验。假如试验的目的是让一个计算机内置的喇叭(蜂鸣器)发出声音。蜂鸣器封装在计算机内部,但它也是外围设备的一种。
用汇编语言比较繁琐,这次我们用 C 语言来实现。在大部分 C 语言的处理(编译器的种类)中,只要使用 _asm{ 和 }括起来,就可以在其中记述助记符。也就是说,采用这种方式就能够使用 C 语言和汇编语言混合的源代码。