8086汇编复习手册

文章目录

8086汇编复习手册

声明 : 本文全部内容的背景均为8086平台;文中代码基本都经实际验证过。文章内容均个人(PGZXB pgzxb@qq.com)整理,未经校正,欢迎勘误!

8086平台基础

大小端

  • 8086平台为小端机, 即低字节存放在低地址, 高字节存放在高地址

寄存器

  1. 通用寄存器

    (1) 数据寄存器

    AX: 累加器, 所有的I/O指令都使用累加器与外设接口传送信息;

    BX: 基址寄存器, 可用来存放偏移地址;

    CX: 计数寄存器, LOOP指令用作计数器;

    DX: 数据寄存器;

    注: DX和AX常用来作为整体存放32位数, 高位存在DX, 低位存在AX (MUL指令、CWD指令)。

    (2) 地址指针寄存器

    SP: 堆栈指针寄存器,指向堆栈的栈顶;

    BP: 基址指针寄存器,通常是与SS寄存器配对使用。

    (3)变址寄存器

    SI: 源变址寄存器。

    DI: 目的变址寄存器。

    它们常常在处理数组(或串)中作为索引(Index)。

    注意: 通用寄存器是通用的,都可用来存放普通数据(地址寄存器存个数字也是可以的),但以上三类每一类寄存器都专有用途。

  2. 段寄存器

CS: 代码段寄存器, 指令存放在代码段内

SS: 堆栈段寄存器, PUSH, POP

DS: 数据段寄存器, 变量(常量)一般都定义于数据段

ES: 附加段寄存器, 不常用,串指令用到过

