0 0 0

科锐学习笔记-第三阶段-16位汇编 07补课

admin
215 0

本文共计8198个字,预计阅读时长32.8分钟。

目录

输入5行文本,统计每行文本的单词的个数,并输出。 

;输入5行文本,统计每行文本的单词的个数,并输出。 
stack_seg segment stack 
   db 512 dup(0) 
stack_seg ends 

data_seg segment   
   g_nCntOfBytes dw 0        ;输入的字符数 
   g_nCntOfWords dw 0        ;输入的单词数 

   g_szOut db 6 dup(0)       ;输出字符数量 
   g_szRet db 0dh, 0ah, '$'  ;回车换行 

   ;字符数组 
   g_szMap db '0', '1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' ; 
   g_buf db 255 dup(0)       ;接收输入的字符串 
data_seg ends 

code_seg segment 
START: 
   assume ds:data_seg 
   mov ax, data_seg 
   mov ds, ax 

   xor cx, cx ;cx的值置0 

;输入单词    
INPUT_CINTNUE: 

   ;lea dx, g_szRet 
   ;mov ah, 09h 
   ;int 21h 

   ;输入文本 
   mov dx, offset g_buf 
   lea bx, [offset g_buf] 
   mov byte ptr [bx], size g_buf 

   mov ah, 0ah 
   int 21h 

   ;回车 
   lea dx, g_szRet 
   mov ah, 09h 
   int 21h 

   ;字符总个数 
   lea bx, [offset g_buf + 1] 
   mov al, byte ptr [bx]        ;获取字符总数量 
   cbw                          ;拓展为字,高位填充0 
   mov g_nCntOfBytes, ax        ;将字符总数量赋值给  g_nCntOfBytes 

   ;直接输入回车,单词数量为0 
   cmp ax,0   
   jz  STATIC_END  

   ;计算单词个数 
   mov si, offset g_buf + 2;字符串起始位置 
   mov di, si 
   add di, g_nCntOfBytes ;字符串结束位置 

;统计单词个数   如果前面是空格 后面是非空格  ,单词数量  + 1 
SKP_SPACE:;跳过空格 
   cmp byte ptr[si], ' ' 
   jnz STATIC_WORD     ;如果不是空格,单词加1   

   inc si              ;移动到下一个字符串 

   cmp si, di 
   jz STATIC_END       ;如果到了字符串结束位置直接跳转到输出字符串 
   jmp SKP_SPACE 

STATIC_WORD:   ;跳过单词 
   inc g_nCntOfWords;单词个数+1 

;比较单词  碰到空格就继续比较下一个,直到找到下一个不是空格的 单词数量+1 
SKIP_WORD:   
   cmp byte ptr [si], ' ' 
   je SKP_SPACE           ;相等执行跳转,如果是空格,就还行跳过空格操作 

   inc si                 ;如果不是空格,比较下一个字符 

   cmp si, di            ;判断是否到了字符串结束位置 
   jne SKIP_WORD         ;不相等执行跳转  

;统计单词个数结束 
STATIC_END: 

   ;输出个数保存到字符串,小尾形式保存 

    ;取出  g_nCntOfWords  的低字节低四位 值  
   mov bx, g_nCntOfWords 
   and bx, 0fh 
   lea bx, [offset g_szMap + bx] 
   mov al, [bx] 
   lea bx, [offset g_szOut+3] 
   mov [bx], al 

   push cx       ;将cx的值入栈  ,保存cx的值,后面会改动 

   ;取出  g_nCntOfWords  的低字节高四位 值   
   mov bx, g_nCntOfWords 
   mov cl, 4 
   shr bx, cl             ;右移四位 
   and bx, 0fh            ;取低字节的 高四位 
   lea bx, [offset g_szMap + bx]    ;获取低四字节对应的字符 
   mov al, [bx] 
   lea bx, [offset g_szOut+2]      ;将对应的字符存入字符串 
   mov [bx], al 

    ;取出  g_nCntOfWords  的搞字节低四位 值   
   mov bx, g_nCntOfWords           
   mov cl, 8 
   shr bx, cl 
   and bx, 0fh 
   lea bx, [offset g_szMap + bx] 
   mov al, [bx] 
   lea bx, [offset g_szOut+1] 
   mov [bx], al 

   ;取出  g_nCntOfWords  的高字节高四位 值   
   mov bx, g_nCntOfWords 
   mov cl, 12 
   shr bx, cl 
   and bx, 0fh 
   lea bx, [offset g_szMap + bx] 
   mov al, [bx] 
   lea bx, [offset g_szOut+0] 
   mov [bx], al 

   ;在输出单词数量后面加 '$' 
   lea bx, [offset g_szOut + 4] 
   mov byte ptr [bx], '$' 

   ;输出单词数量 
   mov ah, 9 
   mov dx, offset g_szOut 
   int 21h 

   ;回车 
   lea dx, g_szRet 
   mov ah, 09h 
   int 21h 

   ;将字符串重置 
   ;mov al,'0' 
   ;lea di,g_buf      
   ;mov cx,g_nCntOfBytes  
   ;REP stosb 

   ;将用来输出结果的字符串重置 
   ;lea di,g_szOut      
   ;mov cx,size g_szOut 
   ;REP stosb 

   mov g_nCntOfWords, 0   ;单词个数重置为0 
   pop cx                 ;cx出栈.记录之前cx的值,循环次数 

   inc cx 
   cmp cx, 5              ; 判断是否循环了5次  
   jl  INPUT_CINTNUE      ; 如果结果小于0继续循环 

   mov ax, 4c00h 
   int 21h 

