汇编指令:机器码助记符 有对应机器码
伪指令:没有对应机器码 由编译器执行 计算机不执行
其他符号:如+ - * / 等 又编译器识别 计算机不执行
存储器 = 128存储单元 = 128Byte
Byte = 8bit =一个内存单元
1bit = 1个二进制位 一个bit只能表示一个开关量,例如 l代表“开关闭合”,0代表“开关断开”。
地址总线 发出地址信息
控制总线 发出内存读命令 选中存储芯片 通知他从中读取数据
数据总线 将读取到的数据送入CPU
地址总线 一根线表示一位 N根表示地址总线宽度为N,寻址范围为0-2^N。
数据总线 总线宽度决定了传输的速度,8根表示一个1Byte,16根表示2Byte.
控制总线 总线宽度决定了CUP对外部器件的控制能力
CUP通过总线于各个物理存储器相连,将他们看作一个个存储器单元,他们分别占据一个地址段,CUP在这段地址空间读写数据 实际上就是在相应的物理存储器上读写数据。而汇编语言就是面对的内存地址空间。
CPU内部
内存单元位线性空间 拥有唯一地址 即为物理地址
物理地址 = 段地址 * 16 + 偏移地址
下图对物理地址左移做了很详细的总结
那么 16位地址 * 16 其实是乘10H,也可以说是16位地址转成10进制成16。最简单的方式就是16位地址左移1位
本质:基础地址+偏移地址 = 物理地址
基础地址 = 段地址 * 16
书中22对其本质做出了生动的解释
当进入指令缓冲器 即为读取一条指令 IP 值自动增加并读取下一条指令根据第一条指令的大小增加值
复位或者加电启动 CS=FFFFH IP=0000H FFFF0H即为8086PC启动执行的第一条指令
jmp 1000:3 就是跳转到1000*16+0003H = 10003H处读取指令。
先找个引路篇-安转篇https://blog.csdn.net/try2find/article/details/39619999
以及debug的命令行使用 https://blog.csdn.net/liuer2004_82/article/details/52638516#t2
书中51页对字的传送做了图文并茂的介绍 表3.2
我学会debug指令 r,t,d查看 a修改
数据段:将一组长度为N(N<=64KB),地址连续,起始地址为16的倍数的内存单元当做专门存储数据的内存空间,即为数据段。
各寄存器初始值:CS = 2000H,IP = 0,DS = 1000H,AX = 0,BX = 0;
栈满push 栈空pop都会造成栈超界问题
8086CPU并没有给出解决方法 需自己注意
//基本操作
mov ax,1000H
mov ss,ax ;设置栈的段地址 值必须由ax传递
push [0] ;将1000:0处的字压入栈中
pop [2] ;出栈 出栈的数据送入1000:2中
push时:CPU操作:先改变SP:SP-2 后向SS:SP处传送
pop时: CPU操作:先读取SS:SP处的数据,后改变SP :SP+2
-r ds
:1000
-d ds:0 ;查看1000:0开始的内存区间 128字节
-r ds
:1000
-d ds:10 18 ;查看1000:0~1000:18 中的内容
-d cs:0 ;查看当前代码段中的指令代码
-d ss:0 ;查看当前栈中的内容
E 直接修改内存
U 显示代码 以汇编的形式
A 汇编指令修改内存
-r ds
:1000
-e ds:0 11 12 33 11 44 ;在从1000:0开始的内存区间写入数据
-u cs:0 ;以汇编指令的形式,显示代码段中的代码,0是段地址
-r ds
:1000
-a ds:0 ;以汇编指令的形式,向1000:0开始的内存单元写入指令
assume cs:codesg;‘假设’编译程序将段寄存器和某个具体的段相关联
codesg segment ;与codesg ends 成对使用 定义一个代码段,名称为‘codesg’
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax
mov ax,4c00H
int 21H
codesg ends
end ;汇编程序结束的标志
目的 | 相关指令 | 指令性质 | 指令执行者 |
---|---|---|---|
通知编译器一个段结束 | 段名 ends | 伪指令 | 编译时 编译器 |
通知编译器程序结束 | end | 伪指令 | 编译时 编译器 |
程序返回 | mov ax,4c00H int 21H | 汇编指令 | 编译时 由CPU行 |
首先需要一套工具 这边提供下载链接 https://blog.csdn.net/wybliw/article/details/53259602
设置完环境后打开dosbox 输入Masm 输入文件路径 一路enter会得到 .OBJ后缀文件 而实际上会得到三个文件
输入Link 然后输入文件名 一路enter 生成 exe
https://blog.csdn.net/lovefengruoqing/article/details/79009456
mov cx ,循环次数
s:
循环执行程序块
loop s
使用[bx]间接传递偏移地址
mov ax , 2000H
mov ds , ax ;段地址2000H送入ds
mov bx , 0 ;偏移地址0送入bx
mov al , [bx] ;ds:bx的送入al
或者
mov al , ds:[0]
assume cs:code ;与系统cs相关联 固定写法
code segment ;代码段 名称 'code'
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ;声明字型数据
mov bx,0 ;赋值语句
mov ax,0 ;赋值语句
mov cx,8 ;循环8次
s:add ax,cs:[bx] ;(ax) =((cs*16)+bx)
add bx,2 ;偏移地址+2
loop s
mov ax,4c00h ;类似于return 固定写法
int 21h
code ends
end ; 代码段结束标记 固定写法
assume cs:code ;与系统cs相关联 固定写法
code segment ;代码段 名称 'code'
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ;声明字型数据
start: mov bx,0 ;赋值语句
mov ax,0 ;赋值语句
mov cx,8 ;循环8次
s:add ax,cs:[bx] ;(ax) =((cs*16)+bx)
add bx,2 ;偏移地址+2
loop s
mov ax,4c00h ;类似于return 固定写法
int 21h
code ends
end start ; 代码段结束标记 固定写法
assume cs:code,ds:data,ss:stack ;关联系统段地址 :固定写法
data segment ;自定义段 data
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h ;16字节内存单元
data ends ;段结束
stack segment ; 自定义栈 stack
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;16字节内存单元
stack ends ;段结束
code segment ;自定义代码段 code
start:mov ax,stack ;start 开始标记 stack段地址给寄存器ax
mov ss,ax ;ax赋给栈段地址
mov sp,20h ;设置栈顶ss:sp 指向stack:20
mov ax,data ;data段地址给寄存器ax
mov ds,ax ;ds 指向 data
mov cx,8 ;设置循环次数8
s:push [bx] ;将ds:[bx]的值入栈,也就是data:0~15的16个字型数据
add bx,2 ;bx 自加 2
loop ;循环标志 固定写法
mov bx,0 ;将bx置空
mov cx,8 ;循环标志 固定写法
s0:pop [bx] ;将栈stack依次出栈 赋值给 ds:[bx]
add bx,2 ;bx 自加 2
loop s0 ;循环标志 固定写法
mov ax,4c00h ;结束标志 固定写法
int 21h
code ends ;代码段结束标记
end start ;程序结束标记
and: 与指令 同为1 则为1
or : 只要有1 则为1
mov al 1100100B
add al 1000000B
//执行和结果为 10000000B
mov al 1000111B
or al 1110111B
//执行结果为 1110111B
ASCII码表 http://ascii.911cha.com/
这边先介绍一下 db dw dd
这里有意思的是
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s:mov al,[bx] ;取出ds:bx所指的第一个字母
and al,11011111B ;将第五位置0 就是-20H
mov [bx],al ;将值返还给ds:[bx]
inc bx
loop s
mov bx,5 ;从ds:5开始指向information的第一个字母
mov cx,11
s0:mov al,[bx]
or al,00100000B ;将第五位置1 就是+20H
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
mov al,[5+bx]
mov [5+bx],al
mov ax ,[bx][si]
//等同于
mov ax,[bx=si]
//描述为
(ax) = (ds*16 + bx + si)
定义如下数据段 6个字符串 等同于下图 的一个
datasg segment;注意每个字符被扩展为16位
db '0. file '
db '1. edit '
db '2. search '
db '3. veiw '
db '4. options '
db '5. helps '
datasg ends
最终:是将datasg数组中的数据全部大写
datasg segment;注意每个字符被扩展为16位
db 'fil '
db 'edi '
db 'sea '
db 'vei '
datasg ends
mov ax,datasg
mov ds,ax
mov bx,0 ;ds:bx 指向datasg
mov cx,4 ;循环次数4行
s0:mov si,0 ;外层循环 第一行 当si=3退出循环
mov cx,3 ;循环次数3列
s: mov al,[bx+si] ;内层循环 ds:[bx][si] 理解为 第bx行 第si列
and al,11011111b ;第五位置0 也就是-20H 也就是 小写 -> 大写
mov [bx+si],al ;
inc si si加1
loop s
add bx,16 ;外层循环 +16 即+10H 即换行
loop s0
用栈的方式暂时保存cx的值
datasg segment;注意每个字符被扩展为16位
db 'fil '
db 'edi '
db 'sea '
db 'vei '
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg segment
codesg segment
start:mov ax ,stacksg
mov ss,ax
mov sp,16 ;ss:sp 指向 stacksg:10H
mov ax,datasg
mov ds,ax
mov bx,0 ;ds:bx 指向 datasg:0
mov cx,4
s0: push cx ;入栈 cx
mov si,0
mov cx,3
s: mov al,[bx+si] ;(al) = (ds*16+bx+si) 也就是[bx][si] bx行si列
and al,11011111b ;改成大写
mov [bx+si],al ;(ds*16+bx+si) = (al)
inc si ;si自加1
loop s
add bx,16 ;换行
pop cx ;出栈 给cx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
处理数据在什么地方?
要处理的数据有多长?
reg:寄存器 ax bx cx dx al ah bl bh cl ch dl dh cl ch sp bp si di
sreg:段寄存器 ds cs ss es
机器码 | 汇编指令 | 指令执行前数据的位置 |
---|---|---|
8E1E0000 | mov bx,[0] | 内存,ds:0单元 |
89c3 | mov bx,ax | CPU内部,ax寄存器 |
BB0100 | mov bx,1 | CPU内部,指令缓冲器 |
实例
div word ptr es:[0]
(ax) = [(dx)*10000H+(ax)]/((es)*16+0)的商 //注意这里是10000H
(dx) = [(dx)*10000H+(ax)]/((es)*16+0)的余数
div byte ptr ds:[0]
(al) = (ax)/((ds)*16+0)的商
(ah) = (ax)/((ds)*16+0)的余数
实例计算 100001/100
mov dx,1
mov ax,86a1h
mov bx,100
div bx
根据公式一:
100 = 64H
答案地址 https://blog.csdn.net/love_jing_forever/article/details/53204818
定义:可以修改IP,或同事修改CS和IP的指令统称为转移指令。
转移行为:
对IP修改范围不同
指令分类
start:mov ax ,offset start ;相当于mov ax ,0
s:mov ax ,offset s ;相当于 mov ax s
jmp指令并不需要转移的目的地址 只需要转移的位移
assume cs:codesg
codesg segment
start:mov ax,0
jmp short s ;short 表示范围在 -128~127之间 即‘段内短转移’
add ax,1
s:inc ax
codesg ends
end start
8位无符号数 00000000B~11111111F 共255个数
8位有符号数
10000000B~11111111F -127~127 共254个数 注意:10000000B不算-0没有意义
最高位为1 表示负数
整数补码取反加1或,为其对应负数的补码,负数补码取反后加1,位其绝对值
1的补码:11111111B,表示-1
-1的补码:00000001B,其绝对值为1.
补码计算法定义:非负数的补码是其原码本身;负数的补码是其绝对值的原码最高位符号位不变,其它位取反,再加1。
模定义:一个负整数与其补码的和。
整理下对补码的知识
0000H-0009H
因为指定jmp short 转成二进制:
00000000B-00001001B
应为没有减法器 所以理解为加法
00000000B+(-00001001B)
也就是求-9(10001001B)的补码
10001001B 取反且首位不变(11110111B)
即F7H
mov ax ,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
//(CS)=0,(IP)=0123H,CS:IP指向0000:0123
JCXZ CX: 为零时转移
格式:jcxz 标号
用高级语言描述:if (cx == 0)
jmp short 标号 ;------> 当cx= 0 转移到标号处
else
next ;------>当cx != 0 程序什么也不做,继续向下执行
(短转移,通过位移进行转移,IP修改范围-128~127
实例:
assume cs:codesg
stack segment
db 16 dup (0) ;申请一个16字节的空间
stack ends
code segment
mov ax,4c00h
int 12h
start: mov ax,stack ;代码开始部分
mov ss,ax
mov sp,16
mov ax,0
push cs
push ax
mov bx,0
retf cs:IP指向 cs:ax即 cs:0000H;代码第一行mov ax,4c00h
code ends
end start
call 标号
汇编表示: 入栈IP后
(SP) = (SP)-2
((ss)*16+(sp))=(IP) 将IP对应的值存入ss:sp中 sp偏移向上2个字节
(IP)=(IP)+16
push IP
jmp near ptr 标号 ;对应(IP)=(IP)+16 就是跳过一行
```c
cal far ptr 标号 段间转移
汇编表示:入栈CS 和 IP后
push CS
push IP
jmp far ptr 标号 cs:ip指向 标号处
call 16位reg
汇编表示:
```c
push IP
jmp 16位 reg ; 直接跳转到ss:reg位置
call word ptr
汇编表示: 跳转到 标号处 SP+2
push IP
jmp word ptr 内存单元地址
call dword ptr
汇编表示: 跳转到 标号处 SP+4
push CS
push IP
jmp word ptr 内存单元地址
对于结果:
mul byte ptr ds:[0]
;含义: (ax) = (al)*((ds)*16+0); (ax) = (al)*(ds:bx)
mul word ptr [bx+si+8]
;(ax) = (ax)*((ds)*16+(bx)+si+8)结果的底16位
;(dx) = (ax)*((ds)*16+(bx)+si+8)结果的高16位
计算100*10
分析:100和10 都小于255 可以做8位乘法
mov al ,100
mov bl ,10
mul bl
结果:(ax) = al*bl = 1000 = 0e38H
计算10000*100
100小于255,10000大于255,所以做16位乘法
mov ax,100
mov bx,10000
mul bx
结果: ax*bx = F4240 = 11110100001001000000 =
1111 0100 0010 0100 0000
(ax) = 4240H dx = 0000 0000 0000 1111 = 0000FH
子程序:也就java中的Util方法 或者普通方法
将
assume cs:code
data segment
dw 1,2,3,4,5,6,7,8 ;这边申请的是word 也就是一个字16位
dd 0,0,0,0,0,0,0,0 ;这边申请的是dword 也就是一个双字32位
data ends
code segment
start:mov ax,data
mov ds,ax
mov si,0 ;ds:si 指向第一组word单元
mov di,16 ;ds:di 指向第二组的dword单元
;si di等同于bx 不能分成两个8位
mov cx 8 ;循环8次
s:mov bx,[si] ;(bx) = ([ds:si])=([ds:0])=1
call cube ;假设bx = 8
mov [di],ax ;([di]) = 0040H 低16位
mov [di].2,ax ;向后移动2个字节 高16位
add si,2 ;ds:si 指向下一个word单元
add di,4 ;ds:di 指向下一个dword单元
loop s
mov ax,4c00h
int 21h
cube:mov ax,bx ;该方法被调用 因为bx小于255(FF) 用8位
mul bx ;(ax) = (al)*(bx) = 0*H*0008H =8 * 8 = 64 =0040H
mul bx ;(ax) = (al)*(bx) = 04H*0040H =4 * 64 = 64 =0100H
ret ;返回调用处的下一行,即:mov [di],ax
code ends
end start
作者告诉我们为什么定义字符串使用 db https://blog.csdn.net/weixin_42553435/article/details/80919722
assume cs:code
data segment
db 'conversation'
;存的byte字节 是顺序的
;可以理解成 db 'c','o','n','v','e','r','s','a','t','i','o','n' 总共12个字符
data ends
code segment
start:mov ax,data
mov ds,ax
mov si,0
mov cx,12
call capital
mov ax,4c00h
int 21h
capital:and byte ptr [si],11011111B
inc si
loop capital
ret
code ends
end start
编写子程序的标准
子程序开始: 子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈
返回(ret,retf)
mov ax,1
sub ax,1
//ax=0,zf =1
mov ax,1
add al,10
//ax=00001011B,zf =0
mov al,10000001B
add al,1
//结果:(al) = 10000010B
//SF=1
mov al,10000001B
add al,01111111B
//结果:(al) = 100000000B //溢出则 去掉最高位1 取00000000B = 00H
//SF=0 结果非负数
mov al,98H
add al,al ;98H*2 = 0130H执行后:(al) = 30H, CF = 1,,CF记录最高有效位的更高位的进位值
add al,al ;30H*2 = 60H ,CF = 0
mov al,97H
sub al,98H ;借位后即: (al) = 197H-98H = FFH ,CF=1,记录向更高位的借位值
sub al,al ;(al) = FFH-FFH = 0H ,CF = 0
mov al,98
add al,97
;结果为::98+97 = 195
;实际结果位 195 = C3H=11000011B
//11000011B 为有符号数
//10111100B+1
//10111101B =-61
mov al,0F0H
; FOH 为有符号数 -16
//11110000
//10001111+1
//10010000
add al,088H
; 088H 为有符号数 -120
//10001000
//11110111+1
//11111000
;0F0H+ 088H 结果为 101111000
//101111000
//110000111+1
//110001000 -136 溢出
;但是实际结果为
//01111000 超界则去掉最高位
//78H
adc ax,bx 实现功能:(ax) = (ax)+(bx)+CF
mov ax,2
mov bx,1
sub bx,ax ;(bx)<(ax) 产生借位:CF=1
adc ax,1 ;(ax)+1+CF = 2+1+1=4
mov ax,8
mov bx,3
cmp ax,bx
//执行后:(ax)-(bx) =5, zf = 0,pf = 1,sf = 0,cf = 0,of =0;
指令 | 含义 | 检测相关标志位 |
---|---|---|
je(equal) | 等于则转移 | zf=1 |
jne | 不等于 | zf!=0 |
jb(below) | 低于 | cf=1 |
jnb | 不低于 | cf=0 |
ja(above) | 高于 | cf = 0 并且 zf = 0 |
jna | 不高于 | cf = 1 后者 zf = 1 |
cmp ah,bh
je s ;如果(ah)= (bh) 其实只要ZF = 1 就执行 :跳转到 s 否则向下执行
add ah,bh
jmp short ok
s:add ah,ah
ok:...
s:movsb
loop s
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
code segment
mov ax ,data
mov ds,ax
mov si,0 ;ds:si 指向 data:0
mov es,ax
mov di:16 ;es:di 指向 data:0010H
mov cx,16
cld ;df=0 正向传送
rep movsb
code ends
检测点11.4 记得这是存的flag 是一个16位的数
下面指令执行后,(ax)=0045h
mov ax,0 ;(ax)=0
push ax ;将(ax)入栈
popf ;将flag寄存器的所有为都初始化为0.因为它们使用的是同一个栈结构。
mov ax,0fff0h ;(ax)=0fff0H
add ax,0010h ;执行add后,结果是:(ax)=0000H(作为无符号数,产生进位cf=1),
;作为有符号数没有溢出,of=0;其它zf=1,sf=0,pf=1.我们把这些标 ; 位按照顺序组合起来(不能确定的我们不管了):00000xxx010x0101 ;我们不能确定的都用X表示。没有使用的用0表示吧
pushf ;将flag的值入栈。
pop ax ;弹栈,(ax)= 00000xxx010x0101B
and al,11000101B ;ax低8位按位与, 010x0101 and 11000101=01000101B=45H
and ah,00001000B ;ax高8位按位与,00000xxx and 00001000=0000000B=00H
CPU随时可能执行中断程序,所以中断程序必须一直存储在内存的某段中
iret指令功能用汇编描述:
pop IP
pop CS
popf
mov ax,1000h
mov bh,1
div bh
计算:
(al) = (ax)/((ds)*16+0)的商 = 1000h/0001H = 1000H (溢出 al范围为00H~FFH)
(ah) = (ax)/((ds)*16+0)的余数 = 0H
实现代码:
assume cs:code
code segment
start:do0 安装程序
设置中断向量表
mov ax,4c00h
int 21h
do0: 显示字符串“overflow!”
mov ax,4c00h
int 21h
code ends
end start
assume cs:code
code segment
start:mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset do0end-offset do0 ;do0 部分代码的长度 ;设置cx为传输长度
cld ;设置传输方向
rep movb
设置中环向量表
mov ax,4c00h
int 21h
do0: 显示字符串“overflow!”
mov ax,4c00h
int 21h
code ends
end start
这里存在一个问题 当code segment执行玩之后data
segment内存随时可能被释放。所以需将data实时生成。
do0代码不执行 只是装载到200H处
assume cs:code
data segment
db "overflow!"
data ends
code segment
start:mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset do0end-offset do0 ;do0 部分代码的长度 ;设置cx为传输长度
cld ;设置传输方向
rep movsb
;设置中环向量表
mov ax,4c00h
int 21h
do0: mov ax,data
mov ds,ax
mov si,0 ;设置ds:si指向字符串
mov ax,0b800H
mov es,ax
mov di,12*160+36*2 ;设置es:di指向显存空间的中间位置
mov cx 9 ;设置cx字符串长度
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s
mov ax,4c00h
int 21h
do0end:nop
code ends
end start
assume cs:code
code segment
start:mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset do0end-offset do0 ;do0 部分代码的长度 ;设置cx为传输长度
cld ;设置传输方向
rep movsb
;设置中环向量表
mov ax,4c00h
int 21h
do0: jmp short do0start
db "overflow!"
do0start: mov ax,cs
mov ds,ax
mov si,0 ;设置ds:si指向字符串
mov ax,0b800H
mov es,ax
mov di,12*160+36*2 ;设置es:di指向显存空间的中间位置
mov cx, 9 ;设置cx字符串长度
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s
mov ax,4c00h
int 21h
do0end:nop
code ends
end start
do0 入口地址0:200 0:0存偏移地址 0:200子单元存段地址.
中断后 并不会设置 ss:SP 且当执行栈操作时 不会去响应中断,此时我们应该将SS和SP连续存放。
mov ax,1000h
mov ss,ax
mov sp,0
格式:int n,n为中断类型码 引发中断
执行过程:
问题 编写安装中断7ch中断例程
求2*3456^2
第一步:
assume cs:code
code segment
start :mov ax,3456
int 7ch
add ax,ax
adc dx,dx
mov ax,4c00h
int 21h
code ends
end start
第二步:
assume cs:code
code segment
start :mov ax,cs
mov ds,ax
mov si,offset sqr
mov ax,0
mov di,200h
mov cx,offset sqrend-offset sqr ;安装子程序到 es:di 也就是 0:200H处
cld ;df=0 正向传送 di si 加1
rep movsb; 循环复制 (mov es:[di],byte ptr ds:[si] df=0)
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h ;es:[7ch*4]中断向量表 偏移地址
mov word ptr es:[7ch*4+2],0 ;es:[7ch*4+2]中断向量表 段地址
mov ax,4c00h
int 21h
sqr: mul ax
iret
sqrend:nop
code ends
end start
```c
其中iret为 恢复数 CS IP 和 flag
pop IP
pop CS
popf
###### int和iret和栈深入理解 书中256页已经变得很复杂了。可能是前面基础没打好。
**先休息一下 本文共17章 且 课后试验部分未做,可谓是任重而道远,未完待续**