8086汇编
ios
本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他同样共享资源、帮助他人的筒子们==本文比较长,因为笔者我的能力有限,错漏在所不免,欢迎读者们批评指正。程序员
总线是链接CPU和其余芯片的导线,逻辑上分为地址总线、数据总线、控制总线。编程
8086 CPU全部的寄存器是16位,能够存放2个字节(一个字)。windows
一字节由8 bit 组成,能够存在8位寄存器中。api
字(word)是两字节,16位。数组
汇编指令对大小写不敏感安全
汇编指令举例框架
汇编指令 | 控制CPU完成的操做 | 用高级语言的语法描述 |
---|---|---|
mov ax,18 | 将8送入AX | AX=18 |
mov ah,78 | 将78送入AH | AH=78 |
add ax,8 | 将寄存器AX中的数值加上8结果存入AX中 | AX=AX+8 |
mov ax,bx | 将寄存器BX中的数据送入寄存器AX | AX=BX |
add ax,bx | 将AX,BX中的内容相加结果存入AX中 | AX=AX+BX |
移位位数 | 二进制 | 十六进制 | 十进制 |
---|---|---|---|
0 | 10B | 2H | 2 |
1 | 100B | 4H | 4 |
2 | 1000B | 8H | 8 |
3 | 10000B | 10H | 16 |
4 | 100000B | 20H | 32 |
段地址*16是移位编辑器
人为定义的,将若干地址连续的内存单元看做一个段。用段地址*16定位段的起始地址(基址),用偏移地址定位段中的内存单元。
一个段的起始地址是16的倍数。偏移地址为16位,寻址能力为64K,因此段的最大长度也是64K。
一、从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器;
8086PC机刚开始启动时,CPU从内存FFFF0h单元中读取指令执行,FFFF0h单元中的指令时8086PC机开机后执行的第一条指令。
三、执行指令。转到步骤1,周而复始。
jmp 段地址:偏移地址;同时修改CS和IP jmp 某一合法寄存器;则是仅修改IP
windows xp系统自带debug,请使用xp以上系统的读者执行自行下载debug.exe和dosbox,使用方法笔者再也不赘述,在dosbox中可使用debug。
8086 CPU因为硬件的设计不支持将数据直接送入段寄存器的操做。
数据 -> 通用寄存器 -> 段寄存器
mov 寄存器名,内存单元
mov 寄存器,数据;mov ax,8 mov 寄存器,寄存器;mov ax,bx mov 寄存器,内存单元;mov ax,[0] mov 内存单元,寄存器;mov [0],ax mov 段寄存器,寄存器;mov ds,ax mov 寄存器,段寄存器;mov ax,ds ……
add 通用寄存器,数据 add 通用寄存器,通用寄存器 add 通用寄存器,内存单元 add 内存单元,寄存器
sub 通用寄存器,数据 sub 通用寄存器,通用寄存器 sub 通用寄存器,内存单元 sub 内存单元,通用寄存器
pop以后数据仍是存在内存中,push时覆盖。
CS和IP存放当前指令的段地址和偏移地址。
;push和pop格式 push 寄存器 pop 寄存器 push 段寄存器 pop 段寄存器 push 内存单元 pop 内存单元
编写完成的汇编语言程序,用编译器编译成可执行文件并在操做系统中运行。
LINKE.EXE对目标文件进行链接生产可在操做系统中直接运行的可执行文件。
可执行文件包含程序(机器码)、数据(源程序中定义的数据)和相关的描述信息。
assume cs:codesg ;假设代码段的名称为codesg codesg segment ;定义一个codesg段 mov ax,0123H mov bx,0456H add ax,bx add ax,ax mov ax,4c00h int 21h codesg ends ;codesg段结束 end ;是个伪指令,程序的结束标记
assume用来加上某一段寄存器和程序中的某一用segment……ends定义的段相关联。经过assume说明这种关联,在须要的状况下编译程序能够将段寄存器和某一个具体的段相联系。
程序与源程序
程序的结构
小练习:
;编程运算2^3 assume cs:abc ;段与寄存器关联 abc segment ;定义一个段,名称为abc mov ax,2;写入汇编指令 add ax,ax add ax,ax abd ends end ;程序结束处
程序的返回:一个程序结束后将CPU的控制权交还给使它得以运行的程序的过程。应该在程序的末尾添加返回的程序段。
codesg:放在segment前面,做为一个段的名称,这个段的名称最终将被编译、链接程序,称为一个段的段地址 。
mov ax,4c00H int 21H ;第21号中断 ;这两条指令说实现的功能就是程序返回。
assume cs:ABC ABC segment mov ax,2 add ax,ax add ax,ax mov ax,4c00H int 21h ABC ends end
masn和 1.asm在同一目录中,dos下使用masm 1.asm命令便可生产1.obj文件。
link 1.obj,生成exe文件,摁enter忽略编译程序提示输入的信息。
当源程序很大时,能够将它分红多个源程序文件编译,每一个源程序编译成目标文件后再用链接程序将他们链接到一块儿,生成一个可执行文件。或者程序中调用了某个库文件中的子程序,须要将这个库文件和该目标文件链接到一块儿,生成一个可执行文件。或者一个源程序编译后获得存有机器码的目标文件,目标文件中的有些内容还不能直接生成可执行文件,链接程序将此内容处理为最终的可执行文件信息。
在dos系统中.exe文件中的加载过程
mov bx,0 mov ax,[bx] mov al,[bx]
二、内训单元的长度(类型)。
咱们用[0]表示一个内训单元时,0表示单元的偏移地址,段地址默认在DS中,单元的长度(类型)能够由具体指令中的其余的操做对象(好比说寄存器)指出。
mov ax,[0];0对应的字单元,主要单位要看操做对象(寄存器) mov al,[0];字节
assume cs:code code segment mov ax,2 add ax,ax mov ax,4c00H int 21H code ends end
;计算2^3 assume cs:code code segment mov ax,2 add ax,ax add,ax,ax mov ax,4c00H int 21h code ends end
;计算2^12 assume cs:code code segment start: mov ax,2 mov cx,11 p:add,ax,ax loop p;p是标号 mov ax,4c00H;masm默认数字是十进制 int 21H code ends end start
;编程计算123*236,结果放在ax中 assume cs:code code segment start:mov ax,0 mov cx,236 an:add ax,123 loop an mov ax,4c00H int 21H code ends end start
assume cs:code code segment start:mov ax,0 mov cx,123 pa:add ax,236 loop pa mov ax,4c00H int 21H code ends end start
mov al,[0] ;将al赋值0 mov al,ds[0] ;将al赋值段地址为ds,偏移地址为0的内存单元中的内容 mov al,[bx] ;默认段地址为ds,将al赋值偏移地址为bx mov al,ds:[bx] ;将al赋值段地址为ds,偏移地址为bx
在8086模式中,随意向一段内存空间写入数据是危险的,由于这段空间中可能存放着重要的系统数据或代码。
assume cs:code code segment mov ax,0 mov ds,ax mov ds:[26H],ax mov ax,4c00H int 21H code ends end
但笔者在练习的时候出现dosbox下debug卡死
dos下0:200H~0:2FFH的256个字节的空间是安全的,dos和其余合法程序通常都不会使用这段空间。内存0000:0000~0000:03FF大小为1kb的空间是系统存放中断处理程序入口地址的中断向量表。通常状况下0:200H~0:2FFH的256个字节的空间所对应的中断向量表都是空的,操做系统和其余应用程序都不占用。
assume cs:code code segment mov bx,0 ;(bx)=0,偏移地址从0开始 mov cx,12 ;(cx)=12,循环12次 s: mov ax,offffh mov ds,ax ;(ds)=0ffffh mov dl,[bx] ;(ds)=((ds)*16+(bx)),将ffff:bx中的数据送入dl mov ax,0020h mov ds,ax ;(ds)=0020h mov [bx],dl ;((ds)*16+(bx))=dl,将数据送入0020:bx inc bx ;(bx)=(bx)+1 loop s mov ax,4c00h int 21h code ends end
;优化后的代码,优化了两次设置ds assume cs:code code segment mov ax,offffh mov ds,ax ;(ds)=0ffffh mov ax,0020h mov es,ax ;(es)=0020H mov bx,0 ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0 mov cx,12 ;(cx)=12,循环12次 s: mov dl,[bx] ;(ds)=((ds)*16+(bx)),将ffff:bx中的数据送入dl mov es:[bx],dl ;((es)*16+(bx))=dl,将数据送入0020:bx inc bx ;(bx)=(bx)+1 loop s mov ax,4c00h int 21h code ends end
assume cs:codesg codesg segment dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H ;dw,define word,定义字型数据,db定义字节型数据 ;因为数据在代码段中,因此段地址是CS ;dw定义的数据在最开始的地方,因此偏移地址是0开始 start:mov bx,0 ;第一条指令 mov ax,0 mov cx,8 s: add ax,cs:[bx] add bx,2 loop s mov ax,4c00H int 21H codesg ends end start ;入口找end
assume cs:codesg codesg segment dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H;地址0~15 dw 0,0,0,0,0,0,0,0;定义8个字型空数据,后面看成栈来使用,地址是16~31 start: mov ax,cs mov ss,ax mov sp,32;设置栈底ss:sp指向cs:32,十进制的32 mov bx,0 mov cx,8 s:push cs:[bx] add bx,2 loop s; 以上代码段0~15个单元中的8个字型数据一次入栈 mov bx,0 mov cx,8 s0:pop cs:[bx] add bx,2 loop s0;依次出栈8个执行数据到代码段0~15单元中 mov ax,4c00h int 21h codesg ends end start;指明程序入口在start处
assume cs:codesg,ds:data,ss:stack;在源程序中为三个段进行有意义的名称 data segment dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H data ends stack segment dw 0,0,0,0,0,0,0,0;定义8个字型空数据,后面看成栈来使用 stack ends code segment start: mov ax,stack mov ss,ax mov sp,16;设置栈底ss:sp指向stack:16, mov ax,data mov ds,ax;ds指向data段 mov bx,0;ds:bx指向data段中的第一个单元 s:push cs:[bx] add bx,2 loop s; 以上代码段0~16个单元中的8个字型数据一次入栈 mov bx,0 mov cx,8 s0:pop cs:[bx] add bx,2 loop s0;依次出栈8个执行数据到代码段0~16单元中 mov ax,4c00h int 21h codesg ends end start;指明程序入口在start处
assume cs:codesg,ds:data,ss:stack data segment dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H data ends stack segment dw 0,0,0,0,0,0,0,0 stack ends codesg segment start: mov ax,stack mov ss,ax mov sp,16 mov ax,data mov ds,ax push ds:[0] push ds:[2] pop ds:[2] pop ds:[0] mov ax,4c00h int 21h codesg ends end start
and指令:逻辑与指令,按位进行与运算。
and两个同时为真的结果才为真。
mov al,01100011B and al,00111011B ;执行后 al=00100011B
and al,10111111B;将al第六位设为0 and al,01111111B;将al第七位设为0 and al,11111110B;将al第0位设为0
or两个同时为假的结果才为假
mov al,01100011B and al,00111011B ;执行后 al=01111011B
and al,01000000B;将al第六位设为1 and al,10000000B;将al第七位设为1 and al,00000001B;将al第0位设为1
assume cs:code,ds:data data segment db 'unIx' db 'foRK' data ends code segment start: mov al,'a' mov bx,'b' mov ax,4c00h int 21h code ends end start
大写 | 二进制 | 小写 | 二进制 |
---|---|---|---|
A | 01000001 | a | 01100001 |
B | 01000010 | b | 01100010 |
C | 01000011 | c | 01100011 |
D | 01000100 | d | 01100100 |
;大小写转换 assume cs:codesg,ds:datasg datasg segment db'BaSiC' db'iNfOfMaTiOn' datasg ends codesg segment start: mov ax,datasg mov ds,ax;设置ds执行datasg段 mov bx,0;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母 mov cx,5;设置循环次数,由于BaSiC有5个字母 s:mov al,[bx];将ASCII码从ds:bx所指向的单元中取出 and al,11011111B;口岸al中ASCII码的第5个位置变为0,变为大写字母 mov [bx],al;转变后将ASCII码写回单元 inc bx;(bx)加1,ds:bx指向下一个字母 loop x mov bx,5;设置(bx)=5,ds:bx指向'iNfOfMaTiOn'的第一个字母 mov cx,11 s0:mov al,[bx] or al,00100000B mov [bx],al inc bx loop s0 mov ax,4c00H int 21H codesg ends end start
;[bx+idata]能够写成如下格式 mov ax,[200+bx] mov ax,200[bx] mov ax,[bx].200
;使用debug查看内存 mov ax,2000H mov ds:ax mov bx,1000H mov ax,[bx] mov cx,[bx+1] add cx,[bx+2]
;改进大小写转换程序 assume cs:codesg,ds:datasg datasg segment db'BaSiC' db'iNfOfMaTiOn' datasg ends codesg segment start: mov ax,datasg mov ds,ax;设置ds执行datasg段 mov bx,0;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母 mov cx,5;设置循环次数,由于BaSiC有5个字母 s:mov al,[bx+0];将ASCII码从ds:bx所指向的单元中取出 and al,11011111B;口岸al中ASCII码的第5个位置变为0,变为大写字母 mov [bx],al;转变后将ASCII码写回单元 mov [bx+5];定位第二个字符串的字符 or al,00100000B mov [bx+5],al inc bx loop s mov ax,4c00H int 21H codesg ends end start
include<stdio.h> char a[5]="BaSiC"; char b[11]="iNfOfMaTiOn"; main() { int i; i=0; do { a[i]=a[i]&0xDF; b[i]=b[i]|0x20; i++; }while(i<5); }
mov bx,0 mov ax,[bx] mov si,0 mov ax,[si] mov di,0 mov ax,[di] ;------------- ;下面的三组指令也实现了另外一个组相同的功能 ;------------- mov bx,0 mov ax,[bx+123] mov si,0 mov ax,[si+123] mov di,0 mov ax,[di+123]
;用DI和SI实现复制到它后面的数据区中 assume cs:codesg,ds:datasg datasg segment db'welcome to asm!' db'................' datasg ends codesg segment start :mov ax,datasg mov ds,ax mov si,0 mov di,16 mov cx,8 s:mov ax,[si] mov [di],ax add si,2 add di,2 loop s mov ax,4c00h int 21H ;------ ;用数组的思惟[bx(si或di)+idata]的方式优化程序 ;------ assume cs:codesg,ds:datasg datasg segment db'welcome to asm!' db'................' datasg ends codesg segment start :mov ax,datasg mov ds,ax mov si,0 mov cx,8 s:mov ax,[si];第一个字符串的的第一个元素 mov [si+16],ax;目标字符串的第二个元素 add si,2 loop s mov ax,4c00h int 21H codesg ends end start
mov ax,2000h mov ds,ax mov bx,1000h mov si,0 mov ax,[bx+si] inc si mov cx,[bx+si] inc si mov di,si mov ax,[bx+di]
mov ax,2000h mov ds,ax mov bx,1000h mov si,0 mov ax,[bx+2+si] inc si mov cx,[bx+si+2] inc si mov di,si mov ax,[bx+di+2]
assume cs:codesg,ds:datasg datasg segment db'1. file ';长度恰好都是16个字节 db'2. edit ' db'3. search ' db'4. view ' db'5. options ' db'6. help ' datasg ends codesg segment start: mov ax,datasg mov ds,ax mov bx,0 mov cx,6 s: mov al,[bx+3] and al,11011111B mov [bx+3],al add bx,16 loop s mov ax,4c00h int 21h codesg ends end start
;有bug,问题在于cx的使用,进行二重循环,只用一个循环计数器,形成在进行内层的时候覆盖了外层循环的循环计数值。 assume cs:codesg,ds:datasg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends codesg segment start:mov ax,datasg mov ds,ax mov bx,0;用bx来定位行 mov cx,4 s0:mov si,0;用si来定位列 mov cx,3 s:mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s add bx,16 loop s0 mov ax,4c00h int 21h codesg ends end start
loop s;三次循环后cx等于0了 add bx,16 loop s0;先是cx=cx-1再判断时候等于0,此时cx=FFFF不为0再循环,变成死循环了
assume cs:codesg,ds:datasg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends codesg segment start: mov ax,datasg mov ds,ax mov bx,0;用bx来定会行 mov cx,4 s0: mov dx,cx;用dx寄存器来临时存放外层cx的值 mov si,0;用si来定位列 mov cx,3 s: mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s add bx,16 mov cx,dx;在进行外层循环的时候回复cx的值 loop s0 mov ax,4c00h int 21h codesg ends end start
assume cs:codesg,ds:datasg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' dw 0;定义一个字用来保存cx的值 datasg ends codesg segment start:mov ax,datasg mov ds,ax mov bx,0;用bx来定位行 mov cx,4 s0:mov ds:[40h],cx;datasg:40h单元存放外层cx的值 mov si,0;用si来定位列 mov cx,3 s:mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s add bx,16 mov cx,ds:[40h];在进行外层循环的时候回复cx的值 loop s0 mov ax,4c00h int 21h codesg ends end start
assume cs:codesg,ds:datasg,ss:stacksg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends stacksg segment dw 0,0,0,0,0,0,0,0;定义一个段,用做栈段,容量为16个字节 stacksg ends codesg segment start:mov ax,stacksg mov ss,ax mov sp,16 mov ax,datasg mov ds,ax mov bx,0;用bx来定位行 mov cx,4 s0:push cx;datasg:40h单元存放外层cx的值 mov si,0;用si来定位列 mov cx,3 s:mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s add bx,16 pop cx;在进行外层循环的时候回复cx的值 loop s0 mov ax,4c00h int 21h codesg ends end start
assume cs:codesg,ds:datasg,ss:stacksg stacksg segment stacksg ends datasg segment db '1. display ' db '2. brows ' db '3. replace ' db '4. modify ' datasg ends codesg segment start:mov ax,stacksg mov ss,ax mov sp,16 mov ax,datasg mov ds,ax mov bx,0 mov cx,4 s0:push cx mov si,0 mov cx,4 s:mov al,[bx+si+3] and al,11011111B mov [bx+si+3],al inc si loop s add bx,16 pop cx loop s0 mov ax,4c00h int 21h codesg ends end start
;如下指令是错误的 mov ax,[ax] mov ax,[cx] mov ax,[dx] mov ax,[ds] mov ax,[bx+bp] mov ax,[si+di]
mov ax,[bx] mov ax,[si] mov ax,[di] mov ax,[bp] mov ax,[bx+si] mov ax,[bx+di] mov ax,[bp+si] mov ax,[bp+di] mov ax,[bx+si+idata] mov ax,[bx+di+idata] mov ax,[bp+si+idata] mov ax,[bp+di+idata]
机器码 | 汇编指令 | 指令执行前数据的位置 |
---|---|---|
89C3 | mov bx,[0] | 内存,ds:0单元 |
89C3 | mov bx,ax | CPU内部,ax寄存器 |
BB0100 | mov bx,1 | CPU内部,指令缓冲器 |
mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add byte ptr [bx],2
;假设内存2000:1000 FF FF FF FF FF FF …… ;若是用如下指令 mov ax,2000H mov ds,ax mov byte ptr [1000H],1 ;那么内存中的内容变为 ;2000:1000 01 FF FF FF FF FF …… 若是是用如下指令 mov ax,2000H mov ds,ax mov word ptr [1000H],1 ;那么内存中的内容变为 ;2000:1000 01 00 FF FF FF ……
mov ax,seg mov ds,ax mov bx,60h;肯定记录物理地址:ds:bx mov word ptr [bx+0ch],38;寄存器相对寻址 排名字段改成38 add word ptr [bx+0eh],70;收入字段增长70 mov si,0;用si来定位产品字符串中的字符 mov byte ptr [bx+10h+si],'V';相对基址变址寻址 inc si mov byte ptr [bx+10h+si],'A' inc si mov byte ptr [bx+10h+si],'X'
struct company /*定义一个公司记录的结构体*/ { char cn[3]; /*公司名称*/ char hn[9]; /*总裁姓名*/ int pm; /*排名*/ int sr; /*收入*/ char cp[3]; /*著名产品*/ }; struct compant dec={"DEC","Ken Olsen",137,40,"PDF"}; /*定义一个公司记录的变量,内存中将存有一条公司的记录*/ mian() { int i; dec.pm=38; dec.sr=dec.sr+70; i=0; dec.cp[i]='V'; i++; dec.cp[i]='A'; i++; dec.cp[i]='X'; return 0; }
mov ax,seg mov ds,ax mov bx,60h;记录首地址送入bx mov word ptr [bx].och,38;排名字段改成38 add word ptr [bx].0eh,70;收入字段增长70 ;产品名字段改成字符串'VAX' mov si,0 mov byte ptr [bx].10h[si],'V' inc si mov byte ptr [bx].10h[si],'A' inc si mov byte ptr [bx].10h[si],'X'
div reg(寄存器) div 内存单元。
div byte ptr ds:[0] div byte ptr [bx+si+idata] ;al放商,ah放余数 div word ptr es:[0] div word ptr [bx+si+idata] ;ax放商,dx放余数
除数 | 被除数 |
---|---|
8位 | 16为(AX) |
16位 | 32位(DX高16位+AX低16位) |
运算 | 8位 | 16位 |
---|---|---|
商 | AL | AX |
余数 | AH | DX |
;被除数1001可用ax寄存器存放,除数100可用8位寄存器存放,要进行8位除法。 mov ax,1001 mov bl,100 div bl ;执行后al的值等于0AH(10),ah的值等于1(余数为1)。
;被除数100001大于2^16=65535(FFFF),不能用ax来存放,要用dx和ax两个寄存器联合存放。除数小于255,可用一个8位寄存器存放,可是被除数是32位的,除数应为16位,因此要用一个16位寄存器来存放除数。 ;100001的十六进制为186A1H,100001的高16位(1)存放在dx,低16位(86AH)存放在ax中。 mov dx,1 mov ax,86A1H mov bx,100 div bx ;执行后ax内容等于03E8H(即1000),dx的值等于1(余数)。
data segment db 1;第一个数据为01h,在data:0处,占1个字节 dw 1;第二个数据为0001h,在data:1处,占1个字 dd 1;第三个数据为00000001h,在data:3处,占2个字 data ends
data segment dd 100001H;低16位存储在ax中,高16位存储在dx中 dw 100 dw 0 data ends mov ax,data mov ds,ax mov ax,ds:[0];低16位存储在ax中 mov dx,ds:[2];高16位存储在dx中 div word ptr ds:[4] mov ds:[6],ax
db 3 dup(0) ;定义了3个字节,它们的值都是0,等同于db 0,0,0。 db 3 dup(0,1,2) ;定义了9个直接,它们是0、一、二、0、一、二、0、一、2,至关于db 0、一、二、0、一、二、0、一、2 db 3 dup('abc','ABC') ;定义了18个直接,它们是'abcABCabcABCabcABC'
;初始化阶段 mov ax,data mov ds,ax mov ax,table;data已经被占用 mov es,ax mov bx,0 mov si,0 mov di,0 mov cx,21 ;存放年份,每个bx就是一个字节 mov al,[bx] mov es:[di],al mov al,[bx+1] mov es:[di+1],al mov al,[bx+2] mov es:[di+2],al mov al,[bx+3] mov es:[di+3],al ;存放公司的总收入 mov ax,[bx+54H];第一个年收入是dd数据类型,段地址为54H mov dx,[bx+54H] mov es:[di+5H],ax mov es:[di+7H],dx ;存放公司的人数 mov ax,[si+0A8H];第一我的数的数据段地址为0A8H mov es:[di+0A8H],ax ;计算人均收入并存放 mov ax,[bx+54H] mov dx,[bx+56H];这两句诗初始化被除数 div word ptr,ds:[si+0A8H];除以人数 mov es:[di+0dH],ax;将商放入指定位置 ;为下一次循环时存放数据作准备 add bx,4;bx肯定年份和收入 add si,2;si肯定人数 add di,16;di肯定的是每行的列数
assume cs:codesg,ds:data,es:table data segment db '1975','1976' '1977' …… dd 16,22,382 …… dw 3,7,9 …… ;数据在题目中 data ends table segment db 21 dup('year summ ne ?? ') table ends start:mov ax,data mov ds,ax mov ax,table mov es,ax mov bx,0 mov si,0 mov di,0 mov cx,21 s:mov al,[bx] mov es:[di],al mov al,[bx+1] mov es:[di+1],al mov al,[bx+2] mov es:[di+2],al mov al,[bx+3] mov es:[di+3],al mov ax,[bx+54H] mov dx,[bx+56H] mov es:[di+5H],ax mov es:[di+7H],dx mov ax,[si+0A8H] mov es:[di+0AH],ax mov ax,[bx+54H] div word ptr ds:[si+0A8H] mov es:[di+0dH],ax add bx,4 add si,2 loop s mov ax,4c00h int 21h codesg ends end start
assume cs:codesg codesg segment start:mov ax,offset start;至关于 mov ax,偏移地址0,段地址是从0开始 s:mov ax,offset s;至关于 mov ax,3,标记的是代码段中的第二条指令,第一条指令长度为3个字节,则s的偏移地址为3 codesg ends end start
assume cs:codesg codesg segment start:mov ax,0 jmp short s add ax,1 s:inc ax codesg ends end start
通常汇编指令中的当即数(idata)会出如今对应的机器指令中。而jmp指令的机器指令并不包含目的地址,包含的是相对于当前IP的转移位移,CPU并不须要目的地址就能够实现对IP的修改。
assume cs:codesg codesg segment start:mov ax,0 mov bx,0 jmp far ptr s db 256 dup(0) s:add ax,1 inc ax codesg ends end start
mov ax,0123H mov ds:[0],ax jmp word ptr ds:[0] ;至关于 jmp ax,执行后(IP)=0123h mov ax,0123H mov [bx],ax jmp word ptr [bx] ;执行后(IP)=0123h
mov ax,0123H mov ds:[0],ax mov word ptr ds:[2],0 jmp dword ptr ds:[0] mov ax,0123H mov [dx],ax mov word ptr [bx+2],0 jmp dword ptr [bx] ;执行后 (CS)=0,(IP)=0123H CS:IP指向0000:0123
jmp short 标号 jmp near ptr 标号 jcxz 标号 loop 标号
assume cs:code code segment start: jmp short s db 128 dup(0) s:mov ax,0FFFFH code ends end start
assume cs:codesg codesg segment mov ax,4c00h int 21h start:mov ax,0 s:nop nop;nop占用两个字节,不执行任何操做 mov di,offset s mov si,offset s2 mov ax,cs:[si];jmp short s1的机器码给了ax mov cs:[di],ax;覆盖到指令 s:nop nop那 s0:jmp short s;s那已经被jmp short s1机器码覆盖 s1:mov ax,0 int 21h mov ax,0 s2:jmp short s1;jmp -8h,向上跳到s1,s1又向上跳-10字节 nop codesg ends end start
assume cs:code,ds:data,ss:stack data segment db'welcome to masm!';定义要显示的字符串(共16字节) db 02H,24H,71H;定义字符的属性 data ends stack segment dw 8 dup(0) stack ends code segment start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov sp,10H xor bx,bx;bx清零,用来索引颜色 mov ax,0b872H;算出屏幕第12行中间的显存的段起始位置放入ax中 mov cx,3;s3循环控制行数,要显示三个字符串外循环为3次 s3: push cx;三个进栈操做为外循环s3保存相关寄存器的值 push ax;以防止它们的值在内循环中被破坏 push bx mov es,ax;此时es为屏幕第12行中间的显存的段起始位置 mov si,0;si用来索引代码列的字符 mov di,0;di用来定位目标列 mov cx,10H ;s1循环控制存放的字符,一个字符串中含有10H个字节内循环为10H次 s1: mov al,ds:[si] mov es:[di],al inc si add id,2 loop s1;吃循环实现偶地址中存放字符 mov di,1;设置di的值为1,为在显存奇数地址中存放字符的颜色属性作准备 pop bx mov al.ds:[bx+10H];取消颜色属性 inc bx mov cx,10H;第二个内循环也为10H s2: mov es:[di],al add di 2 loop s2;此循环实现奇数地址存放字符的颜色属性 ;如下4句为下一趟外循环作准备 pop ax add ax,0AH;将显存的段地址起始地址设置为当前行的下一行 ;[在段地址中甲0aH,至关于在偏移地址中加了0a0h(=160d)] pop cx loop s3 mov ax,4C00H int 21H code ends end start
assume cs:codesg stack segment db 16 dup(0) stack ends codesg segment mov ax,4c00h int 21h start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 push ax mov bx,0 ret codesg ends end start
assume cs:codesg stack segment db 16 dup(0) stack ends codesg segment mov ax,4c00h int 21h start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 push cs push ax mov bx,0 retf codesg ends end start
push IP jmp near 标号
push CS push IP jmp far ptr 标号
push IP jmp 16位寄存器
call word ptr 内存单元地址;段内跳转 call dword ptr 内存单元地址;段间跳转
push IP jmp word ptr 内存单元地址
mov sp,10h mov ax,0123H mov ds:[0],ax call word ptr ds:[0] ;执行后IP的值等于0123H,SP的值等于0EH
push CS push IP jmp word ptr 内存单元地址
mov sp,10h mov ax,0123H mov ds:[0],ax mov word ptr ds:[0],0 call dword ptr ds:[0] ;执行后IP的值等于0123H,SP的值等于0CH,CS的值等于0
assume cs:code code segment start: mov ax,1 mov cx,3 call s mov bx,ax mov ax,4c00h int 21h s: add ax,ax loop s ret code ends end start
mull reg mull 内存单元 mull byte ptr ds:[0] mull word ptr [bx+si+idata] ;(ax)=(ax)*((ds)*16+(bx)+(si)+idata) ;(dx)=(ax)*((ds)*16+(bx)+(si)+idata)
;计算100*10,两个数都小于255,能够作8位乘法 mov ax,100 mov bx,10 mull bl ;结果(ax)=1000(03E8H) ;计算100*1000,1000都大于255,要作16位乘法 mov ax,100;高位自动补零 mov bx,10000 mull bx ;结果(ax)=4240H,(dx)=000FH,F4240H=1000000
cube:mov ax,bx mul bx mul bx ret
assume cs:code data segment dw 1,2,3,4,5,6,7,8 dd 8 dup (0) data ends code segment start: mov ax,data mov ds,ax mov si,0;ds:si指向第一组word单元 mov di,16;ds:di指向第二组dword单元 mov cx,8 s: mov bx,[si] call cube mov [di],ax mov [di+2],dx add si,2;ds:di指向下一个word单元 add di,4;ds:di指向下一个dword单元 loop s mov ax,4c00h int 21h cube:mov ax,bx mul bx mul bx ret code ends end start
assume cs:code data segment db'conversation' data ends start: mov ax,data mov ds,ax mov si,0;ds:si指向字符串(批量数据)所在空间的首地址 mov cx,12;cx存放字符串的长度 call capital mov ax,4c00h int 21h capital: add byte ptr [si],11011111B inc si loop capital ret code ends
capital: mov cl,[si];低8位 mov ch,0;高8位设置为0 jcxz ok;若是(cx)=0则结束,若是不是0则处理 and byte ptr [si],11011111B inc si jmp short capital ok: ret
assume cs:code data segment db'word',0 db'unix',0 db'wind',0 db'good',0 data ends
;此程序有bug,cx有问题 assume cs:code data segment db'word',0 db'unix',0 db'wind',0 db'good',0 data ends code segment start: mov ax,data mov ds,ax mov bx,0 mov cx,4 s: mov si,bx call capital add bx,5 loop s mov ax,4c00h int 21h capital: mov cl,[si] mov ch,0 jcxz ok and byte ptr [si],11011111b inc si jmp short capital ok: ret code ends end start
assume cs:code data segment db 'welcome to masm!',0 data ends code segment start: mov dh,8;行号 mov dl,3;列号 mov cl,2;颜色属性 mov ax,data mov ds,ax mov si,0 call show_str mov ax,4c00h int 21h show_str:;子程序 push cx push si mov al,0A0h;每行有80*2=160个字节=0a0h dec dh;行号在显存中下标从0开始,因此减1 mul dh;至关于从第(n-1)*0a0h个byte单元开始 mov bx,ax;定位好的位置偏移地址存放在bx里(行) mov al,2;每一个字符占2个字节 mul dl;定位列,结果ax存放的是定位好的列的位置 sub ax,2;列号在显存中下标从0开始,又由于是偶字节存放字符,因此减2 add bx,ax;此时bx中存放的是行与列的偏移地址 mov ax,0B800h;显存开始的地方 mov es,ax;es中存放的是显存的第0页的起始地段地址 mov di,0;di指向显存的偏移地址,肯定指向下一个要处理的字符的位置 mov al,cl;cl存放颜色参数,下边cl要用来临时存放要处理的字符 mov ch,0;下边cx存放的是每次准备处理的字符 s: mov cl,ds:[si];指向'welcome to masm ',0 jcxz ok;cl为0时跳转 mov es:[bx+di],cl;偶地址存放字符 mov es:[bx+di+1],al;奇地址存放字符的颜色属性 inc si add di,2;指向了下个字符 jmp short s ;无条件跳转,jcxz是离开的关键跳 ok: pop si pop cx ret;定义结束 code ends end start
assume cs:code,ss:stack stack segment dw 8 dup(0) stack ends code segment start: mov ax,stack mov ss,ax mov sp,10h mov ax,4240h mov dx,0fh mov xx,0ah call divdw mov ax,4c00h int 21h divdw: push ax;低16位先保存 mov ax,dx;ax这时是高16位了 mov dx,0;为了避免影响余数位和高位数 div cx mov bx,ax pop ax div cx mov cx,dx mov dx,dx ret code ends end start
assume cs:code,ds:data data segment db 10 dup(0) data ends code segment start: mov ax,12666 mov bx,data;指向字符串的首地址 mov ds,bx mov si,0 call dtoc;实现将word型整数转化为字符串并存储 mov dh,8;打印初始化 mov dl,3 mov cl,0cah call show_str;开始打印字符串 mov ax,4c00h int 21h dtoc: push dx push cx push ax push si mov bx,0;bx在子程序中用来存放位数,用栈来临时存放修改后的字符 s1: mov cx,10d;d表示十进制,cx准备被除,用取余法来取出数字 mov dx,0 div cx;除以十 mov cx,ax;获得的商复制给cx,要利用jcxz jcxz s2;当商为0则跳到s2 add dx,30h;余数加上30h获得相应的ascii码 push dx inc bx jmp short s1 s2: add ax,30h;当商为0的时候,余数为个位 push dx inc bx;再进行一次栈操做(补充当商为零而余数不为零时的状况) mov cx,bx;总共有bx位进栈,因此循环次数为bx mov si,0 s3: pop ax;s3实现将栈中的数据依次出栈放到指定的内存中 mov [si],al inc si loop s3 okay: pop bx pop si pop ax pop dx ret show_str:;子程序 push bx push cx push si mov al,0A0h;每行有80*2=160个字节=0a0h dec dh;行号在显存中下标从0开始,因此减1 mul dh;至关于从第(n-1)*0a0h个byte单元开始 mov bx,ax;定位好的位置偏移地址存放在bx里(行) mov al,2;每一个字符占2个字节 mul dl;定位列,结果ax存放的是定位好的列的位置 sub ax,2;列号在显存中下标从0开始,又由于是偶字节存放字符,因此减2 add bx,ax;此时bx中存放的是行与列的偏移地址 mov ax,0B800h;显存开始的地方 mov es,ax;es中存放的是显存的第0页的起始地段地址 mov di,0;di指向显存的偏移地址,肯定指向下一个要处理的字符的位置 mov al,cl;cl存放颜色参数,下边cl要用来临时存放要处理的字符 mov ch,0;下边cx存放的是每次准备处理的字符 S: mov cl,ds:[si] jcxz ok mov es:[bx+di],cl mov es:[bx+di+i],al inc si add di,2 jmp short s ok: pop si pop cx pop bx ret code ends end start
mov ax,1 sub ax,1 mov ax,1 and ax,0 ;指令执行后,结果为0,则ZF=1 mov ax,2 sub ax,1 mov ax,1 or ax,0 ;指令执行后,结果为1,则ZF=0
mov al,1 add al,10 ;执行结果为00001011B,有3个1,则PF=0 mov al,1 or al,10 ;执行后结果为00000011B,有2个1,则PF=1
mov al,10000001B add al,1 ;执行指令后al的值是10000010B,无符号数130,有符号数-126
flag的第0位是CF,进位标志位。通常状况下,在进行无符号运算的时候,它记录了运算结果的最高有效位向更高位的进位值或从更高位的借位值。对于位数为N的无符号数,其对应的二进制信息的最高位为N-1位的最高有效位,假想存在第N位。
mov al.98h add al,al;执行后(al)=30h,cf=1,cf记录了从最高有效位向更高位的进位值 add al,al;执行后(al)=60h,cf=0,cf记录了从更高有效位向更高位的进位值 mov al,97h sub al,98h;执行后(al)=ffh,cf=1,cf记录了向更高位的借位值 sub al,al;执行后(al)=0,cf=0,cf记录了向更高位的借位值
assume cs:code code segment start: mov al,01100010b add al,01100011b mov ax,4c00h int 21h code ends end start
assume cs:code code segment start: mov al,10001000b add al,11110000b mov ax,4c00h int 21h code ends end start
assume cs:code code segment start: mov al,98h add al,al add al,al mov ax,4c00h int 21h code ends end start
assume cs:code code segment start: mov al,97h sub al,98h add al,al mov ax,4c00h int 21h code ends end start
mov al,98d add al,99d ;对于无符号数运算,98+99没有进位,CF=0 ;对于有符号数运算,98+99发生溢出,OF=1
mov ax,2 mov bx,1 sub bx,ax adx ax,1 ;执行后 (ax)=4,至关于计算(ax)+1+CF=2+1+1+4 mov ax,1 add ax,ax adc ax,3 ;执行后(ax)=5,至关于执行(ax)+3+CF=2+3+0=5 mov al,98H add al,al adx al,3 ;执行后 (ax)=34H,至关于执行(ax)+3+CF=30H+3+1=34H
mov ax,001EH mov bx,0F000H add bx,1000H adc ax,0020H
mov ax,001EH mov bx,0F000H mov cx,1000H add cx,1EF0H add bx,1000H adc ax,0020H
assume cs:code,ds:data data segment db 16 dup(88H) db 16 dup(11H) data ends code segment start: mov ax,data mov ds,ax mov si,0 mov di,16 mov cx,8 call add128 mov ax,4C00H int 21H add128: push ax push cx push si push di sub ax,ax;将CF设置为0 s: mov ax,[si] adc ax,[di] mov [si],ax inc si;不能用add si,2代替 inc si;由于会影响cf位 inc di;而loop和inc不会影响 inc di loop s pop di pop si pop cx pop ax ret code ends end start
mov bx,1000H mov ax,003EH sbb bx,2000H sbb ax,0020H
cmp ax,ax ;执行后结果为0,ZF=1,PF=1,SF=0,CF=0,OF=0 mov ax,8 mov bx,3 cmp ax,bx ;执行后ax、bx的值不变,ZF=0,PF=1,SF=0,CF=0,OF=0
cmp ax,bx
指令 | 含义 | 检测的相关标志位 |
---|---|---|
je | 等于则转移 | ZF=1 |
jne | 不等于则转移 | ZF=0 |
jb | 低于则转移 | CF=1 |
jnb | 不低于则转移 | CF=0 |
ja | 高于则转移 | CF=0 and ZF=0 |
jna | 不高于则转移 | CF=1 or ZF=1 |
j | e | ne | b | nb | a | na |
---|---|---|---|---|---|---|
jump | equal | not equal | below | not below | above | not above |
cmp ah,bh je s;ZF=1则跳转 add ah,bh jmp short ok s: add ah,bh ok:ret
mov ax,0 mov ax,0 je s inc ax s: inc ax ;执行后ax的值等于1,add ax,0使得ZF=1,因此je指令将进行转移。
;方案一 assume cs:code data segment db 8,11,8,1,8,5,63,38 data ends code segment start: mov ax,data mov ds,ax mov bx,0;ds:bx指向第一个字节 mov ax,0;初始化累加器 mov cx,0 s: cmp byte ptr [bx],8;和8进行比较 jne next;若是不相等转到next,继续循环 inc ax;若是相等就计数值加1 next: inc bx loop s;执行后:(ax)=3 mov ax,4c00h int 21h code ends end segment
;方案二 assume cs:code data segment db 8,11,8,1,8,5,63,38 data ends code segment start: mov ax,data mov ds,ax mov bx,0;ds:bx指向第一个字节 mov ax,0;初始化累加器 mov cx,0 s: cmp byte ptr [bx],8;和8进行比较 je ok;若是不相等转到ok,继续循环 jmp short next;若是不想等就转到next,继续循环 ok: inc ax;若是相等就计数值加1 next: inc bx loop s;执行后:(ax)=3 mov ax,4c00h int 21h code ends end segment
assume cs:code data segment db 8,11,8,1,8,5,63,38 data ends code segment start: mov ax,data mov ds,ax mov bx,0;ds:bx指向第一个字节 mov ax,0;初始化累加器 mov cx,0 s: cmp byte ptr [bx],8;和8进行比较 jna next;若是大于8转到next,继续循环 inc ax;若是大于就计数值加1 next: inc bx loop s;执行后:(ax)=3 mov ax,4c00h int 21h code ends end segment
;下面的程序执行后ax的值是多少? mov ax,0 push ax popf mov ax,0fff0h add ax,0010h pushf pop ax and al,11000101b and ah 00001000b
assume cs:code data segment db'welcome to masm!' db 16 dup(0) data ends code segment start: mov ax,data mov ds,ax mov si,0;指向data:0 mov es,ax mov di,16;指向data:16 mov cx,16;rep循环16次 cld;设置DF=0,正向传送 rep movsb mov ax,4c00h int 21h code ends end start
assume cs:code data segment db 16 dup(0) data ends code segment start: mov ax,0f00h mov ds,ax mov si,0ffffh;指向f0000:ffff mov ax,data mov es,ax mov di,16;指向data:15 mov cx,16;rep循环16次 std;设置DF=1,逆向传送 rep movsb mov ax,4c00h int 21h code ends end start
标志 | 值为1的标记 | 值为0的标记 |
---|---|---|
OF | OV | NV |
SF | NG | PL |
ZF | ZR | NZ |
PF | PE | PO |
CF | CY | NC |
DF | DN | UP |
pop IP pop CS popf
assume cs:codesg codesg segment start: mov ax,1000h mov bh,1 div bh codesg ends end start
把程序存入内存,修改向量表(即将内存地址登记在中断向量表的对应表项中),中断时调用这个内存。
除法溢出对应的中断类型码为0,它的中断处理程序的入口地址应该从0* 4+2地址单元开始存放,段地址存放在0* 4+2字单元中,偏移地址存放在0*4字单元中。也就是改变后的中断处理程序的段地址0存放在0000:0002字单元中,偏移地址200H存放在0000:0000字单元中。若是要显示的字符串在程序的data段中,那么程序执行完成后返回,它所占用的内存空间被系统释放,在其中存放的信息也可能被别的信息覆盖。
assume cs:code code segment start: mov ax,cs mov ds,ax mov si,offset do0;设置ds:di指向源地址 mov ax,0 mov es,ax mov di,200h;设置es:si指向目的地址 mov cx,offset do0end - offset do0;设置cx为传输长度,编译器能够识别加减乘除运算符 cld;设置传输方向为正 rep movsb mov ax,0;设置中断向量表 mov es,ax mov word ptr es:[0*4],200h mov word ptr es:[0*4+2],0 mov ax,4c00h int 21h do0: jmp short do0start db"welcome to masm!";在代码段中存储数据 do0start: mov ax,cs mov ds,ax mov si,202h;jmp short do0start这条指令栈两个字节 ;显示字符串,设置es:di指向字符串 mov ax,0b800h;显存空间,直接显示在显示器上 mov es,ax mov di,12*160+36*2;这只es:di指向显存空间的中间位置 mov cx,16;设置cx为字符串(welcome to masm!)长度 s: mov al,[si] mov es:[di],al inc si add di,1 mov al,02h mov es:[di],al add di,1 loop s mov ax,4c00h int 21h do0end: nop code ends end start
在有些状况下CPU在执行完当前指令后,即使是发生了中断也不会响应。
在执行完向ss寄存器传送数据的指令后,即使检测到了中断信号CPU也不会响应。由于ss:sp指向栈顶,对他们的设置应该连续完成。若是在执行完设置ss指令后mCPU响应中断引起中断过程,要在栈中压入标志寄存器、CS和IP的值。而ss改变,sp并未改变则ss:sp指向不是正确的栈顶将引起错误。
咱们要将栈顶设置为1000:0,不该该隔开
应该 | 不该该 |
---|---|
mov ax,1000h | mov ax,1000h |
mov ss,ax | mov ss,ax |
mov sp,0 | mov ax,0 |
mov ax,0 | mov sp,0 |
assume cs:code code segment start: mov ax,0b800h mov es,ax mov byte ptr es:[12*160+40*2],'!' int 0;执行int 0指令,引起中断过程,执行0号中断处理程序 code ends end start
;计算 ssume cs:code code segment start: mov ax,3456 int 7ch add ax,ax adc ax,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;设置ds:si指向源地址 mov ax,0 mov es,ax mov di,200h;设置es:di指向目的地址 mov cx,offset sqrend- offset sqr;设置cx为传输长度 cld;设置传输方向为正 rep movsb mov ax,0 mov es,ax mov word ptr es:[7ch*4],200h mov word ptr ws:[7ch*4+2],0 mov ax,4c00h int 21h sqr: mul ax iret sqrend: nop code ends end start
assume cs:code data segment db'conversation',0 data ends code segment start: mov ax,data mov ds,ax mov si,0 int 7ch mov ax,4c00h int 21h code ends end start assume cs:code code segment start: mov ax,cs mov ds,ax mov si,offset capital mov ax,0 mov es,ax mov di 200h mov cx,offset capitalend - offset capital cld rep movsb mov ax,0 mov es,ax mov word ptr es:[7ch*4],200h mov word ptr es:[7ch*4+2],0 mov ax,4c00h int 21h capital: push cx push si change: mov cl,[si] mov ch,0 jcxz ok and byte ptr [si],11011111b inc si jmp short change ok: pop si pop cx iret capitalend: nop code ends end start
编程:用7ch中断例程完成loop指令的功能,在屏幕中间显示80个"!".
loop指令须要循环次数和到标号的位移。为了模拟loop指令7ch中断例程应具有下面dec cx和若是cx的值不等于0则转移到标号s处。
assume cs:code code segment start: mov ax,0b800h;显存地址 mov es,ax mov di,160*12 mov bx,offset s- offset se;设置从标号s的转移位移 mov cx,80 s: mov byte ptr es:[di],'!' add di,2 int 7ch;若是cx的值不等于0则转移到标号s处 se: nop mov ax,4c00h int 21h code ends end start ;7ch中断例程 lp: push bp mov bp,sp; dec cx jcxz lpret add [bp+2],bx lpret: pop bp iret
mov ah,2;表示调用10h号中断例程的2号子程序,功能为设置光标位置 mov bh,0;页号 mov dh,5;行号 mov dl 12;列号 int 10h;
;功能为在光标位置显示字符功能 mov ah,9;置光标,调用9号子程序 mov al,'a';字符 mov bl,7;颜色属性,和在显存中的属性字节的格式相同 mov bh,0;第0页 mov cx,3;字符重复个数 int 10h ``` - 编程:在屏幕的第5行12列显示3个红底高亮闪烁绿色的'a' ```asm assume cs:code code segment mov ah,2;设置光标 mov bh,0;第0页 mov dh,5;dh中放行号 mov dl,12;dl中放列号 int 10 mov ah,9;设置光标 mov al,'a';字符 mov bl,11001010b;颜色属性 mov bh,0;第0页 mov cx,3;字符重复个数 int 10h mov ax,4c00h int 21h code ends end
mov ah,4ch;程序返回 mov al,0;返回值0是正常返回 ;合起来写就是 mov ax,4c00h int 21h
int 2h中断例程还具备在光标位置显示字符串的功能、
ds:dx;要显示的字符串须要用 $ 做为结束符 mov ah,9;功能号9,表示在光标位置显示字符串 int 21h
;对0~255之内的端口进行读写 in al,20h;从20h端口读入一个字节 out 20h,al;往20h端口写入一个字节 ;对256~65535的端口进行读写时,端口放在dx中 mov dx,3f8h;将端口号3f8h送入dx in al,dx;从3f8端口读入一个字节 out 3f8h,al;往3f8h端口写入一个字节
bios也提供了相关的程序使用户在开机时配置CMOS RAM中的系统信息。
mov al,01001000b shl al,1;将al中的数据左移一位 ;执行后al的值是10010000b,CF=0
mov al,01010001b mov cl,3 shl al,cl ;执行后al的值为10001000b,cf=0
mov al,00000001b | 执行后al的值等于00000001b=1 |
---|---|
shl al,1 | 执行后al的值等于00000010b=2 |
shl al,1 | 执行后al的值等于00000100b=4 |
shl al,1 | 执行后al的值等于00001000b=8 |
mov cl,3 | |
shl al,cl | 执行后al的值等于01000000b=64 |
在CMOS RAM中以每一个信息一字节存放着当前的时间信息:年09h,月08h,日07h,时04h,分02h,秒00h。这些数据以BCD码的方式存放,BCD码以4位为一位。
assume cs:code code segment start: ;向地址端口70h写入要访问的单元地址,读取CMOS RAM的信息 mov al,8 out 70h,al in al,71h;从数据端口中取得指定单元中的数据 mov ah,al;al中为从CMOS RAM的8号端口读出数据 mov cl,4 shr ah,cl;ah中为月份的十位数码值 and al,00001111b;ah中为月份的个位数值码 add ah,30h;BCD码值+30h(字符'0')=十进制对应的ASCII码 add al,30h ;用BCD码表示的月份以十进制的形式显示到屏幕上。 mov bx,0b800h;显存 mov es,bx mov byte ptr es:[160*12+40*2],ah;显示月份的十位数码 mov byte ptr es:[160*12+40*2+2],al;显示月份的个位数码 mov ax,4c00h int 21h code ends end start
可屏蔽中断所引起的中断过程,除在第一步的实现上有所不一样外,基本上和内中断的中断过程相同。由于可屏蔽中断信息来自于CPU外部,中断类型码是经过数据总线送入CPU的;而内中断的中断类型码是在CPU内部产生的。在中断过程当中将IF置0的缘由是在进入中断处理程序后禁止其余的可屏蔽中断。
前三步由硬件系统自动完成,第四步用户能够修改int 9中断程序。
;显示字符 code segment start: mov ax,0b800h mov es,ax mov ah,'a' s: mov es:[160*12+40*2],ah inc ax cmp ah,'z' jna s mov ax,4c00h int 21h code ends end start
;延迟显示字符 assume cs:code stack segment db 128 dup(0) stack ends code segment start: mov ax,stack mov ss,ax mov sp,128 mov ax,0b800h mov es,ax mov ah,'a' s: mov es:[160*12+40*2],ah call delay inc ah cmp ah,'z' jna s mov ax,4c00h int 21h delay: push ax push dx mov dx,10h;循环100次,延迟的时间和CPU的计算能力成反比 mov ax,0 s1: sub ax,1 sbb dx,0 cmp ax,0 jne s1 cmp dx,0 jne s1 pop dx pop ax ret code ends end start
;实现IF=0,TF=0步骤 pushf pop ax and ah,11111100b push ax popf
assume cs:code stack segment db 128 dup(0) stack ends data segment dw 0,0 data ends code segment start: mov ax,stack mov ss,ax mov sp,128 mov ax,data mov ds,ax mov ax,0 mov es,ax push es:[9*4] pop ds:[0] push es:[9*4+2] pop ds:[2];将原来的int9中断例程的入口地址保存 mov word ptr es:[9*4+2],offset int9 mov es:[9*4+2],cs;在中断向量表中设置新的int 9中断例程的入口地址 mov ax,0b800h mov es,ax mov ah,'a' s: mov es:[160*12+40*2],ah call delay inc ah cmp ah,'z' jna s mov ax,0 mov es,ax push ds:[0] pop es:[9*4] push ds:[2] pop es:[9*4+2];将中断向量表中int9中断例程的入口恢复为原来的地址 mov ax,4c00h int 21h delay: push ax push dx mov dx,10h;循环100次,延迟的时间和CPU的计算能力成反比 mov ax,0 s1: sub ax,1 sbb dx,0 cmp ax,0 jne s1 cmp dx,0 jne s1 pop dx pop ax ret ;新的int 9中断例程 int9: push ax push bx push es in al,60h pushf pushf pop bx and bh,11111100b push bx popf call dword ptr ds:[0];对int指令进行模拟,调用原来的int9中断例程 cmp al,1;esc键盘扫描码 jne int9ret mov ax,0b800h mov es,ax inc byte ptr es:[160*12+40*2+1];改变颜色 int9ret: pop es pop bx pop ax iret code ends end start
assume cs:code stack segment db 128 dup(0) stack ends data segment dw 0,0 data ends code segment start: mov ax,stack mov ss,ax mov sp,128 mov ax,data mov ds,ax mov ax,0 mov es,ax push es:[9*4] pop ds:[0] push es:[9*4+2] pop ds:[2];将原来的int9中断例程的入口地址保存 mov word ptr es:[9*4+2],offset int9 mov es:[9*4+2],cs;在中断向量表中设置新的int 9中断例程的入口地址 mov ax,0b800h mov es,ax mov ah,'a' s: mov es:[160*12+40*2],ah call delay inc ah cmp ah,'z' jna s mov ax,0 mov es,ax push ds:[0] pop es:[9*4] push ds:[2] pop es:[9*4+2];将中断向量表中int9中断例程的入口恢复为原来的地址 mov ax,4c00h int 21h delay: push ax push dx mov dx,10000h;循环100次,延迟的时间和CPU的计算能力成反比 mov ax,0 s1: sub ax,1 sbb dx,0 cmp ax,0 jne s1 cmp dx,0 jne s1 pop dx pop ax ret ;新的int 9中断例程 int9: push ax push bx push es in al,60h pushf pushf pop bx and bh,11111100b push bx popf call dword ptr ds:[0];对int指令进行模拟,调用原来的int9中断例程 cmp al,1;esc键盘扫描码 jne int9ret mov ax,0b800h mov es,ax inc byte ptr es:[160*12+40*2+1];改变颜色 int9ret: pop es pop bx pop ax iret code ends end start
assume cs:code stack segment db 128 dup(0) stack ends code segment start: mov ax,stack mov ss,ax mov sp,128 push cs pop ds mov ax,0 mov es,ax mov si,offset int9;设置ds:si指向源地址 mov di,204h;设置es:di指向目的地址 mov cx,offset int9end - offset int9;设置cx为传输长度 cld;设置传输方向 rep movsb push es:[9*4] pop es:[200h] push es:[9*4+2] pop es:[202h] cli mov word ptr es:[9*4],204h mov word ptr es:[9*4+2],0 sti mov ax,4c00h int 21h int9: push ax push bx push cx push es in al,60h pushf call dword ptr cs:[200h];当此中断例程执行时(CS)=0 cmp al,3bh;f1的扫描码 jne int9ret mov ax,0b800h mov es,ax mov bx,1 mov cx,2000 s: inc byte ptr es:[bx] add bx,2 loop s int9ret: pop es pop cx pop bx pop ax iret int9end: nop code ends end start
assume cs:code cod segment a:db 1,2,3,4,5,6,7,8 b:dw 0 start: mov si,offset a mov bx,offset b mov cx,8 s: mov al,cs:[si] mov ah,0 add cs:[bx],ax inc si loop s mov ax,4c00h int 21h code ends end start ;代码中的 s、start等都是标号,表示了内存的地址
在code段中使用的标号a,b后面没有:,所以他们能够同时描述内存地址和单元长度的标号
assume cs:code cod segment a db 1,2,3,4,5,6,7,8 ;描述了地址code:0,和从这个地址开始之后的内存单元都是直接单元 b dw 0 ;则b是code[8] start: mov si,0 mov cx,8 s: mov al,a[si] ;至关于mov al,cs:0[si] mov ah,0 add b,ax inc si loop s mov ax,4c00h int 21h code ends end start
assume cs:code,ds:data cod segment a:db 1,2,3,4,5,6,7,8 b:dw 0 data ends start: mov ax,data mov ds,ax mov si,0 s: mov al,a[si] mov ah,0 add b,ax inc si loop s mov ax,4c00h int 21h code ends end start
data segment a db 1,2,3,4,5,6,7,8 b dw 0 c dw a,b ;至关于 c dw offset a,offset b data ends data segment a db 1,2,3,4,5,6,7,8 b dw 0 c dd a,b ;至关于 c dw offset a,seg a,offset b,seg b ;seg操做符,功能是取得某一标号的段地址 data ends
assume cs:code code:segment mov al,0eh call showbyte mov ax,4c00h int 21 ;子程序,用al传送要显示的数据 showbyte: jmp short show table db '1023456789ABCDEF';字符表 show: push bx push es mov ah,al shr ah,1 shr ah,1 shr ah,1 shr ah,1;右移4位,ah中获得高4位的值 and al,00001111b;al中为低4位 mov bl,ah mov bh,0 mov ah,table[bx];用高4位的值做为相对于table的便宜,取得对应的字符 mov bx,0b800h mov es,bx mov es:[160*12+40*2],ah mov bl,al mov bh,0 mov al,table[bx];用低4位的值做为相对于table的偏移,取得对应的字符 mov es:[160*12+40*2+2],al pop es pop bx ret code ends end start
;================================入口函数1===================================== ;入口函数说明; ;用ah传递功能号,0是清屏,1是设置前景色,2是设置背景色,3是向上滚动一行 setscreen: jmp short set table dw sub1,sub2,sub3,sub4 set: push bx cmp ah,3;判断传递的功能号是否大于3 ja sret mov bl,ah mov bh,0 add bx,bx;根据ah中的功能号计算对应子程序的地址在table表中的偏移 call word ptr table[bx];调用对应的子程序 sret; pop bx iret ;================================入口函数2===================================== ;入口函数说明; ;用ah传递功能号,0是清屏,1是设置前景色,2是设置背景色,3是向上滚动一行 setscreen: cmp ah,0 je do1 cmp ah,1 je do2 cmp ah,2 je do3 cmp ah,3 je do4 jmp short sret do1: call sub1 jmp short sret do2: call sub2 jmp short sret do3: call sub3 jmp short sret do4: call sub4 jmp short sret ;子功能========================================================================== ;清屏 sub1: push bx push cx push es mov bx,0b800h mov es,bx mov bx,0 mov cx,2000 sub1s: mov byte ptr es:[bx],'' add bx,2 loop sub1s pop es pop cx pop bx ret ;设置前景色 sub2: push bx push cx push es mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 sub2s: mov byte ptr es:[bx],11111000b or es:[bx],al add bx,2 loop sub2s pop es pop cx pop bx ret ;设置背景色 sub3: push bx push cx push es mov cl,4 shl al,cl mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 sub3s: mov byte ptr es:[bx],10001111b or es:[bx],al add bx,2 loop sub3s pop es pop cx pop bx ret ;向上滚动一行 sub4: push cx push si push di push es push ds mov si,0b800h mov es,si mov ds,si mov si,160;ds:si指向第n+行 mov di,0;es:di指向第n行 cld mov cx,24;共复制24行 sub4s: push cx mov cx,160 rep movsb;复制 pop cx loop sub4s mov cx,80 mov si,0 sub4s1: mov byte ptr es:[160*24+si],'';最后一行清空 add si,2 loop sub4s1 pop ds pop es pop di pop si pop cx ret;结束
笔者看不下去了。。。。有兴趣的读者能够继续找相关的资料看。。。