汇编:字符串的输入

程序的处理思路:

1、调用int 16h读取键盘输入;

2、如果不是字符:

2.1 如果是退格键,从字符栈中弹出一个字符,显示字符栈中所有字符;继续执行"读取键盘输入"。

2.2 如果是Enter键,向字符栈中压入0,返回。

3、如果是字符键:字符入栈,显示字符栈中的所有字符,继续执行"读取键盘输入"。

复制代码
assume cs:code ,ds:data
;定义栈空间
data segment
	db 32 dup(?)
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0
	mov dh,12		; 显示行:第12行
	mov dl,20		; 显示列:第20列
	call getstr
return:
	mov ax,4c00h
	int 21h
	
getstr:
	push ax

getstrs:
	mov ah,0
	int 16h   ;阻塞,等等键盘输入
	
	cmp al,20h				; 判读是否为可显示字符(ASCII≥20H)
	jb nochar				; 非可显示字符,跳转到nochar处理
	;字符al入栈
	mov ah,0
	call charstack
	;显示栈中的字符
	mov ah,2
	call charstack
	
	jmp getstrs
	
;处理非字符
nochar:
	cmp ah,0eh		;退格键的扫描码
	je backspace
	cmp ah,1ch		;回车键的扫描码
	je enter
	jmp getstrs
	
;对退格键、回车键的处理
backspace:
	;字符出栈
	mov ah,1
	call charstack
	;显示栈中的字符
	mov ah,2
	call charstack
	
	jmp getstrs
	
enter:
	mov al,0
	;0字符入栈
	mov ah,0
	call charstack
	;显示栈中的字符
	mov ah,2
	call charstack
	
	pop ax
	ret 		;getstr结束
	
;功能子程序实现
charstack:
	jmp short charstart
	table dw charpush ,charpop,charshow		; 功能跳转表(ah=0/1/2对应入栈/出栈/显示)
	top dw 0     						; 栈顶指针(初始=0,记录栈中字符数)	记录栈中有多少个字符的计数器
charstart:
	push bx
	push dx
	push di
	push es
	
	; 功能分发:ah=0(入栈)/1(出栈)/2(显示)
	cmp ah,2
	ja sret			; ah>2,直接退出
	mov bl,ah
	mov bh,0
	add bx,bx				; bx=ah*2(dw占2字节,计算跳转表偏移)
	jmp word ptr table[bx]		; 跳转到对应功能
	
charpush:
	mov bx,top 
	mov [si][bx],al			;AL是键盘读入的字符的ASCII码
	inc top
	jmp sret
	
charpop:
	cmp top,0
	je sret
	dec top
	mov bx,top
	mov al,[si][bx]
	jmp sret
charshow:
	;在dh行dl列显示
	mov bx,0B800H
	mov es,bx
	mov al,160
	mov ah,0
	mul dh			;在代码第12行,dh=12			;此处语法默认mul dh = ax = al*dh
	mov di,ax		;示例:DH=12 → AX=160×12=1920(即 0780H),表示第 12 行的起始偏移是 1920 字节
					;将行偏移存入 DI,作为显存地址的 "行基准"。本例为第12行的首地址
					
	add dl,dl		;代码第13行,显示dl=20,dl+dl=40
	mov dh,0		;将 DX 的高 8 位清零,确保 DX 仅为列偏移的结果(避免 DH 原有值干扰);
	add di,dx		;这是加法计算,相当于在di=12行的基础上,再加40,即1920+40=1960字节,也就是第 12 行第 20 列的起始位置
	
	mov bx,0
	
charshows:
	cmp bx,top 
	jne noempty					;当 bx ≠ top 时,跳转到 noempty 标签(继续显示栈中字符)。
	mov byte ptr es:[di],' '
	jmp sret
	
noempty:
	mov al,[si][bx]
	mov es:[di],al
	mov byte ptr es:[di+2],' '
	inc bx
	add di,2
	jmp charshows
	
sret:
	pop es
	pop di
	pop dx
	pop bx
	ret
	
code ends
end start

关键知识点:

一、为什么是20H?

在汇编代码中 cmp al, 20h 是判断输入字符是否为可显示的 ASCII 字符 ,核心原因是:20H(十进制 32)是 ASCII 表中第一个 "可显示字符(空格)" 的编码,而小于20H的 ASCII 码均为不可显示的控制字符

1、ASCII 码的分类(核心依据)

ASCII 码分为两类关键区间,这是判断的核心逻辑:

ASCII 范围 十进制 类型 示例
0~31 0~20H 控制字符(不可显示) 回车 (0DH)、换行 (0AH)、退格 (08H)、ESC (1BH) 等
32~127 20H~7FH 可显示字符 空格 (20H)、数字 (30H~39H)、字母 (41H~5AH/61H~7AH)、符号等

二、为什么使用al,20h中的AL呢?

明确 int 16h (ah=0) 的返回值分工

调用 BIOS 键盘中断 int 16h (ah=0) 后,CPU 会把键盘输入的两类关键信息存入 AX 寄存器,分工非常清晰:

寄存器 存储内容 核心作用 示例(退格键)
AL 字符的 ASCII 码 表征 "字符本身是什么" 退格的 ASCII 是 08H(不可显示)
AH 键盘按键的扫描码 表征 "按的是哪个物理键" 退格键的扫描码是 0EH

简单说:

  • AL 回答 "这个键对应的字符是什么"(ASCII 是字符的 "身份标识");
  • AH 回答 "按的是键盘上哪个键"(扫描码是按键的 "硬件标识")。

三、cmp与jb的配合使用

指令 功能说明
cmp al, 20h 比较 AL 寄存器的值与 20H(ASCII 空格符的编码),本质是执行 AL - 20H,仅修改标志位(不保存结果)。
jb nochar jb = Jump if Below(无符号小于则跳转),检测 CF(进位标志)是否为 1:- 若 AL < 20HCF=1 → 跳转到 nochar;- 若 AL ≥ 20HCF=0 → 不跳转,执行下一条指令。

四、cmp与ja的配合使用

指令 功能说明
cmp ah, 2 比较指令:执行 AH - 2 的运算(不保存结果),仅修改标志寄存器(CF/OF/ZF 等)
ja sret 条件跳转指令:无符号数大于则跳转 (Jump if Above),跳转到sret标签

五、jmp与jne的配合使用

指令 全称 类型 功能说明
jmp Jump 无条件跳转 直接跳转到指定标签 / 地址,必然执行跳转,无任何条件限制
jne Jump if Not Equal 条件跳转 仅当标志寄存器中 ZF=0(即 "比较结果不相等")时,才跳转到指定标签 / 地址

常见搭配

  • cmp a, b + jne label → 如果 a ≠ b,跳转

六、mul dh的说明解释

mul dh 是 x86 汇编语言中的一条指令,用于无符号乘法。下面是对这条指令的详细解释:


指令格式:

复制代码
mul source

其中 source 是一个 8 位、16 位、32 位或 64 位的寄存器或内存操作数(取决于当前操作模式)。


针对 mul dh 的具体说明:

  • dh 是 8 位寄存器,是 dx 寄存器的高字节。
  • 执行 mul dh 表示将 AL (8 位累加器)中的值与 DH 中的值相乘。
  • 结果是一个 16 位无符号整数 ,存储在 AX 寄存器中。

数学表达:

复制代码
AX = AL × DH   (无符号乘法)

七、去掉si应该也不影响程序运行吧?

结论先行 :去掉SI(直接删除mov si,0,并把所有[si][bx]改成[bx]),程序在逻辑上仍能运行 ,但会丧失SI带来的灵活性,且违背 8086 汇编的寻址设计规范 ------ 本质是SI=0时,[si][bx]等价于[bx],但SI的存在是为了 "适配通用场景",而非 "当前简单场景的必需"。

1、为什么去掉 SI 不影响当前程序运行?

核心原因是代码中SI被固定初始化为 0,且全程未修改:

asm

复制代码
start:
    mov ax,data
    mov ds,ax
    mov si,0  ; SI=0,全程不变
    ...
charpush:
    mov bx,top
    mov [si][bx],al  ; SI=0 → 等价于[bx] = al
    ...
charpop:
    mov bx,top
    mov al,[si][bx]  ; SI=0 → 等价于al = [bx]
    ...
charshow:
    mov al,[si][bx]  ; SI=0 → 等价于al = [bx]

8086 的[si][bx]基址 + 变址寻址 ,公式为:物理地址 = DS×16 + SI + BXSI=0时,公式简化为:物理地址 = DS×16 + BX这与直接使用[bx](仅变址寻址)的结果完全一致,因此去掉SI、将[si][bx]改为[bx],程序的栈读写逻辑不会变化,仍能正常入栈、出栈、显示。

2、但 SI 的存在绝非多余(为什么代码要保留 SI?)

虽然当前场景下SI=0看似无用,但它是为代码的可扩展性和规范性设计的,核心价值体现在:

2.1. 支持 "多栈 / 栈偏移存储"(扩展性)

如果后续需要在data段中定义多个栈(如 "输入栈"+"备份栈"),或让字符栈不从data:0开始存储,只需修改SI的值即可,无需重构核心寻址逻辑:

复制代码
; 示例:字符栈从data:32开始(前32字节用于其他数据)
mov si,32  ; 仅修改SI,栈操作逻辑([si][bx])完全不变

若去掉SI,则需修改所有[bx][bx+32],代码耦合度高、易出错。

相关推荐
3824278279 小时前
8086 CPU汇编伪操作汇总
汇编
渡我白衣11 小时前
C++可变参数队列与压栈顺序:从模板语法到汇编调用约定的深度解析
c语言·汇编·c++·人工智能·windows·深度学习·硬件架构
旧梦吟1 天前
脚本语言 汇编
汇编
iCxhust1 天前
8088单板机C语言汇编混合编程实验方法与步骤
c语言·汇编·单片机·嵌入式硬件·微机原理
元亓亓亓2 天前
考研408--组成原理--day8--汇编指令&不同语句的机器级表示
开发语言·汇编·c#
缘友一世2 天前
计算系统安全速成之机器级编程(数组和指针)【3】
汇编·计算机组成原理·数组和指针
切糕师学AI2 天前
ARM 汇编指令:LDR
汇编·arm开发
询问QQ688238863 天前
探索多虚拟电厂联合调度优化模型:集中式算法的实践
汇编
草莓熊Lotso4 天前
C++11 核心特性实战:列表初始化 + 右值引用与移动语义(附完整代码)
java·服务器·开发语言·汇编·c++·人工智能·经验分享