写这个系列文章的主要目的是记录书中重要的知识点,并和大家分享一些个人理解与实践。由于笔记中的知识点比较零散,而书中系统的介绍了一个 x86-16 处理器在实模式下的工作原理以及如何使用汇编语言与其进行“沟通”,所以推荐想要系统学习的朋友们去学习这本书。当我们掌握了实模式的工作原理之后,就可以进一步研究后来出现的其他运行模式(如保护模式)。除此之外,熟悉汇编语言有助于我们掌握上层语言(如 C)的执行原理,因为它们都要对汇编(机器码)进行抽象,而汇编程序就是基于 CPU 的执行机理写出来的。

第九章

转移行为分类

  • 段内转移(只修改 IP, 根据转移的范围不同又分为:短转移近转移
  • 段间转移(同时修改 CSIP

jmp 指令的两种实现差异

在一般的汇编指令中,立即数不论表示一个数据还是内存单元的偏移地址,都会在对应的机器指令中出现
  1. 基于转移的距离——指令中不包含目的地址,而包含的是位移量(向前或向后移动的距离)

    • 优点,可浮动装配,即位移量不变的情况下,这段程序可在任何地方执行
  2. 基于要转移的目的地址

以上两个维度的汇总

基于转移的距离(间接转移)基于要转移的目的地址(直接转移)
段内转移jmp short Label
jmp Labeljmp near ptr Label
jmp 某一合法16位寄存器——将寄存器中的内容写入 IP
jmp word ptr 地址——转移到目的偏移地址
段间转移 jmp far ptr Label(注:它也能够实现段内转移的效果)
jmp dword ptr 地址h_word=段地址,l_word=目的偏移地址)
  • jmp short Label: 翻译成的机器码为 EB??H , ?? 代表 8 位的位移量(这种方式是短转移

    本质为:IP = IP + 8位的位移量 【重点(容易被忽略):CPU 在执行该指令时,是使用这个位移量来计算 IP 的】
    - short 指明此处的位移为 8 位位移,范围是 -128~127, 用【补码】表示
    - 8位的位移量 = 标号处的地址 - jmp指令后的第一个字节的地址,编译器在【编译时】算出
    • 为什么是 jmp 指令后的第一个字节的地址aj?为什么这么设计呢?因为取址之后(执行之前)IP寄存器中的地址会被设置为该地址aj,这样在执行转移指令时,可以直接参与目的偏移地址的计算!

      • 编译时:disp=as-aj, 执行指令时:IP(as) = IP(aj) + disp(位移量) 即直接完成该指令的功能
  • jmp near ptr Label近转移,本质与短转移相同,区别仅仅是它支持16位的位移量
  • jmp far ptr Label远转移

    CS 设置为 标号所在段的【段地址】
    IP 设置为 标号在该段中的【偏移地址】

masm 对 jmp 内部转移的编译实现

【条件转移】和【循环指令】是对【短转移】的功能扩展

  • jcxz 指令的伪代码:

       ......
    
       if ((cx) == 0) {
         jmp short s
       }
    
    s: ......
  • loop 指令的伪代码

    s: ......
    
       (cx)--;
       if ((cx) != 0) {
         jmp short s
       }
    
       ......

实验八:分析一个奇怪的程序

注意:是将 jmp short s1 的机器码 EBF6 拷贝至 076C:0008~076C:0009 两个存储单元中,F6-10 的补码,表示位移量

其实补码 F6 表示什么有符号数,可以不用图片中的方法计算,而使用以下方法计算更为简单:

  • 因为补码 1111 1111 表示十进制有符号数 -1, 这个 -1 的计算方式是:

    $$ -1 = -1*2^7 + 1*2^6 + 1*2^5 + 1*2^4 + 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0 $$

    $$ = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 $$

  • 那么补码 1111 0110(0xF6) 就等于:

    $$ -1*2^7 + 1*2^6 + 1*2^5 + 1*2^4 + 0*2^3 + 1*2^2 + 1*2^1 + 0*2^0 $$

    $$ (-1*2^7+1*2^6+1*2^5+1*2^4+1*2^3+1*2^2+1*2^1+1*2^0) - 1*2^3 - 1*2^0 = -1 - 8 - 1 = -10 $$

实验九:80x25 彩色字符模式缓冲区输出程序

  • 显示缓冲区:B8000H~BFFFFH(共32KB, 8页,每页4KB)
  • 80x25——每页由 25 行组成,每行由 80 个字符(160byte)组成
  • 注:dosbox 中执行完可执行文件,输出结果会被向上顶一行(弹出输入提示符导致),所以以下程序的输出就显示在 11,12,13 行了
; 在屏幕中间分别显示黑底绿色、绿底红色、白底蓝色的字符串'welcome to masm!'
; 题目要求屏幕中间显示?那就 12,13,14 行显示
assume cs:codesg, ds:datasg, ss:stacksg

datasg segment
    db 'welcome to masm!'
    db 02H, 24H, 71H ; 三种属性:黑底绿色、绿底红色、白底蓝色
datasg ends

stacksg segment
    dd 0,0,0,0
stacksg ends

codesg segment
  start: mov ax, datasg
         mov ds, ax

         mov ax, stacksg
         mov ss, ax
         mov sp, 10H

         mov ax, 0B800H ; masm 汇编器要求立即数不能以字母开头
         mov es, ax

         mov bx, 0
         mov bp, 16

         mov cx, 3       ; 共写入3行
    s0:  mov si, 0
         mov di, 1824    ; di=1760+64, 1760是12行第一字节的offset, 每行中间位置的offset=64
         add di, bx      ; 计算每一行开始写入的偏移地址
         mov ah, ds:[bp] ; 该行要显示的属性值

         push cx         ; 保存
         mov cx, 16
     s:  mov al, [si]
         mov es:[di], al ; 显存区域:字符低字节表示 ASCII
         inc di
         mov es:[di], ah ; 字符高字节对应颜色属性
         inc si
         inc di
         loop s

         add bx, 160 ; 0-160-320 (控制写入下一行)
         inc bp      ; 16-17-18(控制获取下一行的属性值)
         pop cx      ; 恢复
         loop s0

         mov ax, 4c00H
         int 21H
codesg ends

end start

第十章

模块化程序设计

  1. callret 指令配合使用(近转移

    • call 等价于:push IP + jmp ...
    • ret等价于:pop IP
  2. call和retf指令配合使用(远转移

    • call等价于:push CS + push IP + jmp ...
    • retf等价于:pop IP + pop CS(注意这个顺序)

call指令

相当于将call指令之后的那一个指令IPCS+IP压栈之后,进行jmp转移。
支持近转移远转移,不支持短转移

  • call Label

    • push IP
    • jmp near ptr Label
  • call far ptr Label

    • push CS
    • push IP
    • jmp far ptr Label
  • call 16位寄存器

    • push IP
    • jmp 16位寄存器
  • call word ptr 内存地址

    • push IP
    • jmp word ptr 内存地址
  • call dword ptr 内存地址

    • push CS
    • push IP
    • jmp dword ptr 内存地址

总结——【目前已经介绍的】转移指令对应的机器码

call 转移指令机器码jmp 转移指令机器码
call LE8 ????(16位位移量)jmp near ptr LE9 ????
call far ptr L9A ???? ????<br/>标号在段中的偏移地址 标号所在段地址jmp far ptr LEA ???? ????
call ax、call bxFFD0、FFD3jmp ax、jmp bxFFE0、FFE3
call word ptr addrFF160000 (todo)jmp word ptr addrFF260000 (todo)
call dword ptr addrFF1E0000 (todo)jmp dword ptr addrFF2E0000 (todo)

8086 中乘法的计算规则

汇编指令:mul 乘数(在内存或某个寄存器中),【被乘数和乘数】要么都是 8 位,要么都是 16 位

8位乘数16位乘数
被乘数8位——在 al16位——在 ax
结果axdx 存高16位、ax 存低 16 位

实验十:编写子程序

(1)指定位置打印字符串

exp10_1.asm

(2)解决除法溢出的问题

exp10_2.asm

(3)二进制转十进制,打印

exp10_3.asm


zandy
1 声望0 粉丝