文章目录

汇编语言程序设计

2024.8.16 更新。拖了好久,终于搞了图床,有图片了

1 基础知识

1.1 总线

连接CPU和其他芯片的导线

  • 地址总线
  • 数据总线
  • 控制总线

内部总线实现CPU内部各个器件之间的联系

外部总线实现CPU和主板上其他器件的联系


小结

  • 一个存储单元可以存储8个bit,即8位二进制数,一个字节

1.2 各类存储器芯片

从读写属性上分

  • 随机存储器(RAM)

    • 断电数据会丢失
  • 只读存储器(ROM)

2 CPU工作原理

2.1 通用寄存器

  • 一个16位寄存器所能存储的数据最大值是 $ 2^{16}-1$
    • 从零开始,第16位权重为 2 16 2^{16} 216

2.2 几条汇编指令

;AX = 00C5H
ADD AL,85H  ;运算结果为158H,但AL不会进位到AH,所以AX = 0058H

2.3 逻辑地址

  • 偏移地址为16位,16位地址的寻址能力为64K,所以一个段的长度最大为64KB
  • CPU访问内存单元时必须向内存提供内存单元的物理地址
  • 一个地址指向一个内存单元,一个内存单元等于一个字节,一个字节等于八位

2.4 CS和IP

  • CS为代码段寄存器
  • IP为指令指针寄存器

他们指示了CPU当前要读取指令的地址

工作过程

(1)从CS:IP指向内存单元读取指令,读取指令进入缓冲器

(2)IP = IP + 所读取的指令的长度,从而指向下一条指令

(3)执行指令。转到步骤(1),重复。


8086CPU上电启动或复位后,CS:IP被设置为FFFF:0000,即刚启动时,FFFF0H单元中的指令是开机执行的第一条指令

如何修改CS,IP的值?

CS,IP的值不能用 MOV 来修改,要用 MOV

;JMP 段地址:偏移地址
JMP 2AE3:3
JMP 3:0B16
;JMP同时修改CS,IP的值

;仅修改IP的内容
;JMP 某一合法寄存器
JMP AX  ;类似于 MOV IP,AX
JMP BX

2.5 DEBUG

  • R命令查看、改变CPU寄存器的内容

    • R AX
  • D命令查看内存中的内容

  • E命令改写内存中的内容

  • U命令将内存中的机器指令翻译成汇编指令

  • T命令执行一条机器指令

  • A命令以汇编指令的格式在内存中写入一条机器指令

  • Q命令是退出

  • G加上偏移地址可以使程序直接跳到代码位置

  • P跳过循环


  • Debug将程序从可执行文件加载入内存后,CX中存放的是程序的长度

  • Debug执行到INT 21是要用P命令执行结束程序

3 内存访问

3.1 内存中的字存储

这是一张图片

(1) 2地址单元存放的字型数据是多少?

0012H

(2) 2地址存放的字节型数据是多少?

12H

(3) 1地址单元存放的字型数据是多少?

124EH

3.2 DS和[address]

  • CPU要读取一个内存单元时,必须先给出这个内存单元的地址
  • 8086CPU中有一个DS寄存器,通常用来存放要访问的数据段地址

例,读取10000H单元的内容可用如下程序段进行

MOV BX,1000H
MOV DS,BX
MOV AL,[0]  ;[]中放的是偏移地址,段地址由DS提供

上面三条指令将1000:0中的数据读到AL中


  • 8086CPU不支持将数据直接送入段寄存器的操作,只能经过通用寄存器
    • 数据通用寄存器段寄存器

3.3 栈

  • 8086CPU的入栈和出栈操作都是以为单位进行的

image-20221203153027845箭头指针指向的位置是当前的栈顶


  • 段寄存器SS:存放栈顶的段地址

  • 寄存器SP:存放栈顶的偏移地址

  • 任意时刻,SS:SP指向栈顶元素

PUSH指令的执行过程

