输入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位 汇编是才用得上,调试器里经常用,单步用的
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: