汇编学习笔记(四)

来源:互联网 发布:sql语句格式化工具 编辑:程序博客网 时间:2024/06/06 05:55
第七章:更灵活的定位内存地址

  实现双层循环的思路:
将外层的cx的值在覆盖前首先保存到一个位置存放,然后再进行覆盖,当内层的循环执行完了之后,再拿出外层的值赋给cx,进行外层循环,如此一来,便记忆了两次循环的次数

双层循环实现:
assume cs:aa

aa segment
start: mov ax,1
  mov cx,3
s: add ax,ax
  mov dx,cx     ;保存原来的循环信息
  
  mov bx,2
  mov cx,4
  s0: add bx,bx
      loop s0
      
      mov cx,dx     ;恢复原来的保存信息
      loop s
      
      mov ax,4c00H  ;妹的,要记住这段话啦,不然看不出结束....
      int 21h
aa ends

end start

分析:在上面的程序中,si(后面的source寄存器),cx(直接控制循环次数),ax,bx(作为内存地址的寄存器[bx])显然不能用来暂存cx的值,因为这些寄存器在循环中也要使用,cs,ip,ds当然也不能使用,因为cs:ip时刻是指向当前指令,ds指向的数据段
可用的寄存器就只有:dx,di,es,ss,sp,bp等6个寄存器,呵呵,所以上面是拿dx开刀的(临时保险箱啊),但是如果万一寄存器在无意中还是被用到咋整啊,所以把循环的数据放在寄存器里总是不安全的哈,转念一想,就笑啦,眼前这么大的内存放着不存数据干啥啊,所以改进版的就是直接存在一段不会使用到的内存地址就行啦
利用自定义内存空间实现临时保存数据:
assume cs:aa
aa segment
dw 0   ;定义一个字,用来保存cx
start: mov ax,1
  mov cx,3
s: add ax,ax
  mov ds:[0H],cx     ;保存原来的循环信息
  
  mov bx,2
  mov cx,4
  s0: add bx,bx
      loop s0
      
      mov cx, ds:[0H]     ;恢复原来的保存信息
      loop s
      
      mov ax,4c00H  ;妹的,要记住这段话啦,不然看不出结束....
      int 21h
aa ends
end start

分析:呵呵,用内存来存放临时变量是没得商量的啦,然后就是变量的问题啦,呵呵,C语言就是对汇编语言的封装啊,如c语言在调用函数之前,必须先保存当前寄存器的值,呵呵,就是将当前的寄存器(实际是其内容)的状态值保存起来,在VC编译器里就是将这些状态值入栈,很明智的选择哈,然后在call完函数之后,这个临时数据栈就要发挥作用啦,依次出栈,然后覆盖对应的寄存器值,(其实也就是一个简单的双层循环的原理而已),然后接着原来函数指令的下一条指令继续执行
  
[bx+idata]:这里使用是使一个内存单元的内容送入ax,如 mov ax,[bx+200] 的含义就是将bx+200的偏移地址,段地址在ds中的内容送入ax
数学化的描述为:(ax) = ((ds*16)+(bx)+200) ,还有其他格式如:mov ax,[200+bx]       mov ax,200[bx]        mov ax,[bx].200 (常数放在后面就会有一个点)这些不常用的诡异写法还是只会认识就好,最好不要写啦

用[bx+idata] 的方式进行数组的处理:有了[bx+idata] 这种内存单元的方式,我们就可以用更高级的结构来看待所需要处理的数据,实际就是,有了底层的内存布局模型,就可以更清楚的了解高级语言的实现方式

小技巧:
在2进制的数据中,大写字母和小写字母的区别就是在第五位有区别的,小写字母的asc码大一些,所以第五位是1,大写字母的as码小一些,所以第五位是0,则想让8位的2进制数第五位变为1的方法就是使用 and al,11011111b,这样就是说取与的时候,只有al数据和与的数据的第五位数据都同时为1时,才为真,呵呵,好有技巧啊,你已经设置了一个零,所以这里是不能为真的,就是永远为假,那第五位可就惨啦,命运就已经定住啦,与完啦都是0,呵呵,目的达到啦,然后是很多的1,当让是为了不改变数据哈, 如直接的例子是 A:01000001 ,a:01100001
如果想让 第五位都变为 1,则就 or al,00100000b   呵呵,就是第五位的值只要一个真就是真,这样第五位的值就是1啦