code_seg ends 

end START 
``` 

#### 跳转 

跳转中指令用得最多的是 : 

jmp           无条件跳转 

jz/je          相等,等于0 

jz/jnz        不相等,不等于0 

其次是: 

jl                 小于 

jg               大于 

jle              小于等于 

jge             大于等于 

跳转的时候其实不需要怎么关注标志位,只需要根据自己的逻辑去调即可 

#### 字符串 

MOVSB     类似于      memcpy        拷贝 

STOSB        类似于      memset         重置 

LODSB                                                  拷贝一个字符 

CMPSB       类似于     memcmp      字符串比较 

SCASB       类似于     strstr              字符查找 

#### 无分支实现三目运算 

``` 
reg == x ? m :  n            
mov  ax , t 
sub   ax , x 
neg   ax 
sbb   ax , ax 
and   ax , n - m 
add   ax  ,    m 
``` 

#### 段超越 

改变默认的 基址   段寄存器 

bx , si ,di  的默认段基址 寄存器是    ds   

bp 的段寄存器是   ss 

``` 
mov ax, [bx]         ;此时基址段寄存器是ds 
mov ax, es:[bx],     ;此时基址段寄存器是es 
mov ax, ss:[bx]      ;此时基址段寄存器是ss 
mov ax, [bp]         ;此时基址段寄存器是ss 
mov ax, [bp]         ;此时基址段寄存器是ss 
mov ax, ds:[bp]      ;此时基址段寄存器是ds 
mov ax, es:[bp]      ;此时基址段寄存器是es 
``` 

#### 寻址 

mov ax , imm - 立即寻址 

mov ax , [imm] -直接寻址 

mov ax, bx -  寄存器寻址 

[reg16] - bp bx si di -  寄存器寻间接寻址 

mov ax, [bx] 

[reg16+imm]  

mov ax, [bx+ 16] - 寄存器相对寻址 

[reg16+reg16] 

mov ax, [bx+si] - 基质变址 

[reg16+reg16+imm] 

mov ax, [bx+di+133] - 基质变址相对寻址 

汇编给我们提供了灵活的访问内存方式 

例如:   获取数字某个元素的值 

``` 
lea bx, g_ary;    ; g_ary 是 一个 word 的数组 
 mov si, imm       ; g_ary 数组的 第 imm 个 元素 
LOOP: 
 mov ax, [bx+si]   ; 获取数组第 imm  个元素的值 

  ;其它操作 

 add si, 2*1       ;获取下一个元素的值,2是数组元素类型大小(字节),1是下一个字节 
 jmp LOOP 

``` 

注意:  汇编里面并不会自动帮我们计算 元素 类型大小,,需要我们自己告诉程序 

8086寄存器功能  

8个通用:   ax ,cx ,dx  bx    bp  sp  di  bi 

ip寄存器:  ip 

标志寄存器 : flag 

4个段寄存器:  cs    ds     ss     es 

我们使用寄存器的时候并没有做严格的区分 

8个通用寄存器 中 能用 ax 的尽量用  ax ,速度最快,其次cx,一般用于累加器(计数),用dx,bx也可以,没有严格要求 ,在内存操作中  si 一般存 源操作数  , di 一般存目的操作数  ,sp ,bp 用于栈,访问局部变量需要用到,不涉及到字符串(内存)操作的话    ax   cx   dx   bx   di  bi  这6个寄存器区别不是很大,想怎么用都可以 , bp ,sp 有专门用途,一般不用来存数据 

ip 一般不用管,cpu自己负责,随便改动ip寄存器可能导致程序出错, ip一般只能通过  跳转指令(JXX ,  xx是通配符,反之以 J 开头的指令 ,如 jmp ,jz 等)去改  

flag 一般也是由 cpu 负责的 ,一般 我们就改动   DF(控制串方向)  和  IF(中断)  和  TF  标志位(控制位 ) 

 IF标志位是用来屏蔽中断的,一般是开着的,但是当我们修改中断向量表(内存偏移 0 ~ 400)的时候需要暂时关闭,否则可能出现修改到一半,程序跳走的情况修改指令,可通过   CLI (关)   STI(开)  进行修改 ,TF 标志位 32位 汇编是才用得上,调试器里经常用,单步用的 

![image.png](krimg/1652707492547-6e1bbc55-8519-4a86-9192-b6c4a31c937c.png) 

4个段寄存器:   cs,和  ss  基本不用管,也不要去动 es一般是串操作时用到,其他时候基本用不到, ds是唯一一个需要我们取管理的,3环是无法操作段寄存器的,只有进内核才能修改 

文件读写  

``` 
;打开指定文件,在后面最后添加字符串,  
stack_seg segment stack 
   db 512 dup(0) 
stack_seg ends 

data segment 
   g_szFileName db "test.asm", 0    ;要操作的文件 
   g_szErr db "file error", '$'     ;打开文件失败提示 
   g_bufForW db "this is a string, are you believe?"   ;要写入的字符串 
   g_wFileCode dw 0                 ;文件代号(句柄) 
   g_buf db 256 dup(0)              ;接收文件内容的缓冲区 
data ends 

code segment 

START: 
   assume ds:data 
   mov ax, data 
   mov ds, ax 

  ;3D  打开文件  DS:DX=ASCIIZ串地址  成功:AX=文件代号 
   ;AL=0 读 =1 写   错误:AX=错误码 

   ; 根据文件名打开文件 
   mov dx, offset g_szFileName 
   mov al, 1                     ;文件属性0读 1写 ,不要用3,3目前是没用的 
   mov ah, 3Dh                   ;调用打开文件功能 
   int 21h 

   jnc OPEN_SUCC                 ;打开文件失败 cf = 1 代表打开文件失败,CF =0 跳转 
       ;输出打开文件失败字符串 
       mov dx, offset g_szErr 
       mov ah, 09h 
       int 21h 

OPEN_SUCC:                ;打开文件成功 

   mov g_wFileCode, ax   ;将文件句柄给 g_wFileCode,后面需要改动ax的值 

 ;42  移动文件指针  BX=文件代号      成功:DX:AX=新文件指针位置 
      ;CX:DX=位移量    出错:AX=错误码 
      ;AL=移动方式 
      ;0:从文件头绝对位移 
      ;1:从当前位置相对移动 
      ;2:从文件尾绝对位移  

   ;移动文件指针到末尾 
   mov bx, g_wFileCode  ;将文件句柄给bx 
   xor cx, cx           ;将cx置0 等于 mov cx,0 
   xor dx, dx           ;将dx置0 等于 mov dx,0 
   mov al, 2 
   mov ah, 42h 
   int 21h 

   ;40  写文件或设备  DS:DX=数据缓冲区地址  写成功: 
           ;BX=文件代号    AX=实际写入的字节数 
           ; CX=写入的字节数     写出错:AX=错误码  

   ;写入 
   lea dx, g_bufForW        ;获取要写入的字符串缓冲区首地址 
   mov bx, g_wFileCode      ;将文件句柄给bx 
   mov cx, offset g_wFileCode - offset g_bufForW  ;将g_bufForW 的长度给 cx 
   mov ah, 40h              ;调用写文件功能 
   int 21h 
   jmp READ_SUCC            ;文件写入完成后,调用关闭文件功能  

    ;3f     读文件或设备   DS:DX=数据缓冲区地址  读成功: 
                ; BX=文件代号        AX=实际读入的字节数 
             ; CX=读取的字节数     AX=0 已到文件尾 
                          ; 读出错:AX=错误码                  
   ;将文件内容读取到缓冲区 
   mov dx, offset g_buf     ;获取g_buf的首地址 = lea dx, g_buf 
   mov bx, g_wFileCode      ;将文件句柄给 bx 
   mov cx, size g_buf       ;要读取的字节数 
   mov ah, 3fh 
   int 21h 
   jnc READ_SUCC            ;  操作失败 cf位 =  1 

       mov dx, offset g_szErr 
       mov ah, 09h 
       int 21h 

READ_SUCC: 

   ;3E  关闭文件  BX=文件代号 失败:AX=错误码  
   ;关闭 
   mov bx, g_wFileCode 
   mov ah, 3eh 
   int 21h 

   ; 带返回码的退出程序   4C  带返回码结束 AL=返回码  
   mov ax, 4c00h 
   int 21h 

code ends 
end START 

``` 

标志位  

四则运算( add   sub    mul    div)  是需要关注标志位的,因为要确定自己的运算结果有没有出问题,有没有溢出,进位等, 加法和减法  影响除了,控制位以外的其他标志位 ,乘除法要看 具体指令,一般影响 OF  和  ZF 

跳转,逻辑运算,移位,传送 ,串传送 这些基本不关注标志位 

汇编模拟 for 循环  

``` 
for(int i = 0; i <65; ++i) 
{ 
 printf("hello word"); 
} 

;初始化 
INIT:  
 xor cx, cx 

;判断 
IF_CMP: 
  cmp cx, 65 
  jge IF_END 

  ;printf "hello word" 

IF_STEP: 
 inc cx 
 jmp IF_CMP 

IF_END: 

最新回复 ( 0 )
全部楼主