当执行一次push指令时,SP = SP - 2,栈顶指针向上移动两个,然后低地址存放低字节数据,高地址存放高字节数据

image-20221203154102078


  • 如果一个堆栈段是空的,那SP指向栈空间最高地址单元的下一个单元

image-20221203154448595

假设10000H~1000FH当作堆栈段,SS=1000H,栈空间大小为16字节,栈最底部的字单元地址为?

1000:000E

3.4 栈顶越界的问题

栈空间之外的空间可能存放了具有其他用途的数据、代码等,因此要避免栈顶越界的问题

3.5 PUSH,POP指令

  • 操作数可以为段寄存器

    • PUSH DS
      POP ES
      
  • 操作数也可以为内存地址

    • PUSH [0]  ;将0地址单元处的字入栈
      POP [2]  ;用2内存字单元接收出栈的数据
      

将10000H~1000FH这段空间当作栈,初始状态是空的,将AX,BX,DS中的数据入栈

MOV AX,1000
MOV SS,AX

MOV SP,0010H  ;栈为空,指向最高地址单元的下一个单元

PUSH AX
PUSH BX
PUSH DS

  • PUSH指令是先使SP-2,再放入值

  • POP指令是先把值复制出来,再使SP+2

3.6 栈段

如果将10000H~1FFFFH这段空间当作堆栈段,初始状态为空,此时,SS=1000H,SP=?

SP指向最高地址单元的下一个地址,栈中只有一个元素时,SP=FFFEH,栈中没有元素时相当于栈中的唯一一个元素出栈,SP=SP+2,所以SP=0


所以栈顶的变化范围是0~FFFFH,从栈空时候的SP=0,一直压栈,直到栈满时SP=0;如果再次压栈,栈顶将环绕,覆盖了原来栈中的内容

4 第一个程序

4.1 段结束、程序结束、程序返回

段结束

段名 ends (多的s可以理解为segment)

程序结束

end

程序返回

;在程序末尾添加返回的程序段
MOV AX,4C00H
INT 21H

4.2 关于编译和链接

masm 1.asm  ;编译,生成.obj目标文件
link 1.obj  ;链接,生成.exe可执行文件
1.exe       ;运行

4.3 EXE文件中的程序加载过程

  • 程序加载后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为 DS:0
  • 这个内存区的前256个字节中存放的是PSP,DOS用来和程序通信
  • 从256字节处向后的空间存放的是程序 DS+10H:0

5 [BX]和LOOP指令

5.1 MOV AX,[]的说明

debug中可以直接用 MOV AX,[0]

但是在编译器中的话编译器会看成 MOV AX,0

因此要用通用寄存器间接寻址

MOV BX,0
MOV AX,[BX]

5.2 描述性符号"()"

用(X)表示X中存放的内容

(AX) = 0010H

5.3 LOOP指令

LOOP 标号

1 (CX) = (CX) - 1

2 判断CX中的值,不为零则转到标号处执行程序,为零则向下执行

;计算2^12
ASSUME CS:CODE
CODE SEGMENT
START:MOV AX,DATA
	  MOV DS,AX
	  MOV AX,2
	  MOV CX,11
ADD1: ADD AX,AX
      LOOP ADD1
      
      MOV AX,4C00H
      INT 21H
CODE ENDS
END	   

image-20221205094039205

ASSUME DS:CODE
CODE SEGMENT
START:
	MOV AX,0FFFFH  ;在汇编源程序中,数据不能以字母开头,加0
	MOV DS,AX
	MOV BX,6
	MOV AH,0
	MOV AL,[BX]  ;用BX间接作为偏移地址或者用DS:[6]
	MOV DX,0
	MOV CX,3
ADD1:
	ADD DX,AX
	LOOP ADD1
	
	MOV AX,4C00H
	INT 21H
CODE ENDS
END

image-20221205145707803

ASSUME DS:CODE
CODE SEGMENT
START:
	MOV AX,OFFFH
	MOV DS,AX
	MOV BX,0
	MOV DX,0
	
	MOV CX,12
