1.and 和 or 指令
1.and指令:逻辑与指令,按位进行与运算 【一假即假】
1 2 3 4 5 6
| 按位与就是按位与咯 01=0;10=0;11=1;00=0 比如: mov al,01100011B and al,00111011B 执行后 al=00100011B
|
功能:通过该指令可以将操作对象的相应位设置成0
1 2 3 4 5
| 比如: 将al中的第6位设为0 and al,10111111B 将第0位设为0 and al,11111110B
|
2.or指令:逻辑或,按位进行或运算【一真即真】
1 2 3 4
| 比如: mov al,01100011B or al,00111011B 执行后: al=01111011B
|
功能:通过该指令可将操作对象的相应位设为1,其他位不变
1 2 3
| 比如: al的第6位设置位1 or al,01000000B
|
2.关于ASCII码
没什么…
3.以字符形式给处的数据
1.在汇编中,我们可以用 ‘ xxx ‘ 的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的ASCII码
比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| assume ds:data data segment db 'unIX' ;这里会把u n I X 会转化成ASCII码,然后ASCII码只需要8为,所以用db db 'foRK' data ends
code segemnt start: mov al,'a' ;这里ascii码就是是 MOV AL,61H mov vl,'b' mov ax,4c00H int 21h code ends end satrt
|
4.ASCII码大小也转化
改变字母的大小写,其实就是改写ASCII码的值
十进制:a 97 A65 相差32
十六进制 :a61 41A 相差20h
其实在二进制的时候,也就是在第五位0改成1
所以小写<–>大写 可以用and 和 or 完成大小写的转变
哈哈哈练个手:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| assume cs:codesg,ds:datasg datasg segment db 'BaSiC' db 'iNfOrMaTiOn' datasg ends
codesg segment start: mov ax,datasg mov ds,ax ;设置ds指向datasg段 mov bx,0 ;设置(bx)=0,将ds:bx指向'BaSiC'中的第一个字母 mov cx,5 ;设置循环5次,因为BaSiC有五个字母 s: mov al,[bx] ;将ASCII码从ds:bx所指向的内存单元中取出一个放入al中 and al,11011111B;将dl中ASCII码的第五位转成0,将其变成大写字母 mov [bx],al ;将转化有的ASCII码又放回去 inc bx ;(bx)++,将ds:bx指向下一个字母 loop s mov bx,5 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
|
5.[bx+idata]
1.在前面我们可以用[bx]的方式来指明一个内存单元,我们还可以用一种更为灵活的方式来搞
1 2
| [bx+idata] 这个就相当于是:偏移地址=(bx)+idata
|
2.变形
1 2 3 4
| [bx+idata] idata[bx] [bx].idata 这三种方式都可以
|
6.用[bx+idata]的方式进行数组的处理
我们观察datasg段中的俩个字符串,一个起始地址为0,另外一个是5
我们就可以用[0+bx]和[5+bx]在同一个循环中定位这俩个字符串中的字符
1
| 在这里0和5给定了2个字符的起始偏移地址,bx中给出了从起始偏移地址开始的相对地址
|
所以现在就可以把上面的改进
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mov ax,datasg mov ds,ax mov bx,0
mov cx,5 s: mov al[bx] and al,11011111B mov [bx],al mov al[5+bx] or al,00100000B mov [5+bx],al inc bx loop s
|
7.SI和DI
1.SI和DI是8086CPU中和bx功能相近的寄存器,但是SI和DI不能够分成8位寄存器来使用
问题:用寄存器SI和DI实现将字符串 ‘welcome to masm!’复制到它后面的数据区中
思路:用ds:si指向要复制的原始位置,用ds:di来指向复制的位置
写代码咯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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 ;因为si 和di 都是16位所以+2 add di,2 loop s mov ax,4c00H int 21h codesg ends ends start
|
我们也可以用数组的思想
1 2 3 4 5 6 7 8 9 10 11
| codesg segment start: mov ax,datasg mov ds,ax mov si,0 mov cx,8 s: mov ax,0[si] mov 16[si],ax add si,2 loop s
|
8.[bx+si]和[bx+di]
1.[bx+si]表示一个内存单元,它的偏移地址是(bx)+(si)
2.看看
1 2 3
| mov ax,[bx+si] 将一个内存单元的内容送入ax中,这个内存单元的长度为2【字单元】 偏移地址是:bx中的数值+si中的数值,段地址默认是ds
|
3.同类
1 2 3
| [bx+si] [bx][si] 这俩种其实是同一种
|
9.[bx+si+idata]和[bx+di+idata]
emmm和上面的雷同,我吐了
10.用不同的寻址方式的灵活应用
1.比较一下之前用到的几种定位内存地址的方法(也就是寻址的方式)
1 2 3 4 5
| 1.[idata]用一个常量来表示一个地址,可以用于之间定位一个内存单元 2.[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元 3.[bx+idata]这个感觉就是一个一维数组idata[bx]和a[]可以雷同。用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元 4.[bx+si]用2个变量来表示地址 5.[bx+si+idata]用两个变量和一个常量来表示地址
|
2.EMMM这里听得太入迷了,忘了写笔记
无所谓了,那就不写了,记录一点有用的就好了
题目:
3.我们在进行一次循环的时候使用CX 会递减,这个是没有问题的
但是如果我们要用循环里面套循环,这个时候,在里面的循环又需要调用一次cx,这样就会形成cx被覆盖!!
1 2 3 4
| 为了解决这个问题 我们就应该每次开始内层循环的时候,将外层循环的cx中的数值保存起来,在执行外层循环的loop指令前,再回复外层循环的cx数值
我们可以用寄存器dx来保存cx中的值
|
改进后的循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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 mov cx,3 s: mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s ;这个时候cx中的值已经变成0了 add bx,16 mov cx,dx ;将外层的cx还原 loop s0 ;cx=cx-1 再判断是否为0是否再循环
|
上面的程序中就是用的dx,但是如果dx被使用了,或者说找不到合适的寄存器怎么办呢?
就是说,我们这些数据就是需要展示存放一下
为了寻找一个通用的方案,所以我们就可以用内层,我们就可以将需要暂时存的数据放到一段内存单元中,需要使用的时候,再从内存单元中恢复,这样我们就需要开辟一段内存空间
改进后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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 mov cx,4 s0: mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40H单元中 mov si,0 ;用si来表示列 mov cx,3 ;设置内存循环次数 s: mov al,11011111B and al,11011111B mov [bx+si],al ;[bx+si]bx就是行si就是列,虽然在内存中是线性排列的,但是我们可以这么理解 inc si loop s add bx,16 ;这里用bx来表示行 mov cx,ds:[40H] loop s0 mobv ax,4c00H int 21h codesg ends end start
|
但是,这样的作法有点麻烦
所以我们又进步了!!用栈
push 后 pop
又来写代码了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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 mov cx,4 s0: push cx ;把外层循环的cx值放入栈中保存起来,保护cx mov si,0 mov cx,3 ;cx设置成内存循环的次数 s: mov al,[bx+si] and al,11011111B mov [bx+si],al inc si loop s add bx,16 pop cx ;在需要外层的cx的时候,在把他pop出来 loop s0 mov ax,4c00H int 21h codesg ends end start
|
7.9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| ;将datasg段中的每个单词的前4个字母改成大写字母 assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment dw 0,0,0,0,0,0,0,0 stacksg ends
datasg segment db '1. display ' db '2. brows ' db '3. replace ' db '4. modify ' datasg ends ;[bx+si+idata][bx];当列;[si+idata]当行数 codesg segment start: mov ax,stacksg mov ss,ax mov sp,16 ;等会儿外层循环需要push的栈 mov ax,datasg mov ds,dx ;确定数据的段地址 mov bx,0 mov cx,4 ;外层需要4行也就是4次循环 s: push cx ;把cx保护起来 mov si,0 ;因为每一次循环的时候,每一列都行重新索引字母的位置,所以si需要设置在外面 mov cx,4 ;把后面的4个字母都需要改成大写所以要循环4次 s0: mov dl,ds:[bx+3+si] ;这样索引住第一个字母,通过si的自增来索引后面的4个字母 mov dl,11011111B ;这样就是改成大写 mov ds:[bx+3+si],dl ;这里需要把dl中的值放回原来的内存中 inc si ;si++索引下一个 loop s0 add bx,16 ;选中下一行 pop cx ;把原来的cx还回来 loop s mov ax,4c00H int 21h
codesg ends end start
|
总结一下啦
1.si di
2.用栈来保护数据