跳过非指令的数据区

一般来说,所有处理器指令都应当按顺序存放,在它们中间不允许夹杂非指令的普通数据,因为他们不能作为指令执行,所以要想办法让处理器执行不到这些非指令的内容,比如jmp指令等

在数据声明中使用字面值

  char db 'L', 0x07 \ 
          'a', 0x07

编译阶段会将这些字面值转换成等价的ASCII代码,在NASM中\是续行符,表明下一行应该与当前行合并

段之间的批量数据传送

批量数据传送的指令是movsb 和 movsw,b表示传送以字节为单位,w表示传送以字为单位
这两个指令执行时的前置条件:

  1. DS:SI指向原始数据串ES:DI指向传送的目的地址
  2. 传送次数由CX指定
  3. 传送的方向由标志寄存器的DF位决定,0---正向,1---反向
    - 正向传送,低地址--->高地址,执行cld指令将DF置0
    - 反向传送,高地址--->低地址,执行std指令将DF置1
; 数据
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
          'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0,0
...
...
       ; 批量数据传送的代码
       cld
       mov si,mytext                 ; 设置原数据串的偏移位置     
       mov di,0                      ; 设置目的地址的位置位置
       mov cx,(number-mytext)/2      ; 实际上等于 13,除以2是因为一次传送2个字节
       rep movsw                     ; rep表示重复执行,知道cx为0

使用循环分解数位

loop指令也相当于跳转指令,跳转的是相对量,循环次数由CX指定

并且loop指令是先将CX的值减1,在跳转,若CX的值为0,则退出循环

我们可以将loop指令的循环理解为先执行,后判断

         ;得到标号所代表的偏移地址
         mov ax,number
         
         ;计算各个数位
         mov bx,ax
         mov cx,5                      ;循环次数 
         mov si,10                     ;除数 
  digit: 
         xor dx,dx
         div si
         mov [bx],dl                   ;保存数位
         inc bx                        ; inc 指令表示加1,dec指令表示减1
         loop digit                    ; 跳转到digit标号处继续执行

无符号数和有符号数

  • neg 指令: neg al,表示用0减去al中的值,并将其存储到al中
  • idiv 指令: 与div用法相同,不同的是idiv是用于有符号数的计算,而div用于无符号数的计算(注意有符号数的扩展)
  • 有符号数的扩展:计算机根据最高位来判断有符号数的正负,1位负,0为正,所以当16为有符号数扩展到32位时,正数没什么问题,高16位全是0,负数就要注意了,高16位应该全是1
    • cbw指令,将AL中的有符号数扩展到整个AX(convert byte to word
    • cwd指令,将AX的有符号数扩展到DX:AX(convert word to double word

其他标志位和条件转移指令

  • 标志位
    • 奇偶标志位PF: 运算结果低8位有偶数个1的比特,PF=1,反之,PF=0
    • 进位标志位CF:进行算术操作时,最高位有向前进位或借位时,CF=1.反之,CF=0 (inc指令和dec指令无需记录)
    • 溢出标志位OF:超出目标操作数所能容纳的范围,OF=1,反之,OF=0
  • 条件转移指令
    • 可以根据实际计算结果的标志位进行条件转移
    • cmp指令:用于比较操作数,不同的是cmp仅仅根据比较结果设置标志位,而不保留计算结果,不改变原有的内容
      • cmp 目的操作数, 源操作数,重点关心目的操作数,比如cmp ax, bx,我们关心的是ax的内容是大于bx还是等于bx,bx仅仅是一个测量基准

NASM编译器的$和$$标记

  • $标记:可以看成是当前行行首的标号 (即 当前行的汇编地址)
  • $$标记:代表当前汇编段的起始汇编地址

bochs调试命令

  • n命令:可以跳过循环, 但对条件转移指令造成的循环无效
  • u命令:反汇编,将机器码翻译成可读的汇编指令,u/n 0xb8000表示从0xb8000这个地址开始反汇编n条指令
  • 如何越过条件转移指令构造的特殊循环体:
    • 先用u命令反汇编,找到条件转移指令的下一条指令A
    • 在用b命令对指令A设置断点
    • 在用c命令继续执行到断点处
  • info命令查看标志位
    输入info eflags,会跳出标志位的名称,大写表示为1,小写表示为0

思考题

以下程序会执行多少次

        mov cx, 0
delay:  loop delay

首先loop会将cx的值减1,也就是cx的值为-1,即0xFFFF, 执行1次
然后loop在将cx的值减1,也就是cx的值为-2,即0xFFFE, 执行2次
...
最后.......................cx的值为-65535,即0x1 0000, 舍去最高位1(超出16位), cx的值为0x0000即0,跳出循环,执行65535次

在 x86 的寄存器中,减一操作(SUB 指令)会按照二进制补码进行运算。当寄存器的值为 0 时,再次减一会导致它变成全1的补码形式。因此,CX 寄存器的值会从0xFFFF 到 0x0000

在我想来应该是这样,有错误请大家纠正

完整源码

         ;代码清单6-1
         ;文件名:c06_mbr.asm
         ;文件说明:硬盘主引导扇区代码
         ;创建日期:2011-4-12 22:12 
      
         jmp near start
         
  mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
            'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
  number db 0,0,0,0,0
  
  start:
         mov ax,0x7c0                  ;设置数据段基地址 
         mov ds,ax
         
         mov ax,0xb800                 ;设置附加段基地址 
         mov es,ax
         
         cld
         mov si,mytext                 
         mov di,0
         mov cx,(number-mytext)/2      ;实际上等于 13
         rep movsw
     
         ;得到标号所代表的偏移地址
         mov ax,number
         
         ;计算各个数位
         mov bx,ax
         mov cx,5                      ;循环次数 
         mov si,10                     ;除数 
  digit: 
         xor dx,dx
         div si
         mov [bx],dl                   ;保存数位
         inc bx 
         loop digit
         
         ;显示各个数位
         mov bx,number 
         mov si,4                      
   show:
         mov al,[bx+si]
         add al,0x30
         mov ah,0x04
         mov [es:di],ax
         add di,2
         dec si
         jns show
         
         mov word [es:di],0x0744

         jmp near $

  times 510-($-$$) db 0
                   db 0x55,0xaa
 posted on 2024-08-03 17:38  Dylaris  阅读(58)  评论(0)    收藏  举报