ADD1:
	MOV AL,[BX]
	MOV AH,0
	ADD DX,AX
	INC BX
	LOOP ADD1
	
	MOV AX,4C00H
	INT 21H
CODE ENDS
END

image-20221205154734109

	MOV BX,0
	MOV CX,12
S:
    MOV AX,0FFFH
    MOV DS,AX
    MOV DL,[BX]
    MOV AX,0020H
    MOV DS,AX
    MOV [BX],DL
    INC BX
    LOOP S
    
    MOV AX,4C00H
    INT 21H
;********************************
;为了提高效率,可以用ES,将目标段赋给ES,就不用改变DS

6 包含多个段的程序

6.1 在代码段中使用数据

ASSUME CS:CODE
CODE SEGMENT
	DW 0001H,0FF1H,3204H
	·
	·
	·
CODE ENDS
END

因为DW定义的数据处于代码段最开始,段地址肯定为CS,偏移地址就是从0开始。0,2,4

6.2 在代码段中使用栈

;***********逆序****************
ASSUME CS:CODE
CODE SEGMENT
	DW 1234H,0456H,0789H,0ABCH,0DEFH
	DW 0,0,0,0,0    ;通过定义数据在代码段中开辟一部分空间
					;作为堆栈
START:MOV AX,CS
	MOV SS,AX
	MOV BX,0
	MOV AX,0
	MOV SP,20    ;设置栈顶SS:SP指向CS:20,起始是最高地址单元+1
	             ;0-9 + 1
	MOV CX,5
s:  PUSH CS:[BX]
	INC BX
	INC BX  ;注意是字型数据,要+2
	LOOP S
	
	MOV BX,0
	MOV CX,5
S1: POP CS:[BX]
	INC BX
    INC BX
    LOOP S1
    
    MOV AX,4C00H
    INT 21H
CODE ENDS
END START

6.3 将数据、代码、栈放入不同段

DATA SEGMENT
	DB ……
DATA ENDS

STACK SEGMENT
	DB 0,0,0,0,0
STACK ENDS

CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START:MOV AX,STACK
	MOV SS,AX
	MOV AX,DATA
	MOV DS,AX
	PUSH DS:[0]
	PUSH DS:[2]
	POP DS:[2]
	POP DS:[0]
	
	MOC AX,4C00H
	INT 21H
CODE ENDS
END START
  • 如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为 16*(N/16+1)

7 更灵活定位内存地址

7.1 ASCII码

img

  • ASCII码只需要一个字节就能存储

  • 0→30H,A→41H,a→61H

  • DB 'unIX'相当于DB 75H,6EH,49H,58H

  • 大写和小写字母的ASCII码

    • 二进制表示对应位只有 D 5 D_5 D5位不同,大写为0,小写为1
    • 十六进制对应位相差20H

7.2 [BX+IDATA]

MOV AX,[BX+200]还可以写成一下三种形式

MOV AX,[200+BX]

MOV AX,200[BX]

MOV AX,[BX].200

7.3 SI和DI

  • SI和DI是跟BX功能相近的寄存器,但不能分成两个8位寄存器使用

    • image-20221207143711676
  • 也可以与BX组合→[BX+SI]or[BX][SI]

  • image-20221207144903751

7.4 汇编语言实现循环嵌套

;将DATA段中的每个单词改为大写字母

DATA SEGMENT
	DB 'ibm             '
	DB 'dec             '
	DB 'dos             '
	DB 'vax             '
DATA ENDS

CODE SEGMENT
	ASSUME CS:CODE,DS:DATA
START:
	MOV AX,DATA
	MOV DS,AX
	MOV BX,0
	MOV CX,4
LOOP2:
	MOV DX,CX      ;用DX暂存CX的当前值
	MOV SI,0
	MOV CX,3
