Pil0tXia 的书房 Pil0tXia 的书房
首页
  • 第一章 操作系统引论
  • 第二章 进程的描述与控制
  • 第三章 处理机调度与死锁
  • 第四章 存储器管理
  • 第五章 虚拟存储器
  • 期末考试备考
汇编语言课程笔记
GitHub (opens new window)
首页
  • 第一章 操作系统引论
  • 第二章 进程的描述与控制
  • 第三章 处理机调度与死锁
  • 第四章 存储器管理
  • 第五章 虚拟存储器
  • 期末考试备考
汇编语言课程笔记
GitHub (opens new window)
  • 汇编语言课程笔记

    • 课堂速记
      • 引入 进制转换
        • 十六转十进制
        • 十六转二进制
        • 十进制转十六
        • 二进制转十六
        • 十进制转二进制
        • 二进制转十进制
        • 巧算法
      • 第二章 寄存器
        • 8086 访问地址
        • CS:IP
      • 第三章 寄存器(内存访问)
        • 内存中字的存储
        • 例1
        • 例2
        • 堆栈
        • 先进后出
        • SS:SP
      • 第四章
        • 源程序中的“程序”
        • 汇编程序&伪指令
        • 编译&连接
        • 谁将可执行文件中的程序装载进入内存并使它运行?
        • EXE文件中的程序的加载过程 DS
        • 程序执行过程的跟踪
      • 第六章 包含多个段的程序
        • 在代码段中使用数据
        • 在代码段中使用栈
        • 将数据、代码、栈放入不同的段
      • 第七章 更灵活的定位内存地址的方法
        • ASCII码
        • [bx+idata] 寄存器相对寻址
        • SI和DI 基址变址寻址
        • 相对基址变址寻址([bx+si+idata]和[bx+di+idata])
      • 第八章 数据处理的两个基本问题
        • bx,si,di,bp
        • 寻址方式
        • 指令要处理的数据有多长?
        • div指令 书P169
      • 第九章 转移指令的原理
        • offset
        • jmp
      • 第十一章
        • PF标志(Parity)
        • SF标志(Sign)
        • ZF标志(Zero)
        • CF标志(Carry)
        • OF标志(Overflow)
        • adc指令
        • sbb指令
        • cmp指令
        • je指令等
        • DF标志和串传送指令
      • 第十二章 内中断
      • 第十三章 int指令
        • 13.5 BIOS和DOS中断例程的安装过程
      • 第十四章
        • shl和shr指令
      • 期末考试复习
  • 《汇编语言》第三版阅读笔记

    • 第一章 基础知识
    • 第二章 寄存器
    • 第三章 寄存器(内存访问)
    • 第四章 第一个程序
    • 第五章 [BX]和loop指令
    • 第六章 包含多个段的程序
    • 第七章 更灵活的定位内存地址的方法
    • 第八章 数据处理的两个基本问题
    • 第九章 转移指令的原理
    • 第十章 CALL和RET指令
    • 第十一章 标志寄存器
    • 第十二章 内中断
  • 汇编语言
  • 汇编语言课程笔记
Pil0tXia
2023-01-05
目录

课堂速记

# 课堂速记

本笔记使用教材《汇编语言(第三版)》,王爽,清华大学出版社

# 引入 进制转换

# 十六转十进制

就用乘法,每一位乘以 16^0, 16^1, 16^2 ... 然后加在一起。

举个例子,ff bf 是几?答:65471

# 十六转二进制

更简单了,只需把每一位,变成二进制的四位数,然后拼在一起。

看个例子就懂了,ff bf 是二进制的几?答:1111 1111 1011 1111

# 十进制转十六

稍微复杂些,用短除法。每次除以 16,把余数从下到上拼起来,就得到了 16 进制的数。

来一起试试,65471 是十六进制的几?答:ff bf

# 二进制转十六

又容易了,只需切成 4 个 4 个的小段,把每段对应的字母 / 数字拼在一起,就可以了。

比如这个例子,11 1111 1011 1111 是几?答:3f bf

# 十进制转二进制

除二取余,倒序排列,高位补零。

# 二进制转十进制

同十六转十进制,但 16 改成 2

# 巧算法

如何快速把 2^n 的十进制数,转换为二进制?

只需把 n 除以 4,得到 j 余 i。把 i 变成 2^i 做为第一位,其余的就是,j 是几就跟几个零。

# 第二章 寄存器

# 8086 访问地址

image-20220913160043333

image-20220913160006739

image-20220913160226911

image-20220913160311958

一个段的最大大小为 2^16=65536=64K,此为偏移地址的最大表示大小 FFFFH。

image-20220913160354815

# CS:IP

代码段的段地址存放在 CS 中,指令指针寄存器 IP 指示代码段中指令的偏移地址,处理器利用 CS:IP 取得下一条要执行的指令。

image-20220913160541141

# 第三章 寄存器(内存访问)

# 内存中字的存储

image-20220913155716253

(1) 20H (2) 4E20H

