这里应该就是一些理论的笔记部分。
计算机理论
小端模式
下面证明一下“低位对低位,高位对高位”。
C:\> debug- a073F:0100 mov ax, 1234073F:0103 mov [6666], ax073F:0106 int 3 ; 设置断点073F:0107 ; 回车退出- g ; 运行到断点- d ds:6666然后可以看见这个ds:6666位置的数,是地址低位是34(数值低位),地址高位是12(数值高位)。

外设接口
外部设备与主机(CPU和z存储器)的通信是通过外设接口(Interface)进行的,每个接口包括一组寄存器。
数据寄存器:存放外设和主机间传送的数据。
状态寄存器:保存外设或接口的状态信息。
命令寄存器:保存CPU发给外设或接口的控制命令。
外设中每个寄存器有一个端口(Port)地址,构成一个独立于内存的I/O地址空间:0000H ~ 0FFFFH。
专用的I/O指令: IN和OUT。
BIOS和DOS功能调用。
CPU和外设(比如键盘、打印机、硬盘等)的工作速度差异很大,而且它们”说的语言”也不同。接口就像是一个翻译和缓冲区,帮助它们能够顺利通信。
接口中的三种关键寄存器各自担任不同的角色:
- 数据寄存器: 这就像是一个传递文件的托盘。当键盘输入一个字符时,这个字符会先放在数据寄存器中,然后CPU才能来取走它。反过来,当CPU要向打印机发送数据时,也是先把数据放在这个”托盘”中。
- 状态寄存器: 这像是一个状态指示牌,告诉CPU外设当前的工作状态。比如,打印机是否缺纸、键盘是否有按键按下、数据是否准备好等。CPU通过查看状态寄存器,就知道外设是否准备好进行数据传输。
- 命令寄存器: 这相当于一个指令箱,CPU通过它发送控制命令给外设。比如,让打印机开始打印、让硬盘开始读取数据等。
端口地址空间(0000H~0FFFFH)是一个很巧妙的设计: 这就像是给每个接口寄存器都分配了一个门牌号。这些地址与内存地址是分开的,形成了一个独立的地址空间。这样,CPU就可以明确知道它是在与内存通信还是在与外设通信。
为了使用这些端口,处理器提供了专门的指令:
IN指令:从指定端口读取数据(就像从外设收信)OUT指令:向指定端口写入数据(就像给外设发信)
举个具体的例子: 假设键盘的数据端口地址是60H,状态端口是64H。当我们按下一个键时:
- CPU通过
IN指令从64H端口读取状态,检查是否有新数据 - 如果有新数据,就通过
IN指令从60H端口读取按键数据
(下面代码抄的, 没跑过)
; 从键盘读取数据的简单例子mov dx, 60h ; 60h是键盘数据端口in al, dx ; 从端口读取一个字节到AL寄存器
; 向打印机发送数据的例子mov dx, 378h ; 378h是打印机数据端口mov al, 'A' ; 要打印的字符out dx, al ; 发送字符到打印机端口; 完整的键盘读取程序check_keyboard: mov dx, 64h ; 键盘状态端口wait_key: in al, dx ; 读取键盘状态 test al, 1 ; 测试是否有数据可读(第0位) jz wait_key ; 如果没有数据,继续等待
mov dx, 60h ; 键盘数据端口 in al, dx ; 读取按键扫描码
; 此时AL中包含了按键的扫描码实模式存储器寻址

寻址方式
立即寻址
(立即数寻址)
mov al, 5mov ax, 3064h寄存器寻址
mov ax, bxmov al, bh直接寻址
mov ax, [2000h]寄存器间接寻址
mov ax, [bx]mov ax, es:[bx]mov ax, [bp]寄存器相对寻址

mov ax, count[si]mov ax, [count+si]基址变址寻址

mov ax, [bx][di]mov ax, [bx+di]mov ax, es:[bx][si]WARNING必须是一个基址寄存器和一个变址寄存器的组合。
相对基址变址寻址

mov ax, mask[bx][si]mov ax, mask[bx+si]mov ax, [mask+bx+si]汇编语言语法
标志位
伪指令
1. 数据定义伪指令:DB、DW、DD…
; DB - 定义字节(1字节)DATA_BYTE DB 100 ; 定义一个字节,值为100; DW - 定义字(2字节)DATA_WORD DW 100, 100H ; 定义两个字; 重复定义DATA_DUP DB 100 DUP (?) ; 定义100个未初始化的字节2. 字符串定义:MESS1 DB 'AB'
MESS1 DB 'AB' ; 存储为两个字节:41H, 42HMESS2 DW 'AB' ; 存储为一个字:4241H3. 类型转换指令:PTR
OPER1 DB 1, 2OPER2 DW 1234H, 5678H
; 错误示例MOV AX, OPER1+1 ; 错误:类型不匹配MOV AL, OPER2 ; 错误:类型不匹配
; 正确用法MOV AX, WORD PTR OPER1+1 ; 将字节数据作为字访问MOV AL, BYTE PTR OPER2 ; 将字数据作为字节访问4. 标号定义:LABEL
; 定义一个数据区可以用两种不同的方式访问BYTE_ARRAY LABEL BYTEWORD_ARRAY DW 50 DUP (?)5. 常量定义伪指令:EQU、=
; EQU - 定义常量,不能重定义CONSTANT EQU 256ALPHA EQU 7BETA EQU ALPHA-2
; "=" - 可重定义的常量EMP = 7EMP = EMP + 1 ; 允许重新赋值EQU (equate) 是一个比较严格的常量定义伪指令。“不能重定义”的意思是,一旦你使用 EQU 定义了一个常量,就不能再给这个常量赋新的值。
; 这样是正确的 - 第一次定义ALPHA EQU 7
; 这样是错误的 - 试图重定义 ALPHAALPHA EQU 8 ; 汇编器会报错!
; 这样也是错误的ALPHA EQU ALPHA + 1 ; 汇编器会报错!相比之下,使用=定义的常量是可以重新定义的。
; 使用 = 定义常量COUNT = 7; 可以重定义COUNT = 8 ; 正确!COUNT = COUNT + 1 ; 正确!这种区别在实际编程中很重要:如果你想定义一个绝对不应该改变的常量(比如硬件端口地址),应该用 EQU;如果你需要一个可以更新的计数器或变量,应该用 “=”
这就像现代高级语言中的:
const(常量,不能改变)相当于 EQU
let/var(变量,可以改变)相当于 “=”
6. 地址对齐伪指令
; ORG - 设置起始地址ORG 100HSTART:
; EVEN - 偶地址对齐A DB 'morning'EVEN ; 确保下一个数据从偶地址开始B DW 2 DUP (?)
; ALIGN - 按指定边界对齐ALIGN 4 ; 按4字节边界对齐7. 地址计数器:$
; 跳过空间ORG $+8 ; 跳过8个字节
; 在数组中使用ARRAY DW 1, 2, $+4, 3, 4, $+48. 基数控制
.RADIX 16 ; 设置默认数字为16进制MOV BX, 0FF ; 16进制的FFMOV BX, 178D ; 10进制的178 (D后缀表示十进制)