LOOP1:
	MOV AL,[BX+SI]
	AND AL,0DFH
	MOV [BX+SI],AL
	INC SI
	LOOP LOOP1
	
	ADD BX,16
	MOV CX,DX
	LOOP LOOP2  ;先执行CX = CX - 1 再判断是否为零
	
	MOV AX,4C00H
	INT 21H
CODE ENDS
END START

如果寄存器在程序中都被使用,无法用来暂存数据怎么办?

1开辟一段内存空间,用这段空间来暂存

DATA SEGMENT
	DW 0   ;用来存放CX的值

2利用堆栈

image-20221207164309844

7.5 小结

1在8086CPU中,只有BX,BP,SI,DI可以在**[]**中来进行内存单元的寻址

2在**[]**中,这4个寄存器可以单个出现,或只能以四种组合出现:

​ BX和SI、BX和DI、BP和SI、BP和DI

3只要在**[]中使用寄存器BP**,指令中没有明显给出段地址,段地址默认在SS

8 数据处理

8.1 指令处理数据的长度

  • 在没有寄存器名存在的情况下,用操作符 X PTR 指明内存单元的长度,X可以是WORDBYTE

8.2 寻址方式的综合应用

  • 一般,可以用 [BX+i+SI] 的方式来访问结构体中的元素

    用bx定位整个结构体,用i定位结构体中某一个数组项,用SI定位数组项中的每一个元素

    汇编提供了更为贴切的书写方式,如[BX].i[SI]

8.3 DIV指令

image-20221208114912523

image-20221208115025941

指令格式

  • DIV reg
  • DIV 内存单元

8.4 伪指令DD

DD表示定义双字变量

DATA SEGMENT
	DD 1
DATA ENDS

定义数据为00000001H,在DATA:0处占两个字

8.3 DUP

使用格式

DB 重复的次数 DUP(重复的字节型数据)

8.4 实验

image-20221208155416657

image-20221208155447256

9 转移指令

9.1 OFFSET操作符

  • 取得标号的偏移地址
    • MOV AX,OFFSET START

9.2 JMP指令补充

  • 无条件转移指令

JMP SHORT 标号 ;SHORT指明此处为8位位移

实现段内短转移,对IP修改范围为-128~127,即向前转移最多越过128个字节,向后转移最多越过127个字节

JMP NEAR PTR 标号

实现段内近转移,(IP)=(IP)+16位位移,范围为—32769~32767

JMO FAT PTR 标号

实现段间转移,又称为远转移,修改CS和IP

转移地址在内存中的使用格式

(1)JMP WORD PTR 内存单元地址(段内转移)

功能:内存单元地址处存放的一个字是转移的目的偏移地址

(2)JMP DWORD PTR(段间转移)

image-20221209090740129

image-20221209090949317

9.7 JCXZ指令

  • 当CX为0时跳转
    • if((CX)==0) JMP SHORT 标号

9.8 显存(拓展)

image-20221209095422636

image-20221209095638412

10 CALL和RET

10.1 RET和RETF

RET

  • RET指令用栈中的数据修改IP的内容,实现段内转移,执行时
    • (IP)=((SS)*16+(SP))
    • (SP)=SP+2

相当于

POP IP

image-20221209162146940

RETF

  • 用栈中的数据修改CS和IP的内容,实现远转移,顺序执行以下操作
    • (IP)=((SS)*16+SP)
    • (SP)=(SP)+2
    • (CS)=((SS)*16+(SP))
    • (SP)=(SP)+2

相当于

POP IP
POP CS

image-20221209162308386

10.2 CALL指令

image-20221209162601443

  • 将当前的IP压栈后,转到标号处执行指令
    • (SP)=(SP)-2
    • ((SS)*16+(SP))=(IP)
    • (IP)=(IP)+16位位移

image-20221209162931795

CALL将下一条指令的地址入栈,执行完函数后RET从栈中取指令地址跳回去


CALL FAR PTR 标号

image-20221209163711976

相当于

PUSH CS
PUSH IP 
JMP FAR PTR 标号

10.3 具体应用

image-20221209165816626

10.4 MUL指令

相乘的两个数要么都是8位,要么都是16位

image-20221209165924322

10.5 参数和结果传递

;************************
;子程序
;说明:计算N的3次方
;参数:(BX)=N
;结果:(DX:AX)=N^3
;*************************
cube:
	MOV AX,BX
	MUL BX
	MUL BX
	RET

  • 在子程序中使用寄存器之前最好将它们先入栈,然后寄存器就可以充当子程序中的局部变量

11 标志寄存器

image-20221210115622161

11.1 ZF标志

零标志位

  • 指令执行后结果为0,则ZF=1,反之,ZF=0
    • ADD,CMP等指令都会影响,只看结果是否为0

11.2 PF标志

奇偶校验位

  • 指令执行后,结果所有二进制位中1的个数
    • 为偶数,PF=1
    • 为奇数,PF=0

11.3 SF标志

符号标志位

  • 指令执行后

    • 结果为负,SF=1
    • 结果为正,SF=0
  • 将数据当作无符号数来运算则其值没有意义

11.4 CF标志位

进位(借位)标志位

  • 指令执行无符号数发生进位或借位后,置1,其他情况置0

11.5 标志位在DEBUG中的表示

image-20221210145039173

11.6 OF标志

溢出标志位

  • 进行有符号数运算时,结果超出机器所能表示的范围称为溢出
    • 符号位改变了

11.7 ADC指令

带进位的加法指令

11.8 SBB指令

带进位的减法指令

11.9 CMP指令

比较指令

相当于减法指令,但不保存结果只影响标志寄存器

  • 无符号数→观察ZF,CF
    • image-20221210152529543
  • 有符号数
    • 可以通过两数相减结果正负判断大小,即观察SF,但如果结果有溢出的话会干扰,所以还要看OF,如果OF变为1了,则与观察SF判断的结果相反
    • 注意大于等于的情况

11.10 条件转移指令小结

无符号数

指令含义检测的相关标志位
JE等于则转移ZF=1
JNE不等于则转移ZF=0
JB低于则转移CF=1
JNB不低于则转移CF=0
JA高于则转移CF=0,ZF=0
JNA不高于则转移CF=0或ZF=1
  • E:表示Equal
  • NE:表示Not Equal
  • B:表示Below
  • NB:表示Not Below
  • A:表示Above
  • NA:表示Not Above

11.11 DF标志和MOVSB

DF方向标志位

CDL指令和STD指令

  • 在串处理指令中,控制每次操作后SI、DI的增减
    • DF=0,每次操作后SI、DI递增
    • DF=1,……递减
  • CLD指令:将DF置0 clear DF
  • STD指令:将DF置1 set DF

串传送指令

  • MOVSB

image-20221210155341684

  • MOVSW
    • 传送一个字,SI和DI递增2或递减2

image-20221210155621668

;用串传送指令,将DATA段中的第一个字符串复制到它后面的空间中
DATA SEGMENT
	DB 'Welcome to masm!'
	DB 16 DUP(0)
DATA ENDS

CODE SEGMENT
	ASSUME CS:CODE,DS:DATA
START:
	MOV AX,DATA
	MOV DS,AX
	MOV ES,AX
	MOV SI,0
	MOV DI,16
	MOV CX,16
	
	CLD
	REP MOVSB
	
	MOV AX,4C00H
	INT 21H
CODE ENDS
END START

11.12 PUSHF和POPF

PUSHF

将所有标志寄存器的值入栈

POPF

将栈中数据弹出,送入所有标志寄存器

12 内部中断

12.1 中断向量表

  • CPU用8位的终端类型码通过中断向量表找到相应的中断处理程序的入口地址

    • 有8位,即256个中断源
  • 内存0000:0000到0000:03FF的1024个单元中存放着中断向量表

    • 一个中断源要4个字节,2个存段地址,2个存偏移地址

