你说的曾经没有我的故事 提交于 2019-12-02 08:09:12

  不是科班毕业,直接学了Java入行,最近打算学C语言,了解一些入门级的语言,就看了一点入门的东西,这一篇汇编语言也是拾人牙慧,这里贴上原文地址,尊重原创。

  《汇编语言》- 来自底层的较量 - 温故而知新  https://www.cnblogs.com/yougewe/p/8011911.html

  百科的汇编语言定义:汇编语言(assembly language)是一种用于电子计算机微处理器微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。

一、汇编语言能干什么?仅次于机器语言?

  计算机工作是由一系列的机器指令进行驱动工作的,这些指令是一系列二进制数字 0101000110,对应计算机的高低电平,而这些机器指令的集合就是机器语言,这已经是最底层,和硬件一对一了已经。

  很明显,这样的机器语言是反人类的。只有天才和疯子才有能力去把控他。

  于是汇编语言产生了。汇编语言就是将一些难于记忆的0100100转换为便于记忆的格式,让人能够稍微理解点。如 将寄存器bx的内容送到ax中,对应的机器指令是: 100001001110110000(完全不知道是啥玩意,如果真要细究,那么你就得去查看其电路连接了),而变成汇编指令后: mov ax, bx 。哈哈,是不是容易理解多了。所以,我也总结出一个道理,其实所谓的困难与简单,真的只是相对的,没有对比就没有伤害!

  回到前面的问题,汇编语言能干什么? 这个问题有点low了,机器语言都能干成的事,汇编都能干?但是,机器语言这么牛逼吗?难道现在这么多高科技的东西机器语言都能干吗?这是废话,所有语言转到最后,都是机器语言,没有机器语言干不了的事,也可以说,没有汇编干不了的事,只是咱功力不够,哈哈!

  汇编主要有三类指令:1)汇编指令(机器码助记符,与机器指令一一对应,如果可以,其实只要这玩意你就可以操作计算机所有硬件了); 2)伪指令(没有对应机器码,由编译器执行,计算机不执行,也就是说被编译器转换成了一组或更多的机器码去了,现代高级语言编译器都是干这事儿的); 3) 其他符号(如+-*/,和第二个一样,由编译器识别转换)

二、要编写汇编,有哪些基础知识要了解?(我觉得这比语言本身更有意思)

  存储器,其作用仅次于CPU,CPU进行运算,它进行记忆。

  存储单元,内存或外存中比较小的存储块(因为我也不清楚是不是最小的块,哈哈,反正默认情况下操作的都是内存块都是它或者它的倍数)。

  CPU对存储器读写,需进行3步交互:定位存储单元的地址(地址信息);选择器件,读或写的命令(控制信息);对器件读或写的数据(数据信息)。

  地址总线,地址总线的数量,决定了CPU可操作的内存最大值(这个和有的操作系统用不了很大的内存有点像,也可以解释为什么有的手机最大只能扩展xxG内存了);

  数据总线,它的宽度(数量)决定了CPU与外界的传送速度(数据总线越宽,一次能传送的数据量越大,速度越快)。

  控制总线,它的数量决定了对外部器的控制方式数量。

  了解以上这些,也算是对硬件的一些理解,及对真正代码作用的深层次理解吧!

三、汇编所能操作的东西有哪些?(这和写代码还不太一样)

  寄存器,这是整个汇编操作的核心,绝大部分的操作,都是对寄存器的操作。

  对于学习,我们更多的是了解原理,教科书也没有将最新的设备拿出来为难我们。

  针对8086CPU,有14个寄存器,分别为: AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, SS, DS, ES, PSW.。

  ABCD-X,通用寄存器,存储一般性数据。 不过,在汇编中,CX 有个特殊功能,用于存储 loop的循环次数。 这里的寄存器都有分高低位,H、L分别表示高低。示例:

mov ax, 11    ; 将11送入寄存器ax
mov bl, 3       ; 将3送入bl寄存器
add ax, 5       ; 将ax寄存器的值加3

  段寄存器,注意段的概念,段最根本的来源是CPU给出的内存单元的物理地址方式决定的,8086CPU方式为(段地址x16+偏移地址=物理地址)。但是实际的段划分是人为的概念,10000H~100FFH 可以是个段,10000H~10080H也可以是个段。

   CS、IP 是最关键的两个寄存器,它们指示了当前 要读取指令的地址,CS是段地址,IP是指令指针寄存器。 CSx16+IP 就是CPU当前要执行的指令。换个通俗的话,要让CPU执行什么命令,只要改变这两个寄存器的值,就可以做到了。(有一种皇帝的感觉,哈哈),但是CPU不可能提供 mov cs, 11h  等命令操作,使用jmp cs:ip 进行更改值,从而跳转到想执行的命令处。

  DS 寄存器是用于存储数据的段地址,比如要读取某段数据中的值,那么就要先DS指向那里的地址,再加上偏移地址 [...],就可以进行数据读取了(ES作为附加段寄存器,与DS功能一致,在段太多的情况,可以视情况选用即可),如:

