21,459
社区成员
发帖
与我相关
我的任务
分享
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
START: JMP BEGIN
KEEPCS DW 0
KEEPIP DW 0
BEGIN:
MOV AH,01H
INT 21H
CMP AL,'0'
JB BEGIN
CMP AL,'9'
JA BEGIN
MOV CH,AL ;这里放入ch,保存使用者输入的[十位数]的[字符]
SUB AL,30H ;字符ascii转作实值
MOV CL,10 ;将十位数 x 10
MUL CL
MOV DL,AL ;保存在dl
MOV AH,01H
INT 21H
CMP AL,'0'
JB BEGIN
CMP AL,'9'
JA BEGIN
MOV CL,AL ;这里放入cl,保存使用者输入的[个位数]的[字符]
SUB AL,30H ;字符ascii转作实值
ADD DL,AL ;乘了10的十位数加上个位数,就是使用者输入的迟延值
停下来看一看,代码用了二次int21,ah=1读取使用者输入的两个字符,但若输入单一数值呢?
假若我们输入9,按回车,程式不接受,它就是死懒不活的要我们输入两个数字,不可以只输入个数按回车!
又假若我们输入了9,然后发觉错了,想按退位改为回8,对不起不可以,没有编辑功能啊兄弟!
好吧,你终于输入了两个字符,它把输入的迟延值做成两个变数....
变数1是 dl,下面会再放入bl 是个实值,比如输入12,dl=0ch ,用做倒数的初始值,每次到约1秒就减1
变数2是 cx 分别是两个字符ch=十位数,cl=个位数,用做显示,每次到约1秒cl减1,到零就回到9,并ch减1
cpu每18.2秒会触动时脉中断08h,而08中断代码中会再呼叫1ch中断,原1ch中断里只有一条iret,
iret意思是不做任何事返回,一般做定时工作的程式就利用修改1ch,每1/18.2秒被触动一次,检查是否条件合适才做事
若拿来做计时的话,就设定每18次作一次累加或累减,楼主的代码就是干这码子事,每18次就减bl(迟延值),直到0为止
但,这坨烂代码却衍生两个问题..
1.纯粹做倒数,用得着劳师动众去修改时脉中断吗?(当然可以是题目要求!)
2.不精准,18和18.2毕竟有误差,这里的每次减1秒,实际上是少于1秒,假若要倒数180秒,就有2秒的偏差
以下是修改中断向量1ch,指向自己的代码....
MOV AX,CS
MOV DS,AX ;cs和ds对齐
CLI ;不必要
MOV AH,35H ;读取中断向量
MOV AL,1CH ;1ch中断
INT 21H
MOV KEEPIP,BX ;保存原中断向量cs:ip
MOV KEEPCS,ES
PUSH DS
MOV DX,OFFSET NINT ;指向自己的新中断代码
MOV AX,SEG NINT
MOV DS,AX
MOV AH,25H ;修改中断向量
MOV AL,1CH
INT 21H
POP DS
MOV BL,DL ;保存迟延值
MOV BH,18 ;被始化bh = 18
STI ;不必要
LOP: CMP BL,0 ;迟延值是否减到0?
JG LOP ;未,再回lop...这时1ch已被修改,所以回圈虽然是2条指令,但新1ch中断己在背后运作,每1/18.2秒一次
DONE: CLI ;不必要
PUSH DS ;至此,倒数已终正
MOV DX,KEEPIP ;取回原1ch中断的cs:ip
MOV AX,KEEPCS
MOV DS,AX
MOV AH,25H ;修改回原中断
MOV AL,1CH
INT 21H
POP DS
STI ;不必要
NEXT4: MOV DL,'0' ;最后印出双0
MOV AH,2
INT 21H
INT 21H
MOV AH,4CH
INT 21H
NINT PROC FAR ;新1ch中断
CLI
DEC BH ;第一次运行时这个初值是18
JNZ NEXT2
MOV BH,18 ;重置
DEC CL ;减迟延字符的[个位]
CMP CL,2FH;因0的ascii是30h,所以若减到2fh,则须借位
JNZ NEXT1
DEC CH ;减迟延字符的[十位],即借位
MOV CL,39H ;重置迟延字符的[个位]为字符9,即39h
NEXT1: MOV DL,CH ;至此,ch和cl就是将要显示的倒数值,以下以int21h,ah=2的方式显示
CMP CH,'0'
JA NEXT3
CMP CL,'0'
JE NEXT4
NEXT3: MOV AH,2
INT 21H
MOV DL,CL
INT 21H
MOV DL,20H
INT 21H
DEC BL ;显示完毕,迟延值 bl减1
JNZ NEXT2 ;未减完,返回
JMP DONE ;减完,直接跳回主程式!真的无语了,好歹设定一个flag,由主程式那个loop决定要不要终止回圈好不好!
NEXT2: STI
IRET
NINT ENDP
CODE ENDS
END START
老实说,这坨烂代码写得既冗长又不规范,我是老师只会给10分,若100为满分的话....
下面的代码是一个不用中断,且比较优雅和精简的方式做倒数,楼主可以参考一下
.286
DATA SEGMENT
in_msg db 'Input delay seconds(1-99):$'
in_buf db 3,0,3 dup (0)
disp_msg db 0,0,0dh,'$'
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
mov ax,data
mov ds,ax
mov ah,9
mov dx,offset in_msg
int 21h
mov dx,offset in_buf
mov ah,0ah ;读取使用者输入字符串
int 21h
mov dl,0dh ;以下输出回车
mov ah,2
int 21h
mov dl,0ah
int 21h
mov si,offset in_buf + 2 ;指向输入缓冲
xor cx,cx
mov cl,in_buf + 1 ;取实际输入个数
jcxz quit ;若无输入,离开
xor bx,bx
next:
lodsb ;取字符
cmp al,0dh ;回车?
jz next1
sub al,'0' ;ascii -> 值
cmp al,9 ;是否数字0-9
ja quit ;不是,则离开
xchg al,bl ;交换前值
mov ah,10
mul ah ; 前值 x 10
add al,bl ;加上个位
mov bl,al ;保存
jmp short next ;读下一字符
next1:
xor al,al
out 70h,al ;利用port70h的cmos时脉索引0
in al,71h ;读取当前的秒值
cmp cl,al ;是否到了下一秒值
jz next1 ;仍未转秒值
mov cl,al ;保存新秒值
dec bl ;减倒数
js quit ;倒数到负则离开
mov al,bl ;取倒数值
aam ;bcd调整,若al=0c, aam后 ax=0102
or ax,3030h ;转ascii
xchg ah,al ;交换
mov word ptr disp_msg,ax ;送到字符串
mov dx,offset disp_msg
mov ah,9 ;印出倒数字串
int 21h
jmp short next1 ;回圈
quit:
mov ah,4ch
int 21h
CODE ENDS
END START