中断过程

image-20221210164949371

image-20221210165401923

12.2 中断处理程序

12.2.1 编写步骤

image-20221210170610376

12.2.2 除法错误中断处理

执行DIV指令时,如果发生除法溢出错误(结果无法存放),将产生0号中断

修改0号中断执行程序

 assume cs:code
 
 code segment
 start: 
		mov ax,cs
        mov ds,ax
        mov si,offset do0				;设置ds:si指向源地址
        mov ax,0
        mov es,ax
        mov di,200h       				;设置es:di指向目的地址
        mov cx,offset do0end-offset do0	;设置cx为传输长度
        cld                     		;设置传输方向为正
        rep movsb
        
		;设置中断向量表 
        
		mov ax,4c00h
        int 21h

do0: 
		;显示字符串“Welcome to Fishc.com!” 
        
		mov ax,4c00h
        int 21h
do0end:	
		nop

code ends
end start

通过编译器"-"计算do0函数的代码长度

12.3 单步中断

CPU执行完一条指令后,检测标志寄存器TF位为1则产生单步中断,中断类型码为1

13 INT指令

CPU执行INT N指令,引发N号中断

13.1 对INT、IRET和栈的深入理解

用INT触发中断实现LOOP的功能

image-20221210201332015

image-20221210201235351

13.2 INT 21H

程序返回功能

  • 21号中断中还有很多子程序,其中的4CH号程序功能就是程序返回功能,如下
MOV AH,4CH  ;程序返回
MOV AL,0    ;返回值
INT 21H

光标位置显示字符串功能

;DS:DX指向字符串,要显示的字符串需用“$”作为结束符
MOV AH,9	;功能号9,表示光标位置显示字符串
INT 21H

14 端口

14.1 端口的读写

IN和OUT指令

  • IN AL,60H 从端口60H读数据放到AL中
  • OUT 21H,AL 将AL中的数据放入端口21H中

image-20221210205413013

14.2 CMOS RAM芯片

image-20221210205605703

14.3 SHL和SHR指令

逻辑左移指令SHL

(1) 将一个寄存器或内存单元中的数据向左移位

(2) 将最后移出的一位写入CF中

  • 移动的位数大于1时,必须将移动位数放在CL中
  • 左移一位相当于×2

逻辑右移指令SHR

同理

14.4 BCD码相关

image-20221210210502878

15 外部中断

15.1 可屏蔽中断和不可屏蔽中断

可屏蔽中断

image-20221210211140493

  • 指令STI,将IF置1
  • 指令CLI,将IF置0

不可屏蔽中断

image-20221210211432998

15.2 DELAY函数

DELAY:
	PUSH AX
	PUSH DX
	MOV DX,1000H	;循环10000000H次
	MOV AX,0
S1:
	SUB AX,1
	SBB DX,0
	CMP AX,0
	JNE S1
	CMP DX,0
	JNE S1
	POP DX
	POP AX
	
	RET

16 直接定址表

16.1 数据标号

  • 能够存储数据单元的地址长度

image-20221210214936928

image-20221210214922649

MOV AL,B会报错,因为B代表的内存单元是字单元,但AL是8位的

image-20221210215203801

image-20221210215223428


具体应用

例如,如果要将输入的字符显示在屏幕上,根据数值转化成ASCII码要经过两个区间段的映射,如16.2,因此可以直接用数据标号建立一个表存放0-F的字符,然后直接通过标号索引调用这张表

SHOWBYTE:
	JMP SHORT SHOW
	
	TABLE DB '0123456ABCDEF'
	
SHOW:
	PUSH BX
	PUSH ES
	
	MOV AH,AL    		;START:MOV AL,0EH
	SHR AH,1
	SHR AH,1
	SHR AH,1
	SHR AH,1			;右移4位,AH中得到高位的值
	AND AL,00001111B	;AL中为低四位的值
	
	MOV AL,AH
	MOV BH,0
	MOV AH,TABLE[BX]	;用高四位作为相对于TABLE的偏移,取得对应字符
		·
		·
		·
			

16.2 数值对应ASCII码

0-9的ASCII

+30H

10-15的ASCII

+37H

17 使用BIOS进行键盘输入

17.1 键盘输入

  • INT 16H中断例程有一个功能就是从键盘缓冲区中读取键盘输入,该功能编号为0
MOV AH,0
INT 16H		;读取键盘输入并将其从缓冲区中删除

;结果:(AH)=扫描码,(AL)=ASCII码

17.2 字符串的输入

  • (1) 在输入的同时需要显示这个字符串

  • (2) 一般在输入回车后,字符串输入结束

    • 可以在字符串中加入0,表示字符串结束
  • (3) 能够删除已经输入的字符串


程序处理过程

1、调用INT 16H读取键盘输入

2、如果是字符,进入字符栈,显示字符栈中的所有字符,继续执行1

3、如果是退格,从字符栈中弹出一个字符,显示字符栈中所有字符,继续执行1

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

子程序:字符栈的入栈、出栈和显示

  • 参数说明
    • (AH)=功能号,0表示入栈,1表示出栈,2表示显示
    • DS:SI指向字符栈空间
    • 0号功能:(AL)=入栈字符
    • 1号功能:(AL)=返回的字符
    • 2号功能:(DH)、(DL)=字符串在屏幕上显示的行、列位置
;最基本的字符串输入程序,需要具备下面的功能:
;(1) 在输入的同时需要显示这个字符串;
;(2)一般在输入回车符后,字符串输入结束;
;(3)能够删除已经输入的字符。

;编写一个接收字符串的输入子程序,实现上面三个基本功能。
;因为在输入的过程中需要显示,子程序的参数如下:
;	(dh)、(dl)=字符串在屏幕上显示的行、列位置;
;	ds:si 指向字符串的存储空间,字符串以O 为结尾符。


assume cs:code

code segment
start:	
	call getstr	

return:	
	mov ax,4c00h
	int 21h

;完整的接收字符串输入的子程序

getstr:	
	push ax 

getstrs:
	mov ah,0
	int 16h
	
	cmp al,20h
	jb nochar   		;判断的是ASCII码小于0,说明不是字符
	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
	mov ah,0
	call charstack 		;0入栈
	mov ah,2
	call charstack		;显示栈中的字符

	pop ax
	ret ;getstr ends


;功能子程序实现

charstack:
	jmp short charstart
	
	table dw charpush,charpop,charshow
	top dw 0   			;栈顶
	
charstart:
	push bx
	push dx
	push di
	push es

	cmp ah,2
	ja sret
	mov bl,ah
	mov bh,0
	add bx,bx
	jmp word ptr table[bx]

charpush:
	mov bx,top
	mov [si][bx],al
	inc top
	jmp sret

charpop:
	cmp top,0
	je sret
	dec top
	mov bx,top
	mov al,[si][bx]	
	jmp sret

charshow:
	mov bx,0b800h
	mov es,bx
	mov al,160
	mov ah,0	
	mul dh
	mov di,ax
	add dl,dl
	mov dh,0
	add di,dx

	mov bx,0

charshows:
	cmp bx,top
	jne 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

可编程接口芯片

7.1 8255

image-20221222091411875

image-20221222092222266

image-20221222092536342

image-20221222092720440

image-20221222093037820

image-20221222093659083

image-20221222094404117

image-20221221210825059

image-20221221210946083

image-20221221211450472

7.2 8253

image-20221222001123464

image-20221222000528922

image-20221222000607331

image-20221222000703841

image-20221222000812140

image-20221222000926069

image-20221222001043227

image-20221222085744023

image-20221222085913272

image-20221222090633669

image-20221222090510931

中断

image-20221222124248697

image-20221222124525904

image-20221222124659527

image-20221222143210987

I/O

image-20221224205849532

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