大理寺少卿 发表于 2024-12-21 22:19:20

8086汇编(16位汇编)学习笔记07.补课

#### 输入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,
    mov byte ptr , size g_buf

    mov ah, 0ah
    int 21h

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

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

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

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

;统计单词个数   如果前面是空格 后面是非空格,单词数量+ 1
SKP_SPACE:;跳过空格
    cmp byte ptr, ' '
    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 , ' '
    je SKP_SPACE         ;相等执行跳转,如果是空格,就还行跳过空格操作

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

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

;统计单词个数结束
STATIC_END:

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

   ;取出g_nCntOfWords的低字节低四位 值
    mov bx, g_nCntOfWords
    and bx, 0fh
    lea bx,
    mov al,
    lea bx,
    mov , al

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

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

   ;取出g_nCntOfWords的搞字节低四位 值
    mov bx, g_nCntOfWords      
    mov cl, 8
    shr bx, cl
    and bx, 0fh
    lea bx,
    mov al,
    lea bx,
    mov , al

    ;取出g_nCntOfWords的高字节高四位 值
    mov bx, g_nCntOfWords
    mov cl, 12
    shr bx, cl
    and bx, 0fh
    lea bx,
    mov al,
    lea bx,
    mov , al

    ;在输出单词数量后面加 '$'
    lea bx,
    mov byte ptr , '$'



    ;输出单词数量
    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次
    jlINPUT_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         
movax , t
sub   ax , x
neg   ax
sbb   ax , ax
and   ax , n - m
add   ax,    m
```

#### 段超越

改变默认的 基址   段寄存器

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

bp 的段寄存器是   ss

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

#### 寻址

mov ax , imm - 立即寻址

mov ax , -直接寻址

mov ax, bx -寄存器寻址

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

mov ax,



mov ax, - 寄存器相对寻址



mov ax, - 基质变址



mov ax, - 基质变址相对寻址

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

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

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

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

```

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

8086寄存器功能

8个通用:   ax ,cx ,dxbx    bpspdibi

ip寄存器:ip

标志寄存器 : flag

4个段寄存器:cs    ds   ss   es

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

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

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

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

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

!(./notesimg/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:
```
页: [1]
查看完整版本: 8086汇编(16位汇编)学习笔记07.补课