汇编语言笔记7更灵活的定位内存地址的方法

汇编语言笔记7更灵活的定位内存地址的方法

0x00 and和or指令和ascii码

​ and 指令:逻辑与指令,按位进行与运算

​ mov al,01100001b and al,00111011b

​ 最终al的结果是 00100011b 可以将操作对象的相应位设为0,其他位不变

​ or指令:逻辑或运算,按位进行或运算

​ mov al,01100011b or al,00111011b

​ 最终al的结果是01111011b 可以将操作对象的相对应为设为1,其他位不变

​ 关于ascii码 计算机为了能将文字信息储存,要将其二进制编码,方案有很多中ascii码是其中一种,例如61h就代表‘a’,ascii 只包括英语大小写字母与部分符号,每个字符8位表示

​ 我们按下键盘a建,这个按键事件被送入计算机,计算机利用ascii码规则找到对应编码61h,储存到内存空间,文本编辑器从内存中取出61h,将其送到显卡的显存中,显卡用ascii码的规制解释显存中的内容,61h被当作字符a,驱动显示器,将‘a’画到屏幕上

以字符形式给出数据

assume cs:code,ds:data
data segment
		db 'unIX'
		db 'foRK'
data ends
code segment
start:	mov al,'a'
		mov bl,'b'
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200530213244

QQ截图20200530213301

​ 通过查看编译后的结果我们知道,masm在编译时遇到字符会直接编译成对应ascii码

大小写转换问题

assume cs:code,ds:data
data segment
		db 'BaSic'
		db 'iNfOrMaTiOn'
data ends
code segment
	...

​ 如何补充代码将data区里第一个字符串变成大写,第二个字符串变小写?

​ c语言时用的方法是判断ascii的区间65-90为大写字母 +32变成对应小写字母 97-122是小写 -32变对应大写,但是 我们现在没有学汇编的判断指令,所以不能用这个方法

QQ截图20200530214105

​ 通过观察上图可以看出 二进制表示时 第5位(从右到左从0开始数)为0即是大写字母,反之为小写,那么很简单了,我们要将字符串大写就全部将第5为变0 其余不变 也就是 and 11011111b 反之变小写就是 or 00100000b了

​ 那么我们的代码如下

assume cs:code,ds:data
data segment
		db 'BaSic'
		db 'iNfOrMaTiOn'
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov bx,0
		mov cx,5
s:		mov al,[bx]
		and al,11011111b
		mov [bx],al
		inc bx
		loop s
		mov cx,11
s1:		mov al,[bx]
		or al,00100000b
		mov [bx],al
		inc bx
		loop s1
		
		mov ax,4c00h
		int 21h
code ends
end start

​ 这是执行完后的结果,说明正确完成了要求

QQ截图20200530215410

0x01 更多偏移地址表示方式

[bx+idata]

​ 表示偏移地址由bx里的值+idata组成,利用这个我们可以进行多个等长数组同时处理了

​ 例如将第一个字符串大写,第二个字符串小写

assume cs:code,ds:data
data segment
		db 'BaSic'
		db 'MinIX'
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov bx,0
		mov cx,5
s:		mov al,[bx]
		and al,11011111b
		mov [bx],al
		mov al,[bx+5]
		or al,00100000b
		mov [bx+5],al
		inc bx
		loop s
		
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200531092702

​ 由上图结果可知代码无误,另外[bx+idata]在masm里还有一种写法 idata[bx] 例如 [bx]可以写成0[bx] [bx+5]可以写成5[bx]

QQ截图20200531092955

si和di寄存器

​ si和di是8086cpu中和bx功能近似的寄存器,也用来表示偏移地址,但是不能拆分成两个8为寄存器来使用,以下指令实现相同功能

​ 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]

QQ截图20200531093400

assume cs:code,ds:data
data segment
		db 'welcome to masm!'
		db '................'
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov si,0
		mov cx,16
s:		mov al,[si]
		mov [si+16],al
		inc si
		loop s
		
		mov ax,4c00h
		int 21h