数组的理解:
实际内存都是平趟的一段一维线性空间,之所以创造这种数据类型,因为这样会便于我们人类理解,就像内存本来没有栈,我们人为将其看做栈空间,这样所谓的数据类型就出现啦,呵呵,一个抽象的(连续的)内存模型而已,这些内存模型就是在内存的地址上进行了一个分段和分类,呵呵

SI:DI:是8086CPU中和bx功能相近的寄存器,但是SI和DI不能分成两个8位的寄存器来使用

db 'welcome to masm'  ;同样定义了一段地址来存放这些字

用SI:DI 实现数据复制:
在上面这16字节后面的的16个字节的偏移地址就是应该是复制的地址,我们使用DS:SI指向要复制的原始字符串,用DS:DI指向复制的目的空间,然后使用一个循环来完成复制,这里的DS 是该段的地址就不用说啦,然后就说一下SI和DI吧,SI(source 资源地址),DI(direction 目标,目的),呵呵,这样就拿下了这两个寄存器吧

[bx+si+idata]和[bx+di+idata] :书写形式还是那么多种,呵呵,就是一个拼接了偏移地址,段地址还是不变的

第八章:指针
    
    简单的指向字符串的指针变量就不说啦,值得一提的是函数指针,一个函数在编译时被分配给一个入口地址,这个函数的入口地址就是函数的指针

    简单的函数调用的实现:

  assume cs:code

data segment   ;自定义数据段
dw 1,2,3,4,5,6,7,8  ;自定义8个字型的数据,就是16个字节
dd 0,0,0,0,0,0,0,0  ;定义8个define double word 就是32个字节
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]  ;将si的值传入bx,这里bx就相当与是传给函数的参数

call cube
mov [di],ax   ;存放第一个函数的返回值
mov 2[di],dx  ;存放函数的高端位
add si,2    ;ds:si指向下一个word单元
add di,4      ;ds:si指向下一个dword单元
loop s
mov ax,4c00H  ;亲爱的结束,一定要记住

int 21H

cube: mov ax,bx  ;使用bx的参数,并且放入ax中,因为mul指令是一个参数,如:mul bx
;就是将ax= bx * ax,即将将变量乘以ax的值然后再放在ax中,这里有个默认
;函数的返回值默认是放在dx:ax中,在dx中存放该数的高端位,在ax存放低端
;位,这里的cube操作就是call函数的入口,类似于跳转
 mul bx
 mul bx
 ret
 
code ends
end start

分析:在这里还是使用寄存器作为函数的返回值,还是像以前的老方法,就是使用内存存放函数的返回值,这样就可以解决多函数返回值的问题,也就解决了多参数的问题,就如VC的入栈保存当前所以寄存器状态值的操作,内存这么大,很容易存噻,自定义数据段,就是所谓的批量的数据传递问题

一个指令的解释: and byte ptr [si],11011111b     就是一次处理一个字节的数据, ptr 的意思是将[si]地址中的数据取出,最后执行and数据的操作,就是将[si]的数据同11011111b的进行与运算

寄存器冲突问题:

一个指令的解释:jcxz  是用来检测是否是零,如果是0则跳转,如果不是零,则不跳转,这个jcxz 检测的寄存器就是CX寄存器的值
例子如:

capital: mov cl,[si]   ;将低八位放置之定义的数据
mov ch,0   ;高八位置零
jcxz ok       ;有条件跳转,当是零的时候就跳转
and byte ptr [si],11011111b
inc si        ;si自加1
jmp short capital    ;无条件跳转
ok: ret

一个指令的解释:div :该指令是除法的意思,进行八位除法的时候,用al存储结果的商,ah存储结果的余数;进行16位除法的时候,用ax存储结果的商,dx储存结果的余数

利用显卡显示字符串:
由于显卡遵循的是ascii编码,为了让我们在显示器上看到这串字符串,他在机器里应该是以ASCii码的形式存储,如:122666,应该为31H,32H,32H,36H,36H,36H(字符'0'-'9'对应的ASCII码应该是30H-39H,这里是十六进制,如果是十进制的就是48-57)
原创粉丝点击