存放段基地址, 即段起始地址的高16位。

  1. 注意事项
    • CS不能被指令修改
    • 只有BX, BP, SI, DI能当作地址寄存器被使用, SP能作地址寄存器但是不能被使用(修改)
    • 不是地址寄存器的寄存器也可以用来存地址(所谓地址只是16位无符号数而已), 只不过不能用来寄存器寻址、寄存器相对寻址、基址变址寻址、相对基址变址寻址(总之就是用寄存器存地址的寻址方式都不能
    • BP默认段寄存器是SS, 其他默认均为DS

标志位

  • 注意: 标志位设置和指令有关, 当指令要求与一般规律冲突时, 以指令要求为准.
标志名 英文简称 标志表示 1/0 标志位设置的一般规律
溢出 OF OV/NV 正 + 正 得 负 时为1
负 + 负 得 正 时为1
正 - 负 得 负 时为1
负 - 正 得 正 时为1
方向 DF DN/UP
中断 IF EI/DI
符号 SF NG/PL 符号位为1时为1
符号位为0时为0
ZF ZR/NZ 运算结果为0时为1
辅助进位 AF AC/NA
奇偶 PF PE/PO 结果最后一个字节
二进制形式1的个数
为偶数时为1, 反之为0
进位 CF CY/NC 最高位有进位(借位)时为1, 反之为0
  • 标志位的设置总结(每条指令的标志位的设置查看指令速查表):

    • 数据传送类指令除了标志寄存器传送指令不影响标志位

    • INC, DEC不影响CF, 其他标志位的设置按一般规律

    • 加减法指令除了INC, DEC设置标志位均按一般规律

    • 乘法指令如果乘积结果的高一半为0则设置CF=OF=0, 否则设置CF=OF=1. 乘法指令对于CF和OF以外的标志位设置无定义(无定义不意味不影响)

    • 除法指令对所有标志位的设置均无定义

    • NOT指令不影响标志位

    • 除NOT以外的逻辑指令(& | ^ TEST)设置CF=OF=0, AF无定义, 其他标志位的设置按一般规律

    • 移位指令对标志位的设置比较复杂

      • 移位指令都会影响CF, 影响方式如下图

      8086汇编复习手册

      • 移位指令只有当CNT为1时才会设置OF, 高位变换OF=1, 否则OF=0

      • 循环移位指令不影响除CF,OF以外的标志位

      • 循环移位以外的移位指令对AF无定义, 其他标志位的设置按一般规律

    • 串传送 MOVSB / MOVSW不影响条件码。

    • 存串 STOSB / STOSW 不影响条件码。

    • 取串LODSB / LODSW不影响条件码。

    • 串比较 CMPSB / CMPSW 对条件码的影响 : 看书

    • 串扫描 SCASB / SCASW对条件码的影响 : 看书

寻址方式及其使用和注意事项

  • 寻址方式一共七种
    • 立即寻址方式
    • 寄存器寻址方式
    • 直接寻址方式
    • 寄存器间接寻址方式
    • 寄存器相对寻址方式(常用于处理数组)
    • 基址变址寻址方式
    • 相对基址变址寻址方式
寻址方式 示例 注意事项
立即寻址方式 MOV AX, 10H
寄存器寻址方式 MOV AX, BX
直接寻址方式 MOV AX, [2000H]
寄存器间接寻址方式 MOV AX, DS:[BX] only BX BP SI DI
寄存器相对寻址方式 MOV AX, ARR[SI]
MOV AX, [ARR + SI]
only BX BP SI DI
基址变址寻址方式 MOV AX, [BX][DI] 基址寄存器 : BX BP
变址寄存器 : SI DI
相对基址变址寻址方式 MOV AX, ARR[BX][DI]
MOV AX, ARR[BX + DI]
MOV AX, [ARR + BX + DI]
基址寄存器 : BX BP
变址寄存器 : SI DI

8086对段寄存器使用的约定

序号 内存访问类型 默认段寄存器 可重设的段寄存器 段内偏移地址来源
1 取指令 CS IP
2 堆栈操作 SS SP
3 串操作之源串 DS ES、SS SI
4 串操作之目标串 ES DI
5 BP用作基址寻址 SS ES、DS 按寻址方式计算得有效地址
6 一般数据存取 ES ES、SS 按寻址方式计算得有效地址

注: 除BP外其他可用来当地址寄存器的默认段均为DS

8086汇编模板(熟练记忆, 考试要写)

DATA SEGMENT ; 数据段, 对应DS段, 习惯将变(常)量定义于此
DATA ENDS ; ENDS : END of SEGMENT

CODE SEGMENT ; 代码段

ASSUME DS: DATA, CS: CODE ; 声明DATA段是DS段, CODE段是CS段

;; 一般在这里定义函数
;; 示例:
;;;;	FUNC PROC ; FUNC是函数名
;;;;	;; 函数代码
;;;; 	RET ; return
;;;;	FUNC ENDP

START: ; 程序从这里开始执行

	; 手动设置DS的值
    MOV AX, DATA
    MOV DS, AX
    
    ; 写自己的代码
    ;

	; 调用DOS中断返回DOS
    MOV AH, 4CH
    INT 21H
CODE ENDS
	END START

8086汇编指令速查表

指令 作用 对标志位的影响 备注
MOV MOV 1, 2
将2的内容移到1
不影响 不能直接MOV立即数到段寄存器
不能修改CS寄存器
两个操作数不能都是内存(CMP, XCHG也是)
PUSH 入栈 不影响 只能操作字; 不能搞立即数
POP 出栈 不影响 只能操作字; 不能搞立即数
XCHG 交换两个的值 不影响 两个操作数不能都是内存
两个操作数必须有一个在寄存器中
操作数不能使用段寄存器
两个操作数类型要匹配
IN 输入 不影响 仅限于AX,AL传送信息
用法:
IN AL, PORT(字节)
IN AX, PORT(字节)
IN AL, DX(字节)
IN AX, DX(字)
OUT 输出 不影响 仅限于AX,AL传送信息
用法:
OUT PORT, AL(字节)
OUT PORT, AX(字节)
OUT DX, A(字节)L
OUT DX, AX(字)
XLAT 换码指令 不影响
LEA 有效地址送寄存器 不影响 目的操作数可使用16/32位寄存器, 不能使用段寄存器

操作数长度, 地址长度, 操作
16, 16, 正常送
16, 32, 截取低位
32, 16, 零扩展到32位
32, 32, 正常送
CBW 字节扩展到字 不影响 AL -(有符号扩展)-> AX
CWD 字扩展到双字 不影响 AX -(有符号扩展)-> DX, AX
ADD 加法 一般规律 两个操作数不能同时是内存
ADC 带进位加法 一般规律 两个操作数不能同时是内存
INC 加1 不影响CF, 其他一般
SUB 减法 一般规律 两个操作数不能同时是内存
SBB 带借位减法 一般规律 两个操作数不能同时是内存
DEC 减1 一般规律
NEG 求补 不影响CF, 其他一般
CMP 比较 一般规律 两个操作数不能同时是内存
没有结果, 仅设置标志位
MUL/IMUL 乘法 乘积结果的高一半为0设置CF=OF=0, 否则设置CF=OF=1. 对于CF和OF以外的标志位无定义 操作数不能是立即数、段寄存器
内存操作数指定是WORD PTR 还是BYTE PTR
DIV/IDIV 除法 无定义 操作数不能是立即数、段寄存器
内存操作数指定是WORD PTR 还是BYTE PTR
AND 位与 设置CF=OF=0, AF无定义, 其他标志位的设置按一般规律
OR 位或 设置CF=OF=0, AF无定义, 其他标志位的设置按一般规律
NOT 按位求反 不影响
XOR 位异或 设置CF=OF=0, AF无定义, 其他标志位的设置按一般规律
TEST 测试 设置CF=OF=0, AF无定义, 其他标志位的设置按一般规律
SHL/SHR 逻辑左右移 见标志位设置总结 所移位数要么为1, 要么为CL
SAL/SAR 算术左右移 见标志位设置总结 所移位数要么为1, 要么为CL
ROL/ROR 循环左右移 见标志位设置总结 所移位数要么为1, 要么为CL
RCL/RCR 循环带借位左右移 见标志位设置总结 所移位数要么为1, 要么为CL
MOVS 串转移 不影响
CMPS 串比较 见标志位设置总结
LODS 从串取 不影响
STOS 存入串 不影响
SCANS 扫描串 见标志位设置总结
INS 串输入 不影响
OUTS 串输出 不影响
JMP 无条件跳转 不影响
LOOP 循环 不影响
LOOPZ/LOOPE 循环 不影响
LOOPNZ/LOOPNE 循环 不影响
J__ 有条件跳转 不影响 三类:
标志位类JZ, JS, JO, JC
无符号JA, JB
有符号JG, JL
(‘不’ : J后加N, ‘等于’ : 后面跟E)
CALL 子程序调用 不影响
RET 子程序返回 不影响

常用DOS调用

  • 1号调用

    MOV AH, 01H
    INT 21H
    ; 控制台等待输入, 输入字符的ASCII存在AL中
    
  • 2号调用

    MOV AH, 02H
    MOV DL, 'A'
    INT 21H ; 控制台输出一个字符, 其ASCII码在DL中
    
  • 9号调用

    ; 输出字符串, 以'$'为结尾标志, 字符串起始地址需要存在DX中
    MOV AH, 09H
    LEA DX, STRING ; STRING DB "Hello, World!$"
    INT 21H
    
  • 10号调用

    ; 输入字符串, 要求较复杂
    ; 定义缓冲区 : BUF DB 80, ?, 80 DUP('$')
    ; 规则 : 调用前将BUF的地址传入DX, 调用后实际字符串存到了BUF的第三个字节开始之后
    ;        最多输入字符的个数有BUF的第一个字节指定, 这也意味着10号调用一次最多可读入255个
    ;        输入字符串实际大小将被存放在BUF的第2个字节
    MOV AH, 0AH
    LEA DX, BUF
    INT 21H
    
  • 4CH号调用

    ; 返回DOS, 程序结束应调用它, 否则会出运行时错误
    MOV AH, 4CH
    INT 21H      ;调用4CH号功能,结束程序
    

汇编子程序设计

  • 函数定义

    函数名 PROC
    	; 函数体
    	RET ; 函数一定有RET语句作为返回调用处的出口
    函数名 ENDP
    
  • 函数调用

    CALL 函数名
    
  • 函数参数和返回值

    ; 汇编函数没有所谓参数和返回值
    ;; 作为参数功能的寄存器, 即利用全局变量传递数据
    ; 例子:
    
    ; MOD函数, 参数为AX, BL, 返回值为DL, 为AX % BL的值, 均为无符号数
    MOD PROC
    	DIV BL
    	MOV DL, AH
    MOD ENDP
    
  • 避免寄存器被破坏

    ; 函数内可能会用到除了用作参数和返回值的寄存器, 
    ; 为了避免调用方的数据被破坏, 应该在函数体内对适当的寄存器进行保护, 利用PUSH,POP
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;
    ; function : print \r\n ; ; 打印回车换行
    ;;;;;;;;;;;;;;;;;;;;;;;;;
    FUNC_PRINT_CRLF PROC
        PUSH DX ; 保护DX, AX
        PUSH AX
    
        ; print \r\n
        MOV DL, 0DH ; '\r'
        MOV AH, 02H
        INT 21H
        
        MOV DL, 0AH ; '\n'
        MOV AH, 02H
        INT 21H
    
        POP AX ; 恢复DX, AX
        POP DX
    
        ; return
        RET
    FUNC_PRINT_CRLF ENDP
    

汇编常用技法

  • 灵活使用栈(PUSH & POP)

    • 暂存(保护)寄存器的值
    • 逆序数组
    • 加个特殊标记当栈底(我常用-1(0FFFFH)做栈底)
  • 字符串加个特殊标记当结尾(常用’$’)

汇编编程实战

输入输出

0.输入以回车结束的字符串

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function : input a string into addr-BX, ; 
;            whose length must be less    ;
;            than CX                      ;
; params : BX, addr; CX, max-length       ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FUNC_INPUT_STRING_TO_BX PROC
	PUSH AX
	PUSH SI
	
    MOV SI, 0
FISTB_BEGIN:
    ; 如果长度大于CX, 直接结束
    CMP SI, CX
    JE FISTB_END2
    
    ; 从键盘获取一个ASCII, 存放在AL
    MOV AH, 1
    INT 21H
    CMP AL, 0DH
    JE FISTB_END ; 遇到回车直接跳出
    
    MOV [BX][SI], AL ; 存放到..
    INC SI
    
    JMP FISTB_BEGIN
FISTB_END:
    ; End of loop of inputing & saving
    MOV AL, '$'
	MOV [BX][SI], AL ; 字符串以'$'结尾
FISTB_END2:

	POP SI
	POP AX
	RET
FUNC_INPUT_STRING_TO_BX ENDP

1.输出有结束标志的字符串

结尾为 ‘$’

; 结尾 : '$'

LEA DX, 字符串名
MOV AH, 09H
INT 21H

2.输出N进制数

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function : print CX base 10 ; ; 打印CX的值以10进制无符号
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FUNC_PRINT_CS_BASE10 PROC
    PUSH AX
    PUSH DX

    MOV AX, CX
    MOV DX, -1
    PUSH DX ; -1为栈底标志

    ; 除10取余获取各位数字并压栈
FUNC_PRINT_CS_BASE10_GET_NUMBER_BIT_LOOP_BEGIN:
    
    CBW ; 将上一步的商扩展到字以作为这一步的被除数
    
    MOV DL, 10
    DIV DL
    PUSH AX

    CMP AL, 0
    JNE FUNC_PRINT_CS_BASE10_GET_NUMBER_BIT_LOOP_BEGIN

    ; 出栈并打印
FUNC_PRINT_CS_BASE10_PRINT_BIT_BY_BIT_BEGIN:
    POP AX
    CMP AX, -1
    JE FUNC_PRINT_CS_BASE10_PRINT_BIT_BY_BIT_END

    ; 打印一位数字
    ADD AH, 30H ; bitNumber + '0'
    MOV DL, AH

    ;;;;;;;;;;;;; ; 打印DL
    MOV AH, 02H
    INT 21H
    ;;;;;;;;;;;;;

    JMP FUNC_PRINT_CS_BASE10_PRINT_BIT_BY_BIT_BEGIN
FUNC_PRINT_CS_BASE10_PRINT_BIT_BY_BIT_END:

    POP DX
    POP AX
    RET
FUNC_PRINT_CS_BASE10 ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function : print CX base 16 ; ; 打印CX的值以16进制无符号
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FUNC_PRINT_CS_BASE16 PROC
    PUSH AX
    PUSH DX

    MOV AX, CX
    MOV DX, -1
    PUSH DX ; -1为栈底标志

    ; 除10取余获取各位数字并压栈
FUNC_PRINT_CS_BASE16_GET_NUMBER_BIT_LOOP_BEGIN:
    
    CBW ; 将上一步的商扩展到字以作为这一步的被除数
    
    MOV DL, 16
    DIV DL
    PUSH AX

    CMP AL, 0
    JNE FUNC_PRINT_CS_BASE16_GET_NUMBER_BIT_LOOP_BEGIN

    ; 出栈并打印
FUNC_PRINT_CS_BASE16_PRINT_BIT_BY_BIT_BEGIN:
    POP AX
    CMP AX, -1
    JE FUNC_PRINT_CS_BASE16_PRINT_BIT_BY_BIT_END

    ; 打印一位数字
    CMP AH, 9
    JNA FUNC_PRINT_CS_BASE16_PRINT_BIT_LESS10
    ADD AH, 7
FUNC_PRINT_CS_BASE16_PRINT_BIT_LESS10:
    ADD AH, 30H ; bitNumber + '0'
    MOV DL, AH

    ;;;;;;;;;;;;; ; 打印DL
    MOV AH, 02H
    INT 21H
    ;;;;;;;;;;;;;

    JMP FUNC_PRINT_CS_BASE16_PRINT_BIT_BY_BIT_BEGIN
FUNC_PRINT_CS_BASE16_PRINT_BIT_BY_BIT_END:

    POP DX
    POP AX
    RET
FUNC_PRINT_CS_BASE16 ENDP

;#######################################################################;
; 打印16位无符号数(比上面的更好, 上面的打印比较大的数会出BUG)
DATA SEGMENT
    VAL DW 65535
DATA ENDS

STACK SEGMENT
STACK ENDS

CODE SEGMENT
ASSUME DS: DATA, CS: CODE, SS: STACK
START:
    MOV AX, DATA
    MOV DS, AX

    MOV AX, VAL
    MOV DX, -1
    PUSH DX
GET_NUMBER_BIT_LOOP_BEGIN:
    MOV DX, 0

    MOV BX, 10
    DIV BX
    PUSH DX

    CMP AX, 0
    JNE GET_NUMBER_BIT_LOOP_BEGIN

PRINT_LOOP_BEGIN:
    POP DX
    CMP DX, -1
    JE PRINT_LOOP_END

    ADD DL, 30H
    MOV AH, 02H
    INT 21H

    JMP PRINT_LOOP_BEGIN
PRINT_LOOP_END:

    MOV AX, 4C00H
    INT 21H

CODE ENDS
	END START

3.输入N进制数

; 10进制为例
; 其他进制注意除数, 字母的处理(A-F)
	MOV BX, 0
INPUT_BEGIN:
	MOV AH, 01H
	INT 21H              ; 输入字符到AL
	
	CMP AL, 0DH
	JE INPUT_END         ; 回车, 结束
	
	SUB AL, 30H          ; AL <- AL - '0', 获取字符对应的数字(0-9)
	PUSH AX              ; 保护AX
	MOV AX, 10
	MUL BX               ; AX <- BX * 10
	MOV BX, AX           ; 将乘法结果存回BX, 即相当于 : BX <- BX * 10
	POP AX               ; 恢复AX
	CBW                  ; 扩展AL -> AX, 以便于将AL(最新输入的数位)累加到BX
	
	ADD BX, AX           ; 此条及以上 : BX <- BX * 10 + AL
	JMP INPUT_BEGIN      ; next-loop
INPUT_END:

数组

0.找出数组中的最大值

新增代码示例

DATA SEGMENT ; 数据段, 对应DS段, 习惯将变(常)量定义于此
	ARR DW 45H, 32H, 12H, 54H, 2H, 23H, 12H, 11H ; 数组
	ARR_LEN DW 8 ; 数组大小
DATA ENDS ; ENDS : END of SEGMENT

CODE SEGMENT ; 代码段
	ASSUME DS: DATA, CS: CODE ; 声明DATA段是DS段, CODE段是CS段
START: ; 程序从这里开始执行
    MOV AX, DATA
    MOV DS, AX

	; [1]写自己的代码, 要求找出ARR数组中的最大值, 将结果存在AX中
	MOV AX, ARR[0]
	MOV SI, 2
LOOP_BEGIN:
	CMP SI, ARR_LEN
	JAE LOOP_END
	
	CMP AX, ARR[SI]
	JAE NEXT_LOOP
	
	MOV AX, ARR[SI]

NEXT_LOOP:
	ADD SI, 2
	JMP LOOP_BEGIN
	
LOOP_END:
	
	; 调用DOS中断返回DOS
	MOV AH, 4CH
	INT 21H
CODE ENDS
	END START

1.插入元素到升序序列

新增代码示例

DATA SEGMENT ; 数据段, 对应DS段, 习惯将变(常)量定义于此
		ARR DW 10H, 20H, 30H, 43H, ? ; 数组
DATA ENDS ; ENDS : END of SEGMENT

CODE SEGMENT ; 代码段
	ASSUME DS: DATA, CS: CODE ; 声明DATA段是DS段, CODE段是CS段
START: ; 程序从这里开始执行
    MOV AX, DATA
    MOV DS, AX

	MOV CX, 90H
	; [2]写自己的代码, 要求将CX中的数字插入ARR中, 使得ARR仍然保持升序
	MOV SI, 8
LOOP_BEGIN:
	CMP CX, ARR[SI - 2]
	JA LOOP_END
	MOV DX, ARR[SI - 2]
	MOV ARR[SI], DX
	SUB SI, 2
	CMP SI, 0
	JE LOOP_END
	JMP LOOP_BEGIN
	
LOOP_END:
	MOV ARR[SI], CX

	; 调用DOS中断返回DOS
	MOV AH, 4CH
	INT 21H
CODE ENDS
	END START

2.排序数组(冒泡排序)

DATA SEGMENT
    ARR DW 45H, 32H, 12H, 54H, 2H, 23H, 12H, 11H
    ARR_LEN DW 8
DATA ENDS

CODE SEGMENT
    ASSUME DS: DATA, CS: CODE
START:
    MOV AX, DATA
    MOV DS, AX

    ; 将ARR升序排列
    MOV CX, ARR_LEN ; 数组大小为8 ; 外循环采用CX计数
    DEC CX
    MOV DX, CX ; 内循环采用DX进行计数
OUTER_LOOP_BEGIN:

    MOV SI, 0
    MOV DX, ARR_LEN
    DEC DX
INNER_LOOP_BEGIN:

    ; if arr[SI] > arr[SI + 2] : swap
    MOV BX, ARR[SI]
    CMP BX, ARR[SI + 2]
    JNA INNER_LOOP_CONTINUE ; else continue

    XCHG BX, ARR[SI + 2] ; swap
    MOV ARR[SI], BX
INNER_LOOP_CONTINUE:

    ADD SI, 2 ; 数组元素为DW, 注意SI每次加2
    DEC DX
    CMP DX, 0
    JNZ INNER_LOOP_BEGIN
    LOOP OUTER_LOOP_BEGIN

    MOV AH, 4CH
    INT 21H
CODE ENDS
    END START

字符串

0.比较字符串是否相等

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; function : compare string a and string b ; 
;            compare-max-len is in CX      ;
; params : CX, maxlen;                     ; ; 比较两个字符串,
;          AX, a's addr;                   ; ; 它们的起始地址在AX, BX里
;          BX, b's addr                    ; ; 要求字符串均已'$'结尾
; return : DL, 0 if ==, else 1             ; ; CX被用来指定比较最大长度
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FUNC_CMP_STRING_A_AND_B PROC
	PUSH BP
	PUSH SI
	PUSH DX

	MOV BP, AX
	MOV SI, 0
FCSAAB_LOOP_BEGIN:

	MOV DH, DS:[BX][SI]
	CMP DS:[BP][SI], DH ; CMP不可直接比较两块内存
	JNE FCSAAB_LOOP_END_NE 

	CMP DH, '$'
	JE FCSAAB_LOOP_END_E

	INC SI
	LOOP FCSAAB_LOOP_BEGIN
FCSAAB_LOOP_END_E:
	POP DX
	MOV DL, 0
	POP SI
	POP BP
	RET
FCSAAB_LOOP_END_NE:
	POP DX
	MOV DL, 1
	POP SI
	POP BP
	RET
FUNC_CMP_STRING_A_AND_B ENDP

1.翻转字符串(同样适用于数组)

DATA SEGMENT
    S DB "Hello World!!"
    S_LEN EQU 13
DATA ENDS

CODE SEGMENT
    ASSUME DS: DATA, CS: CODE
START:
    MOV AX, DATA
    MOV DS, AX

    ; 思路, 两个下标分别从头向后和从尾向前, 每次循环将两者交换, if SI >= DI : break
    MOV SI, 0 ; 从头向后的下标
    MOV DI, S_LEN - 1 ; 从尾向前的下标

REVERSE_LOOP_BEGIN:
    
    ; swap
    MOV CL, S[SI] ; 注意数据类型匹配
    XCHG CL, S[DI]
    MOV S[SI], CL

    INC SI
    DEC DI
    CMP SI, DI
    JAE REVERSE_LOOP_END ; if SI >= DI : break
    JMP REVERSE_LOOP_BEGIN ; else continue
REVERSE_LOOP_END:

    MOV AH, 4CH
    INT 21H
CODE ENDS
    END START

综合示例

0.输入字符串找出出现数量最多的字符输出该字符及其数量

新增代码示例

(见期末模拟(一), 参考程序随答案公布)

  • 输入格式 : 输入一行字符串, 以回车结尾, 输入字符个数不会多于200个
  • 输出格式 : 输出两行, 每一行均以回车换行结尾, 第一行为出现最多的字符, 第二行为出现的次数
  • (最多出现的字符由多个时, 随意输出一个即可)
  • 思路 : 内外两层循环,当外循环找到一个ASCII不为0的字符时进入内循环否则一直向后找,内循环统计和外循环找到的不为0的字符相同的字符个数,最后加上外循环找到的那一个,和CNT变量比大小,若大于CNT则更新CNT为新的计数,更新CHAR为新的最多出字符,为了避免重复,统计完即刻将当前位置字符赋为ASCII 0,将外循环那个字符也赋成ASCII 0
DATA SEGMENT ; 数据段, 对应DS段, 习惯将变(常)量定义于此
    CNT DB 0
    CHAR DB 0
    HELPER DB 200, ?
    STR DB 200 DUP('$')
DATA ENDS ; ENDS : END of SEGMENT

CODE SEGMENT ; 代码段
	ASSUME DS: DATA, CS: CODE ; 声明DATA段是DS段, CODE段是CS段
START: ; 程序从这里开始执行
    MOV AX, DATA
    MOV DS, AX

    LEA DX, HELPER
    MOV AH, 0AH
    INT 21H

    MOV SI, 0
OUTER_LOOP:
    MOV AL, HELPER[1]
    MOV AH, 0
    CMP SI, AX
    JAE OUTER_LOOP_END

    MOV DL, STR[SI]
    CMP DL, 0

    JNE OUTER_NOT_0
    INC SI
    JMP OUTER_LOOP

OUTER_NOT_0:
    MOV CL, 1
    MOV DI, SI
    INC DI

INNER_LOOP:
    MOV AL, HELPER[1]
    MOV AH, 0
    CMP DI, AX
    JAE OUTER_NEXT_LOOP
    
    MOV DL, STR[SI]
    CMP DL, STR[DI]
    JNE INNER_NEXT_LOOP

    INC CL
    MOV STR[DI], 0

INNER_NEXT_LOOP:
    INC DI
    JMP INNER_LOOP

OUTER_NEXT_LOOP:
    CMP CL, CNT
    JBE OUTER_NEXT_LOOP2

    MOV CNT, CL
    MOV CHAR, DL

OUTER_NEXT_LOOP2:
    MOV STR[SI], 0
    INC SI
    JMP OUTER_LOOP

OUTER_LOOP_END:
    MOV DL, CHAR
    MOV AH, 02H
    INT 21H

    MOV AL, CNT
    MOV AH, 0

    ; 以十进制打印AX
    MOV DX, -1
    PUSH DX ; -1为栈底标志

    ; 除10取余获取各位数字并压栈
GET_NUMBER_BIT_LOOP_BEGIN:
    
    MOV AH, 0 ; 将上一步的商扩展到字以作为这一步的被除数
    
    MOV DL, 10
    DIV DL
    PUSH AX

    CMP AL, 0
    JNE GET_NUMBER_BIT_LOOP_BEGIN

    ; 出栈并打印
PRINT_BIT_BY_BIT_BEGIN:
    POP AX
    CMP AX, -1
    JE PRINT_BIT_BY_BIT_END

    ; 打印一位数字
    ADD AH, 30H ; bitNumber + '0'
    MOV DL, AH

    MOV AH, 02H
    INT 21H

    JMP PRINT_BIT_BY_BIT_BEGIN
PRINT_BIT_BY_BIT_END:

    MOV AH, 4CH
    INT 21H
CODE ENDS
	END START

1.输入若干数字求平均值, 最大值, 最小值并输出

  • 思路
    • 边输入边求和并计数以及记录最大最小值
    • 输出

2.输入字符串反向输出

  • 格式 : 输入一行字符串, 要求倒序输出, 末尾要求打印回车换行
  • 思路
    • 边输入边压栈
    • 边弹栈边输出
DATA SEGMENT
DATA ENDS

CODE SEGMENT
    ASSUME DS: DATA, CS: CODE
START:
    MOV AX, DATA
    MOV DS, AX

    MOV DX, -1
    PUSh DX ; 以0FFFFH为栈底标志
    
INPUT_LOOP_BEGIN: ; 边输入边压栈
    
    MOV AH, 01H
    INT 21H

    CMP AL, 0DH
    JE INPUT_LOOP_END

    PUSH AX ; 入栈
    JMP INPUT_LOOP_BEGIN
INPUT_LOOP_END:

OUTPUT_LOOP_BEGIN: ; 边弹栈边输出
    POP DX
    CMP DX, -1
    JE OUTPUT_LOOP_END

    MOV AH, 02H
    INT 21H ; print DL
    JMP OUTPUT_LOOP_BEGIN
OUTPUT_LOOP_END:

    ; print \r\n
    MOV DL, 0DH ; '\r'
    MOV AH, 02H
    INT 21H
    
    MOV DL, 0AH ; '\n'
    MOV AH, 02H
    INT 21H

    MOV AH, 4CH
    INT 21H
CODE ENDS
    END START

3.输入N求斐波那契数列第N项并输出

  • 思路 : 三个变量(寄存器)来回倒腾

4.判断输入括号形式是否合法[拓展, 栈的应用]

附录

流程图速学

  • 开始/结束 : 圆边矩形

  • 执行语句 : 矩形

  • 条件 : 菱形

    8086汇编复习手册

ASCII码表

  • 考试要用单步给出, 注意记住一些特殊的字符的ASCII

    • 回车 : 0DH
    • 换行 : 0AH
    • ‘A’ : 41H
    • ‘a’ : 61H
    • ‘0’ : 30H

    (其实除了回车换行其他的记不住也没啥事, 程序中直接写其字符形式即可, 比如将0-9数字转换成字符, 直接 + 30H也可以直接+‘0’, 回车换行必须要记住, 因为汇编编译器不支持转义字符(’\n’这种没意义))

DEC HEX 缩写/符号 描述
0 00 NUL Null char (空字符)
1 01 SOH Start of Heading (标题开始)
2 02 STX Start of Text (正文开始)
3 03 ETX End of Text (正文结束)
4 04 EOT End of Transmission (传输结束)
5 05 ENQ Enquiry (请求)
6 06 ACK Acknowledgment (收到通知)
7 07 BEL Bell (响铃)
8 08 BS Back Space (退格)
9 09 HT Horizontal Tab (水平制表符)
10 0A LF Line Feed (换行键)
11 0B VT Vertical Tab (垂直制表符)
12 0C FF Form Feed (换页键)
13 0D CR Carriage Return (回车键)
14 0E SO Shift Out / X-On (不用切换)
15 0F SI Shift In / X-Off (启用切换)
16 10 DLE Data Line Escape (数据链路转义)
17 11 DC1 Device Control 1 (设备控制1)
18 12 DC2 Device Control 2 (设备控制2)
19 13 DC3 Device Control 3 (设备控制3)
20 14 DC4 Device Control 4 (设备控制4)
21 15 NAK Negative Acknowledgement (拒绝接收)
22 16 SYN Synchronous Idle (同步空闲)
23 17 ETB End of Transmit Block (传输块结束)
24 18 CAN Cancel (取消)
25 19 EM End of Medium (介质中断)
26 1A SUB Substitute (替补)
27 1B ESC Escape (溢出)
28 1C FS File Separator (文件分割符)
29 1D GS Group Separator (分组符)
30 1E RS Record Separator (记录分离符)
31 1F US Unit Separator (单元分隔符)
32 20 Space (空格)
33 21 ! Exclamation mark
34 22 " Double quotes
35 23 # Number
36 24 $ Dollar
37 25 % Procenttecken
38 26 & Ampersand
39 27 Single quote
40 28 ( Open parenthesis
41 29 ) Close parenthesis
42 2A * Asterisk
43 2B + Plus
44 2C , Comma
45 2D - Hyphen
46 2E . Period, dot or full stop
47 2F / Slash or divide
48 30 0 Zero
49 31 1 One
50 32 2 Two
51 33 3 Three
52 34 4 Four
53 35 5 Five
54 36 6 Six
55 37 7 Seven
56 38 8 Eight
57 39 9 Nine
58 3A : Colon
59 3B ; Semicolon
60 3C < Less than
61 3D = Equals
62 3E > Greater than
63 3F ? Question mark
64 40 @ At symbol
65 41 A Uppercase A
66 42 B Uppercase B
67 43 C Uppercase C
68 44 D Uppercase D
69 45 E Uppercase E
70 46 F Uppercase F
71 47 G Uppercase G
72 48 H Uppercase H
73 49 I Uppercase I
74 4A J Uppercase J
75 4B K Uppercase K
76 4C L Uppercase L
77 4D M Uppercase M
78 4E N Uppercase N
79 4F O Uppercase O
80 50 P Uppercase P
81 51 Q Uppercase Q
82 52 R Uppercase R
83 53 S Uppercase S
84 54 T Uppercase T
85 55 U Uppercase U
86 56 V Uppercase V
87 57 W Uppercase W
88 58 X Uppercase X
89 59 Y Uppercase Y
90 5A Z Uppercase Z
91 5B [ Opening bracket
92 5C \ Backslash
93 5D ] Closing bracket
94 5E ^ Caret - circumflex
95 5F _ Underscore
96 60 ` Grave accent
97 61 a Lowercase a
98 62 b Lowercase b
99 63 c Lowercase c
100 64 d Lowercase d
101 65 e Lowercase e
102 66 f Lowercase f
103 67 g Lowercase g
104 68 h Lowercase h
105 69 i Lowercase i
106 6A j Lowercase j
107 6B k Lowercase k
108 6C l Lowercase l
109 6D m Lowercase m
110 6E n Lowercase n
111 6F o Lowercase o
112 70 p Lowercase p
113 71 q Lowercase q
114 72 r Lowercase r
115 73 s Lowercase s
116 74 t Lowercase t
117 75 u Lowercase u
118 76 v Lowercase v
119 77 w Lowercase w
120 78 x Lowercase x
121 79 y Lowercase y
122 7A z Lowercase z
123 7B { Opening brace
124 7C | Vertical bar
125 7D } Closing brace
126 7E ~ Equivalency sign (tilde)
127 7F Delete
上一篇:CF1554E You


下一篇:MFC知识点总结