⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reader.asm

📁 汇编语言课程设计《文本阅读器》
💻 ASM
字号:
MYDAT	SEGMENT
MAXLEN	DW 300	;最大行数
INDEX	DW 300 DUP(?)	;行索引
LENCT	DW 0	;实际行数
CURLINE	DW 0	;当前页第一行行数
CCOUNT	DW ?	;文件总字符数

TOP	DB 5	;窗口左上角行数
LEFT	DB 5	;窗口左上角列数
WS	DW 60	;窗口宽度
HS	DW 15	;窗口高度
FG	DB 17H	;文字属性
FGB	DB 1FH	;窗口边框属性

MSG1	DB 'File:$'	;输入文件名提示
MSG2	DB 'File open error!$'	;打开文件出错时提示
MSG3	DB 'File read error!$'	;读文件出错时提示
FILENA	DB 80,?,80 DUP(?)	;输入文件名缓冲区

;下面是定义功能键和相应的处理程序
;每个功能定义两个数据:扫描码,处理程序入口地址
;该数据由DOSUB子程序使用
KEYSUB	DW 48H,SUB1 ,50H,SUB2, 0

MYDAT	ENDS

BUFFER	SEGMENT
	DB 65535 DUP(?)	;文件缓冲区,文件最大为64K
BUFFER	ENDS

CODE	SEGMENT	
	ASSUME CS:CODE, DS:MYDAT, ES:BUFFER
START:	CALL	INIT	;程序初始化
	CALL	READFILE	;读入文件
	CALL	CLIST	;初始化索引行
	CALL	CLSDISP	;清屏
	CALL	CREWIN	;创建窗口
	CALL	SHOWCUR	;显示当前页
	CALL	MAIN	;进入主程序
	CALL	CLSDISP	;清屏
	MOV	AX,4C00H
	INT	21H	;退出本程序

;显示当前页子程序
SHOWCUR PROC
	MOV	DH,0	;窗口最顶行
	MOV	SI,CURLINE	;取当前页第一行行数

	MOV	CX,HS	;窗口的第一行和最后一行为边框,
	SUB	CX,2	;所以能够显示内容的高度为窗口高度减二
SC1:	PUSH	CX
	PUSH	SI
	PUSH	DX
	CALL	SHOWLINE	;显示一行
	POP	DX
	POP	SI
	POP	CX
	INC	DH	;窗口下一行
	INC	SI	;文件下一行
	MOV	AX,LENCT	;
	CMP	SI,AX	;文件是否显示完
	JNC	SC2	;显示完则转SC2
	LOOP	SC1
	JMP	SC1X

SC2:	JCXZ	SC1X	;该页显示满则结束

;该页没有显示满,则用空行填满该页
SC3:	PUSH	CX
	PUSH	DX
	CALL	SHOWSPLINE	;显示空行
	POP	DX
	POP	CX
	INC	DH
	LOOP	SC3
SC1X:
	RET
SHOWCUR	ENDP

;显示一行子程序
;输入:SI=显示行行号
;      DH=窗口中的行号
SHOWLINE	PROC
	ADD	DH,TOP	;
	INC	DH	;实际行数=窗口左上角行数+DI+1
	MOV	DL,LEFT	;
	INC	DL	;实际列数=窗口左上角列数+1
	CALL	VADD	;
	
	MOV	CX,WS	;窗口的第一列和最后一列是边框,
	SUB	CX,2	;所以能够显示内容的宽度为窗口宽度减二
	SHL	SI,1	;行索引为字类型,所以乘二
	MOV	SI,INDEX[SI]	;取该行第一个字符在文件缓冲区的地址
	PUSH	DS
	MOV	AX,0B800H
	MOV	DS,AX	;DS指向显示缓冲区段地址
SL1:	MOV	AX,ES:[SI]
	CMP	AX,0A0DH
	JZ	SL2	;如果是回车换行,则该行结束
	MOV	DS:[DI],AL	;显示一个字符
	ADD	DI,2	;
	INC	SI	;取下一个字符
	LOOP	SL1	;
	JCXZ	SLX	;该行正好显示完,则结束,否则用空格填满

SL2:	MOV	BYTE PTR DS:[DI],' '	;该行没有显示满,则用空格填满
	ADD	DI,2
	LOOP	SL2
SLX:	POP	DS
	RET
SHOWLINE	ENDP

;显示空行子程序
;输入:DI=窗口中的行号
SHOWSPLINE	PROC	;
	MOV	AX,HS	;
	SUB	AX,2	;
	CMP	DH,AL	;显示的空行是否超出最低行
	JNC	SSLX	;如果是,则转SSLX
	ADD	DH,TOP	;
	INC	DH	;实际行数=窗口左上角行数+DI+1
	MOV	DL,LEFT	;
	INC	DL	;实际列数=窗口左上角列数+1
	CALL	VADD	;

	MOV	CX,WS	;窗口的第一列和最后一列是边框,
	SUB	CX,2	;所以能够显示内容的宽度为窗口宽度减二
	PUSH	DS
	MOV	AX,0B800H
	MOV	DS,AX
SSL1:	MOV	BYTE PTR DS:[DI],' '
	ADD	DI,2
	LOOP	SSL1
	POP	DS
SSLX:	RET
SHOWSPLINE	ENDP

;索引行初始化子程序
CLIST	PROC
	MOV	CX,CCOUNT
	JCXZ	CSTX	;文件为空结束

	MOV	SI,0	;文件缓冲区字符指针
	MOV	DI,0	;索引行指针
	MOV	DX,0	;行数计数
	MOV	INDEX[DI],0
	INC	DX	;假设文件至少有一行
CST1:	CMP	WORD PTR ES:[SI], 0A0DH	;判断是否为回车换行符
	JNZ	CST2	;不是行结束,转CST2
	INC	DX	;行数加一
	ADD	SI,2	;跳过回车换行符
	ADD	DI,2	;索引行指针加一
	MOV	INDEX[DI],SI	;指向本行开始地址
	CMP	DX,MAXLEN
	JNC	CST4	;是否超过最大行,超过转CST4,结束
	JMP	CST3	;
CST2:	INC	SI	;文件缓冲区字符指针指向下一字符
CST3:	LOOP	CST1	;
CST4:	MOV	LENCT,DX	;保存行数到LENCT中
CSTX:	RET
CLIST	ENDP

;创建窗口子程序
CREWIN	PROC
	PUSH	ES
	MOV	AX,0B800H	;显示缓冲区地址为B800:0
	MOV	ES,AX	;

	MOV	DH,TOP
	MOV	DL,LEFT	;窗口左上角行列数

	MOV	CX,HS	;窗口高度(行数)
CW1:	PUSH	DX	;
	CALL	VADD	;计算地址

	PUSH	CX	;
	MOV	CX,WS	;窗口宽度(列数)
	MOV	AL,FG	;取属性
CW2:	MOV	BYTE PTR ES:[DI],' '	;送空白字符
	INC	DI	;
	MOV	BYTE PTR ES:[DI],AL	;送属性
	INC	DI	;
	LOOP	CW2	;

	POP	CX
	POP	DX
	INC	DH	;行数加一
	LOOP	CW1

	CALL	DRAWB	;画边框
	POP	ES
	RET
CREWIN	ENDP

;画边框子程序
DRAWB	PROC
	MOV	BL,FGB	;取边框属性
	MOV	DH,TOP
	MOV	DL,LEFT	;窗口左上角行列数
	CALL	VADD	;计算地址
	
;画左上角角框
	MOV	BYTE PTR ES:[DI],0C9H	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	INC	DI

;画第一行边框
	MOV	CX,WS
	SUB	CX,2	;因为去掉了左右角框,所以宽度减二
DB1:	MOV	BYTE PTR ES:[DI],0CDH	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	INC	DI
	LOOP	DB1

;画右上角角框
	MOV	BYTE PTR ES:[DI],0BBH	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL

;画第二行到倒数二行左右边框
	MOV	DH,TOP
	MOV	DL,LEFT
	INC	DH	;从第二行开始
	MOV	CX,HS	;第一行和最后一行没有左右边框,
	SUB	CX,2	;所以高度减二
DB2:	PUSH	DX	;
	CALL	VADD	;计算左边框地址
	MOV	BYTE PTR ES:[DI],0BAH	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	DEC	DI
	MOV	AX,WS	;
	DEC	AX	;
	SHL	AX,1	;
	ADD	DI,AX	;计算右边框地址
	MOV	BYTE PTR ES:[DI],0BAH	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	POP	DX
	INC	DH	;下一行
	LOOP	DB2	;

;画左下角角框
	MOV	DH,TOP	;
	MOV	DL,LEFT	;
	MOV	AX,HS	;
	DEC	AX	;
	ADD	DH,AL	;左下角行数=右上角行数+窗口高度-1
	CALL	VADD
	MOV	BYTE PTR ES:[DI],0C8H	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	INC	DI

;画最低行边框
	MOV	CX,WS	;
	SUB	CX,2	;因为去掉了左右角框,所以宽度减二
DB3:	MOV	BYTE PTR ES:[DI],0CDH	;
	INC	DI
	MOV	BYTE PTR ES:[DI],BL
	INC	DI
	LOOP	DB3

;画右下角角框
	MOV	BYTE PTR ES:[DI],0BCH
	INC	DI
	MOV	BYTE PTR ES:[DI],BL

	RET
DRAWB	ENDP

;计算行列对应显存地址子程序
;输入:DH=行 DL=列
;输出:DI为显存地址
VADD	PROC
	MOV	AL,80	;
	MUL	DH	;
	XOR	DH,DH	;
	ADD	AX,DX	;行数×80+列数
	SHL	AX,1	;乘2,因为每个字符占两个字节
	MOV	DI,AX
	RET
VADD	ENDP