code ends
end start

​ 注意 上述代码用的al作为临时储存的寄存器,所以是一个字节一个字节的复制,但是我们观察数据是16字节,所以我们也可以用ax两个字节一起赋值,这样就不是inc si 是 add si,2

[bx+si]和[bx+di]

​ 有了两个变量作为偏移地址,那我们就可以实现二维数组的遍历 将bx作为行偏移量 di/si作为列偏移量,但是这样需要嵌套的循环 而我们的loop都以cx作为循环依据,如何实现嵌套循环呢?实现思路就是进入内循环前保存外循环cx的值,出时读取 同样的保存内循环的值

QQ截图20200531094414

QQ截图20200531094424

​ 很简单 ax 00beh bx 1000h cx 0606h

[bx+si+idata]和[bx+di+idata]

QQ截图20200531094803

​ ax 0006h cx 6a00h bx 6a22h

0x02 不同寻址方式的灵活运用-嵌套循环的实现

QQ截图20200531095132

assume cs:code,ds:data
data segment
	db '1. file         '
	db '2. edit         '
	db '3. search       '
	db '4. help         '
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov bx,0
		mov cx,4
s:		mov al,[bx+3]
		and al,11011111b
		mov [bx+3],al
		add bx,16
		loop s
		
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200531095824

​ 上述代码是正确的

QQ截图20200531095926

assume cs:code,ds:data
data segment
		db 'ibm             '
		db 'dec             '
		db 'dos             '
		db 'vax             '
data ends
code segment
start:	mov ax,data
		mov ds,ax
		mov bx,0;行初始值
		mov cx,4;行循环变量初始值
h:		mov dx,cx;行循环变量保存在dx里
		mov cx,3;列循环变量初始值
		mov si,0
l:		mov al,[bx+si]
		and al,11011111b
		mov [bx+si],al
		inc si
		loop l
		mov cx,dx;恢复行循环变量
		add bx,16
		loop h
		
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200531101239

​ 上述代码运行正确,其对应的c语言代码如下

int translate(char* start){
    int h=0,l=0;
    int bx=0;
    for(h=4;h>0;h--){
        int si=0;
        for(l=3;l>0;l--){
            start[bx+si]&=11011111b;
            si++;
        }
        bx+=16;
    }
    return 0;
}

​ 8086中只有一个循环变量,我们要同时进行两个循环,因此每次开始内循环前,将cx保存起来,我们这里用的是dx保存,实际上应该用栈比较好,保存就push cx 回复就pop cx

assume cs:code,ds:data
data segment
		db 'ibm             '
		db 'dec             '
		db 'dos             '
		db 'vax             '
data ends
stack segment
		dw 0,0,0,0,0,0,0,0
stack ends
code segment
start:	mov ax,data
		mov ds,ax
		mov ax,stack
		mov ss,ax
		mov sp,16
		mov bx,0;行初始值
		mov cx,4;行循环变量初始值
h:		push cx;行循环变量进栈
		mov cx,3;列循环变量初始值
		mov si,0
l:		mov al,[bx+si]
		and al,11011111b
		mov [bx+si],al
		inc si
		loop l
		pop cx;行循环变量出栈
		add bx,16
		loop h
		
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200531102232

QQ截图20200531102258

assume cs:code,ds:data,ss:stack
data segment
		db '1. display      '
		db '2. brows        '
		db '3. replace      '
		db '4. modify       '
data ends
stack segment
		dw 0,0,0,0,0,0,0,0
stack ends
code segment
start:	mov ax,stack
		mov ss,ax
		mov sp,16
		mov ax,data
		mov ds,ax
		mov cx,4
		mov bx,0
h:		push cx
		mov si,0
		mov cx,4
l:		mov al,[bx+si+3]
		and al,11011111b
		mov [bx+si+3],al
		inc si
		loop l
		pop cx
		add bx,16
		loop h
		
		mov ax,4c00h
		int 21h
code ends
end start

QQ截图20200531103051

​ 以上代码运行结果正确