字节型数据<字型数据。一个字型数据 (如 1234H) 存放在内存中,由 2 个连续的地址的内存单元组成。高地址内存单元存放字型数据的高位字节,低地址内存单元存放字型数据的低位字节。

image-20220913160921444

image-20220913182153695

image-20220913182321717

mov 的大小就是 al 的大小

image-20220913175140878

image-20220913182520984

此处 “一般的寄存器” 就是上例中的 bx

# 例 1

image-20220920084151074

# 例 2

image-20220920090126282

1200+7C0A+4532+A963(舍弃溢出)

# 堆栈

8086CPU 入栈出栈都以字为单位,不能 push/pop 一个 al

# 先进后出

image-20220920090712907

# SS:SP

image-20220920090806265

image-20220920091135542

入栈出栈时 SP 会先进行 - 2/+2 的操作,push 时 SP 向上(低位)移动 - 2。然后将数据送入 SS:SP 指向的内存单元处。

image-20220920094349099

image-20220920094416090

image-20220920094504975

image-20220920094531191

image-20220920094635326

image-20220920095356999

# 第四章

# 源程序中的 “程序”

汇编源程序:

  • 伪指令(编译器处理)
  • 汇编指令(编译为机器码)

程序:源程序中最终由计算机执行、处理的指令或数据。

# 汇编程序 & 伪指令

codesg:标号,放在 segment 的前面,作为一个段的名称,这个段的名称最终将被编译、连接程序处理为一个段的段地址。

image-20220927170327619

image-20220927170240588

# 编译 & 连接

当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;

程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;

一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。

所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。

# 谁将可执行文件中的程序装载进入内存并使它运行?

在 DOS 中,可执行文件中的程序 P1 若要运行,必须有一个正在运行的程序 P2 将 P1 从可执行文件中加载入内存,将 CPU 的控制权交给它,P1 才能得以运行;当 P1 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P2。

(1)我们在 DOS 中直接执行 1.exe 时,是正在运行的 command 将 1.exe 中的程序加载入内存。

(2)command 设置 CPU 的 CS:IP 指向程序的第一条指令(即程序的入口),从而使程序得以运行。

(3)程序运行结束后,返回到 command 中,CPU 继续运行 command

image-20220927201137970

# EXE 文件中的程序的加载过程 DS

image-20220927203832279

程序加载后,ds 中存放着程序所在内存区的段地址,这个内存区的偏移地址为 0,则程序所在的内存区的地址为:ds:0;

这个内存区的前 256 个字节中存放的是 PSP,dos 用来和程序进行通信。

所以,我们从 ds 中可以得到 PSP 的段地址 SA,PSP 的偏移地址为 0,则物理地址为 SAX16+0。

因为 PSP 占 256(100H)字节,所以程序的物理地址是:

SA×16+0+256= SA×16+16×16= (SA+16)×16+0

可用段地址和偏移地址表示为:SA+10:0

# 程序执行过程的跟踪

用 R 命令查看各个寄存器的设置情况

image-20220927211120745

用 U 命令查看其他指令

image-20220927211023177

使用 P 命令执行 int 21

使用 Q 命令退出 Debug

# 第六章 包含多个段的程序

# 在代码段中使用数据

IMG_20221004_082246

程序解读见书 P124。 mov ax,4c00h 代表终止。

程序 6.2

end start 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。

# 在代码段中使用栈

程序 6.3

30h 是 48 字节,正好对应 dw 分配的 16 个字型数据,用于栈的空间。

# 将数据、代码、栈放入不同的段

程序 6.4

cs 是自动装载的,不用在代码段中指定 cs 的指向。

  • 为什么 mov bx,0 可以将 ds:bx 指向 data 段中的第一个单元?

    因为 0 被认为是 ds,[0]

  • 为什么 mov cx,5 表示循环 5 次?

    cs 用来控制循环次数,每次执行 loop 指令时,都会检查 cs 的值是否为 0

# 第七章 更灵活的定位内存地址的方法

# ASCII 码

程序 7.1

inc 加 1,而不是加 2,因为一个 ASCII 码占一个字节

# [bx+idata] 寄存器相对寻址

[bx+idata] 表示一个内存单元,它的偏移地址为 (bx)+idata(bx 中的数值加上 idata)

mov ax,[bx+200]

数学化描述:(ax)=((ds)*16+(bx)+200)

# SI 和 DI 基址变址寻址

类似于 bx,但是不能分成两个 8 位寄存器

段寄存器、两个内存单元之间、两个段之间都不能直接 mov,需要用寄存器中转

SI 元变址寄存器

DI 目的变址寄存器

# 相对基址变址寻址([bx+si+idata] 和 [bx+di+idata])

问题 7.1、7.3、7.4、7.5 的分析很实用

# 第八章 数据处理的两个基本问题

用 reg 表示一个寄存器,sreg 表示段寄存器

# bx,si,di,bp

bp 用于在堆栈段上寻址,bs 默认用于数据段寻址。都是基址 (base)

“两个 i 不相见,两个 b 不相见”

错误指令:

