登录  | 立即注册

游客您好!登录后享受更多精彩

查看: 131|回复: 0

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

[复制链接]

42

主题

-7

回帖

48

积分

网站编辑

积分
48
发表于 2024-12-21 22:19:20 | 显示全部楼层 |阅读模式

输入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

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:

本帖被以下淘专辑推荐:

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|断点社区 |网站地图

GMT+8, 2025-1-18 21:06 , Processed in 0.070155 second(s), 29 queries .

Powered by XiunoBBS

Copyright © 2001-2025, 断点社区.

快速回复 返回顶部 返回列表