;提示并读入文件子程序
READFILE PROC
	MOV	DX,OFFSET MSG1	;
	CALL	PROMPT	;
	CALL	GETFILE	;
	RET
READFILE ENDP

;将文件读入文件缓冲区子程序
GETFILE PROC
	CALL	SIN	;读入文件名
	MOV	AX,3D00H
	MOV	DX,OFFSET FILENA+2
	INT	21H	;打开文件读
	JC	GETERR1	;打开文件出错转GETERR1
	PUSH	DS
	PUSH	ES
	POP	DS	;将DS指向文件缓冲区段
	MOV	BX,AX	;BX=文件号
	MOV	AH,3FH	;读文件
	MOV	CX,0FFF0H	;读入最多64K
	MOV	DX,0	;DS:DX指向文件缓冲区
	INT	21H
	POP	DS
	JC	GETERR2	;读入文件出错转GETERR2
	MOV	CCOUNT,AX	;实际读入字符数存入CCOUNT中
	RET

GETERR2:MOV	DX,OFFSET MSG3	;打开文件出错处理
	CALL	PROMPT
	CALL	KEYIN
	MOV	AX,4C00H
	INT	21H	;

GETERR1:MOV	DX,OFFSET MSG2	;读入文件出错处理
	CALL	PROMPT
	CALL	KEYIN
	MOV	AX,4C00H
	INT	21H
GETFILE ENDP

;读文件名子程序
SIN	PROC
	MOV	AH,0AH
	MOV	DX,OFFSET FILENA
	INT	21H	;读文件名
	XOR	CH,CH
	MOV	CL,FILENA+1	;取文件名长度
	LEA	BX, FILENA+2
	ADD	BX,CX	;计算字符串结尾地址
	MOV	BYTE PTR [BX],'0'	;在字符串未加0
	RET
SIN	ENDP

;主循环程序
MAIN	PROC
NEXT:	CALL	KEYIN	;读键盘
	CALL	COMMAND	;命令分析
	JNC	NEXT	;非x键继续循环
	RET
MAIN	ENDP

;命令分析子程序
COMMAND PROC
	CMP	AH,2DH	;2DH为x键的扫描码
	JNZ	COM1	;不是x键,转COM1
	STC	;值x键标志
	RET
COM1:	CALL	DOSUB	;转命令散转子程序
	CLC
	RET
COMMAND	ENDP

;读键子子程序
;这么简单的子程序最好使用宏
KEYIN	PROC
	MOV	AH,0	;读键
	INT	16H	;AH中为扫描码
	RET
KEYIN	ENDP

;命令散转子程序
DOSUB	PROC
	MOV	AL,AH
	MOV	AH,0	;将键扫描码放入AX中
	MOV	BX,OFFSET KEYSUB	;BX指向KEYSUB
DOSUB1:	CMP	WORD PTR [BX],0
	JZ	DOSUBX	;KEYSUB表查完,即没有找到转DOSUBX
	CMP	AX,[BX]
	JZ	DOSUB2	;找到扫描码转DOSUB2
	ADD	BX,4
	JMP	DOSUB1	;
DOSUB2:	ADD	BX,2
	MOV	BX,[BX]	;取处理程序入口地址
	JMP	BX	;转处理程序
DOSUBX:	RET

;SUB1和SUB2均为功能键处理程序
;处理完后应使用JMP DOSSUBX结束

;上箭头键处理程序
SUB1:
	CMP	CURLINE,0
	JZ	SUB1X	;已到第一行,结束
	DEC	CURLINE	;向上一行
	CALL	SHOWCUR	;显示当前页
SUB1X:
	JMP	DOSUBX

;下箭头键处理程序
SUB2:
	MOV	AX,LENCT
	DEC	AX
	DEC	AX	;至少显示一行
	CMP	AX,CURLINE	;已到最后一行
	JZ	SUB2X
	INC	CURLINE	;向下一行
	CALL	SHOWCUR	;显示当前页
SUB2X:
	JMP	DOSUBX

DOSUB	ENDP

;清屏子程序
CLSDISP	PROC
	MOV	AX,0600H	;清屏
	MOV	CX,0
	MOV	DH,50
	MOV	DL,79
	MOV	BH,07H
	INT	10H
	RET
CLSDISP	ENDP

;显示方式初始化子程序
DISPINIT PROC
	MOV	AH,0003H	;文本方式 25x80
	INT	10H
	RET
DISPINIT ENDP

;显示子程序
;输入:DS:DX=字符串首地址,'$'结束
;如此简单的子程序最好使用宏
PROMPT	PROC
	MOV	AH,09H	;
	INT	21H	;
	RET
PROMPT	ENDP

;初始化子程序
INIT	PROC
	MOV	AX,MYDAT
	MOV	DS,AX
	MOV	AX,BUFFER
	MOV	ES,AX
	CALL	DISPINIT	;显示方式初始化
	RET
INIT	ENDP

CODE	ENDS

END	START

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -