📄 reader.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 + -