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标志位设置的一般规律
溢出OFOV/NV正 + 正 得 负 时为1
负 + 负 得 正 时为1
正 - 负 得 负 时为1
负 - 正 得 正 时为1
方向DFDN/UP
中断IFEI/DI
符号SFNG/PL符号位为1时为1
符号位为0时为0
ZFZR/NZ运算结果为0时为1
辅助进位AFAC/NA
奇偶PFPE/PO结果最后一个字节
二进制形式1的个数
为偶数时为1, 反之为0
进位CFCY/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, 影响方式如下图

      在这里插入图片描述

      • 移位指令只有当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取指令CSIP
2堆栈操作SSSP
3串操作之源串DSES、SSSI
4串操作之目标串ESDI
5BP用作基址寻址SSES、DS按寻址方式计算得有效地址
6一般数据存取ESES、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汇编指令速查表

指令作用对标志位的影响备注
MOVMOV 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.判断输入括号形式是否合法[拓展, 栈的应用]

附录

流程图速学

  • 开始/结束 : 圆边矩形

  • 执行语句 : 矩形

  • 条件 : 菱形

    在这里插入图片描述

ASCII码表

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

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

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

DECHEX缩写/符号描述
000NULNull char (空字符)
101SOHStart of Heading (标题开始)
202STXStart of Text (正文开始)
303ETXEnd of Text (正文结束)
404EOTEnd of Transmission (传输结束)
505ENQEnquiry (请求)
606ACKAcknowledgment (收到通知)
707BELBell (响铃)
808BSBack Space (退格)
909HTHorizontal Tab (水平制表符)
100ALFLine Feed (换行键)
110BVTVertical Tab (垂直制表符)
120CFFForm Feed (换页键)
130DCRCarriage Return (回车键)
140ESOShift Out / X-On (不用切换)
150FSIShift In / X-Off (启用切换)
1610DLEData Line Escape (数据链路转义)
1711DC1Device Control 1 (设备控制1)
1812DC2Device Control 2 (设备控制2)
1913DC3Device Control 3 (设备控制3)
2014DC4Device Control 4 (设备控制4)
2115NAKNegative Acknowledgement (拒绝接收)
2216SYNSynchronous Idle (同步空闲)
2317ETBEnd of Transmit Block (传输块结束)
2418CANCancel (取消)
2519EMEnd of Medium (介质中断)
261ASUBSubstitute (替补)
271BESCEscape (溢出)
281CFSFile Separator (文件分割符)
291DGSGroup Separator (分组符)
301ERSRecord Separator (记录分离符)
311FUSUnit Separator (单元分隔符)
3220Space (空格)
3321!Exclamation mark
3422"Double quotes
3523#Number
3624$Dollar
3725%Procenttecken
3826&Ampersand
3927Single quote
4028(Open parenthesis
4129)Close parenthesis
422A*Asterisk
432B+Plus
442C,Comma
452D-Hyphen
462E.Period, dot or full stop
472F/Slash or divide
48300Zero
49311One
50322Two
51333Three
52344Four
53355Five
54366Six
55377Seven
56388Eight
57399Nine
583A:Colon
593B;Semicolon
603C<Less than
613D=Equals
623E>Greater than
633F?Question mark
6440@At symbol
6541AUppercase A
6642BUppercase B
6743CUppercase C
6844DUppercase D
6945EUppercase E
7046FUppercase F
7147GUppercase G
7248HUppercase H
7349IUppercase I
744AJUppercase J
754BKUppercase K
764CLUppercase L
774DMUppercase M
784ENUppercase N
794FOUppercase O
8050PUppercase P
8151QUppercase Q
8252RUppercase R
8353SUppercase S
8454TUppercase T
8555UUppercase U
8656VUppercase V
8757WUppercase W
8858XUppercase X
8959YUppercase Y
905AZUppercase Z
915B[Opening bracket
925C\Backslash
935D]Closing bracket
945E^Caret - circumflex
955F_Underscore
9660`Grave accent
9761aLowercase a
9862bLowercase b
9963cLowercase c
10064dLowercase d
10165eLowercase e
10266fLowercase f
10367gLowercase g
10468hLowercase h
10569iLowercase i
1066AjLowercase j
1076BkLowercase k
1086ClLowercase l
1096DmLowercase m
1106EnLowercase n
1116FoLowercase o
11270pLowercase p
11371qLowercase q
11472rLowercase r
11573sLowercase s
11674tLowercase t
11775uLowercase u
11876vLowercase v
11977wLowercase w
12078xLowercase x
12179yLowercase y
1227AzLowercase z
1237B{Opening brace
1247C|Vertical bar
1257D}Closing brace
1267E~Equivalency sign (tilde)
1277FDelete
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值