mov bx, 1000h    ; 将1000h送入bx寄存器中
mov ds bx           ; 将段地址指向 bx的值指定的值,mov ds, 1h 是不合法的
mov al, [0]          ; 将0号内存单元的值送入al寄存器中

  SS, SP, BP 是栈相关寄存器。CPU提供的栈机制,这真是个伟大的发明,虽然看起来很简单。

  

  入栈过程

  出栈过程

  在语言层面表现出来就是,push, pop 两个命令,任意时刻,SS:SP都指向栈顶元素。 push 时, SP=SP-2,pop时,如果有接收的寄存器则先将栈顶值送入,然后 SP=SP+2,指向下面一个单元。 这有什么用呢? 当然有用,它可以用来临时保存各种数据,然后再插入功能执行完成后,进行现场的恢复,事实上,大部分时候是这么用的。

四、真正的汇编是什么样的?(其实到这里,个人觉得已经有点近高级语言了)

  大家都说,学语言都是从hello world 开始的,然而在汇编这里好像行不通,应该这太难了,哪能入门呢!入门如下:

复制代码
assume cs:codesg        ; 将寄存器与代码段关联

codesg segment
    start: 
        mov ax, 0123h    ; 寄存器送入值
        mov bx, 0457h
        add ax, bx        ; ax + bx 寄存器值相加
        add ax, ax        ; 算 ax 的平方
    
    mov ax, 4c00h
    int 21h                ; 程序结束返回

codesg ends
end start                ; 指定程序入口标号为 start
复制代码

   段前缀,可以直接将某指定的跨段的内存单元读取到。

复制代码
mov ax, ds:[bx]     ; 段地址在ds中,偏移地址在 bx中, ds: cs: ss: ... 等为段前缀
mov ax, cs:[bx]
mov ax, ss:[bx]
mov ax, es:[bx]
mov ax, ss:[0]
mov ax, cs:[0]
复制代码

  一个定位代码地址的样例,如何定位代码的内存地址(我觉得能够说出某数据在内存中的位置是件很酷的事情,绝对够你装逼)?

复制代码
assume cs:codesg        ; 将寄存器与代码段关联,其开始地址即为cs:[0],后续地址只需要进行加减推演即可知道你想知道的代码的内存地址了

codesg segment
    dw 0123h,0456h,0789h,0987h
    start: 
        mov bx, 0
        add ax, 0        
        add cx, 4
        
    s:  add ax, cs:[bx]
        add bx, 2
        loop s
    
    mov ax, 4c00h
    int 21h    

codesg ends
end start
复制代码

 

  SI, DI 寄存器,它的功能与 BX寄存器 相似,但是这两个寄存器无法分成两个8位的寄存器,即不能像 BX 可分成 BH BL一样,SI/DI不能分成 SH SL。SI是源变址寄存器,DI是目的变址寄存器。在串处理指令中,SI用作隐含的源串地址,默认在DS中;DI用做隐含的目的串地址,默认在ES中;此时不能混用。

  BP: 是和堆栈指针SP联合使用的,作为SP校准使用的,只有在寻找堆栈里的数据和使用个别的寻址方式时候才能用到比如说,堆栈中压入了很多数据或者地址,你肯定想通过SP来访问这些数据或者地址,但SP是要指向栈顶的,是不能随便乱改的,这时候你就需要使用BP,把SP的值传递给BP,通过BP来寻找堆栈里数据或者地址.

  PSW 寄存器,标志寄存器,存储各种运算状态标志,配合运算类型进行相应取舍,从而得到相关需要的结果。比如,机器中没有正负,只有标志。下图中空白位为未使用的标志位,也可以从另一个层面来说,标志位不在多,在精。

  OF(11位-overflow flag-溢出标志位)

  DF(10位-direction flag-方向标志位)

  IF(9位-interrupt flag-中断标志位)

  TF(8位-trap flag-陷阱标志位)

  SF(7位-sign flag-负号标志位)

  ZF(6位-zero flag-零值标志位)

  AF(4位-auxiliary carray flag-辅助进位标志位)

  PF(2位-parity flag-奇偶标志位)

  CF(0位-carry flag-进位标志位)

  使用pushf, popf 进行标志状态的暂存。

五、另一个层面的功能?(创世纪的功能)

  中断。使用中断,可以使CPU能放下当前的工作,转而去处理中断的请求,处理完成后,继续回到这里继续执行。中断的功能举足轻重啊!

  内中断。BIOS中断全程例程。

  int n 可以指定中断号。 中断向量表保存了中断源对应的处理程序的入口。使用iret返回中断之前的位置。

  CPU与外部器件之间通信使用端口。

in al, 20h    ; 从20h端口中读入一个字节
out 20h, al ; 往20h端口定稿一个字节

  CMOS RAM芯片。

  外中断。随时接收外设输入的到达,即通过中断机制来完成。中断是伟大的!

 

  汇编还是有好些编程技巧,但是本文并没有讲许多语言层面的东西。因为,越是技巧性性的东西,我觉得现代的语言已经够多了,咱也没必要在汇编上去花费太多功夫。

  了解汇编,不是想用来写什么牛逼的功能,而是让自己更清楚的明白自己在做的是什么事情。

  我觉得有必要好好了解!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!