mov ax,[bx+bp]

mov ax,[si+di]

只要在 [...] 中使用寄存器 bp,且指令中没有显性地给出段地址,段地址就默认在 ss 中。

# 寻址方式

P164 表 8.2

# 指令要处理的数据有多长?

word ptr 和 byte ptr 来显式的指定内存单元的长度

P166: mov word ptr [1000H],1 -> 0100FF

# div 指令 书 P169

div byte ptr ds:[0]

(al) = (ax) / ((ds) * 16 + 0) 的商

(ah) = (ax) / ((ds) * 16 + 0) 的余数

div word ptr es:[0]

(ax) = [(dx) * 10000H + (ax)] / ((es) * 16 + 0) 的商

(dx) = [(dx) * 10000H + (ax)] / ((es) * 16 + 0) 的余数

低商高余

例题

mov ax,data

mov ds,ax

mov ax,ds[0]

mov dx,ds[2]

div word ptr ds:[4]

# 第九章 转移指令的原理

# offset

P175、176

问题 9.1 为什么要加 cs: ?

不加冒号复制的是默认 ds 段

# jmp

讲得太快

# 第十一章

# PF 标志(Parity)

表示奇偶性,1 的个数为奇数时 PF=0,为偶数个时 PF 为 1

# SF 标志(Sign)

结果为负那么 SF=1,结果非负数则 SF=0;

# ZF 标志(Zero)

结果为 0 那么 ZF=1, 结果不为 0 则 ZF=0;

# CF 标志(Carry)

mov al,97H

sub al,98H

执行后:(al)=FFH, CF=1, CF 记录了向更高位的借位值

10010111

10011000

(-1)11111111

FFH

# OF 标志(Overflow)

mov al,98
add al,99
1
2
1
2

执行后 CF=0, OF=1

对于无符号数运算,没有进位,CF=0;对于有符号数运算,发生了溢出(数值位向符号位进了一位,虽然污染了符号位,但此时数还是八位。如果进到第九位,则产生了进位),OF=1

# adc 指令

mov ax,1
add ax,ax
adc ax,3
1
2
3
1
2
3

adc 利用了 CF,执行时相当于计算 (ax)+3+CF=2+3+0=5

计算 1EF000H+201000H,结果放在 ax(高 16 位)和 bx(低 16 位)中

mov ax,001EH
mov bx,F000H
add bx,1000H
adc ax,0020H
1
2
3
4
1
2
3
4

# sbb 指令

adc 的减法版

# cmp 指令

不保存结果的减法比较,仅仅根据结果设置标志位

IMG_20221101_091647

cmp 比较大小不能仅靠 SF,因为可能溢出,还需要借助 OF

  • SF=1, OF=0,说明没有溢出,逻辑上结果正负 = 实际上结果正负,即 ah<bh

  • SF=1, OF=1,溢出会污染符号位,正负性颠倒,即 ah>bh

  • SF=0, OF=1,跟刚才逻辑一样,ah<bh

  • SF=0, OF=0,ah>bh;若 ZF=0,则 ah=bh

任意一个为 1,另一个为 0 时,前者<后者

# je 指令等

IMG_20221101_093204

有符号位的是 jl(小于)、jg(大于)

# DF 标志和串传送指令

df=0 每次操作后 si、di 递增

df=1 每次操作后 si、di 递减

movsb 将 ds:si 指向的内存单元的一个字节送入 es:di 中,然后根据 df 位的值,将 si 和 di 递增或递减

movsw

rep

# 第十二章 内中断

中断向量表 四个单元

do0

# 第十三章 int 指令

int n,n 是中断类型码,功能是引发中断,相当于引发一个 n 号中断的中断过程,执行过程:

标志寄存器入栈,IF=0,TF=0;CS、IP 入栈;(IP)=(n*4), (CS)=(n*4+2)

# 13.5 BIOS 和 DOS 中断例程的安装过程

编程时可以用 int 指令调用 BIOS 和 DOS 提供的中断例程

//不重要
mov ah,9 //调用设置光标的子程序
mov al,'a'
mov bh,7 //颜色设置
1
2
3
4
1
2
3
4

# 第十四章

# shl 和 shr 指令

逻辑移位指令。将一个寄存器或内存单元中的数据向左移位,最后移出的一位写入 CF 中,最低为用 0 来补充。移动位数大于 1 时,必须把移动位数放在 cl 中。

shl 左移,shr 右移。会影响到符号位。(算术右移不会影响符号位)

# 期末考试复习

选择,可能有判断题

写一些指令,进行纠正

2 道编程题

宏不作要求

一直到系统调用,比较重要

https://github.com/sanmianti/AssemblyLanguageTest/blob/master/%E3%80%8A%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E3%80%8B%E7%AC%AC%E4%B8%89%E7%89%88%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md

上次更新: 2023/01/06, 19:28:46

第一章 基础知识→

Copyright © 2022-2023 Pil0tXia | CC BY-NC-SA 4.0 Licensed | 苏ICP备2023001491号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式