如何在8086x汇编中测量用户键盘输入的打字时长
8086汇编打字计时程序实现方案
实现原理说明
你要的计时功能不需要直接操作硬件定时器,直接调用BIOS维护的系统全局计数器即可,原理如下:
- 8086平台的BIOS会自动维护一个32位的计时计数器,存放在内存地址
0040H:006CH处,系统定时器每间隔约55ms(精确值为54.925ms)会触发一次INT 1CH中断,每触发一次该计数器加1,每秒大约累加18.2次 - 计时逻辑只需要记录两个时间点的计数器值:用户按下第一个按键时的计数值、按下回车键时的计数值,二者的差值乘以55就可以得到近似的毫秒耗时,误差在可接受范围内
- 字符串数组在8086汇编里直接用指针数组实现即可:数据段先定义10个字符串,再定义一个字数组存储每个字符串的首地址,和高级语言的字符串数组逻辑完全一致
完整示例代码
; 运行环境:DOSBox、MASM/TASM编译 DATA SEGMENT ; 字符串指针数组,对应长度为10的字符串数组 STR_ARR DW STR0, STR1, STR2, STR3, STR4, STR5, STR6, STR7, STR8, STR9 ; 10个测试单词 STR0 DB 'hello',0 STR1 DB 'world',0 STR2 DB 'x86asm',0 STR3 DB 'assembly',0 STR4 DB 'dosbox',0 STR5 DB 'bios',0 STR6 DB 'interrupt',0 STR7 DB 'counter',0 STR8 DB 'keyboard',0 STR9 DB 'program',0 INPUT_TIP DB 0DH,0AH,'Please input the word above: $' RES_TIP DB 0DH,0AH,'Total time used: $' MS_UNIT DB ' ms',0DH,0AH,'$' START_CNT DD 0 ; 存储开始计时的计数器值 DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA START: MOV AX, DATA MOV DS, AX ; ---------------------- ; 1. 随机取一个单词打印 ; ---------------------- RAND_GET: MOV AX, 0040H MOV ES, AX MOV AL, ES:[006CH] ; 取系统计数器低字节做随机种子 XOR AH, AH MOV BL, 10 DIV BL ; 模10得到0-9的索引 MOV AL, AH XOR AH, AH MOV BX, AX SHL BX, 1 ; 每个指针占2字节,索引乘2 MOV SI, STR_ARR[BX] ; 取对应字符串首地址 ; 打印目标单词 PRINT_STR: MOV AL, [SI] CMP AL, 0 JZ PRINT_TIP MOV AH, 0EH INT 10H INC SI JMP PRINT_STR PRINT_TIP: MOV AH, 09H LEA DX, INPUT_TIP INT 21H ; ---------------------- ; 2. 等待第一次按键,开始计时 ; ---------------------- WAIT_FIRST_KEY: MOV AH, 00H INT 16H ; 阻塞读键盘,无输入不返回 ; 回显第一个按键 MOV AH, 02H MOV DL, AL INT 21H ; 记录开始计数值 MOV AX, 0040H MOV ES, AX MOV AX, ES:[006CH] MOV WORD PTR START_CNT, AX MOV AX, ES:[006CH+2] MOV WORD PTR START_CNT+2, AX ; ---------------------- ; 3. 等待用户输入直到回车,停止计时 ; ---------------------- INPUT_LOOP: MOV AH, 00H INT 16H CMP AL, 0DH ; 判断是否是回车键 JZ STOP_COUNT ; 回显按键 MOV AH, 02H MOV DL, AL INT 21H JMP INPUT_LOOP STOP_COUNT: ; 读取结束计数值 MOV AX, 0040H MOV ES, AX MOV DX, ES:[006CH] MOV CX, ES:[006CH+2] ; 计算差值 = 结束值 - 开始值 SUB DX, WORD PTR START_CNT SBB CX, WORD PTR START_CNT+2 ; 差值乘以55得到毫秒数(默认差值不超过65535,足够日常测试) MOV AX, DX MOV BX, 55 MUL BX ; 结果存在DX:AX,这里只取AX即可 ; ---------------------- ; 4. 打印耗时 ; ---------------------- PRINT_RES: MOV AH, 09H LEA DX, RES_TIP INT 21H ; AX存毫秒数,转十进制打印 CALL PRINT_NUM MOV AH, 09H LEA DX, MS_UNIT INT 21H ; 退出程序 EXIT: MOV AH, 4CH INT 21H ; 子程序:打印AX中的十进制数字 PRINT_NUM: PUSH BX PUSH CX PUSH DX MOV CX, 0 MOV BX, 10 LOOP1: DIV BL PUSH AX INC CX XOR AH, AH CMP AL, 0 JNZ LOOP1 LOOP2: POP AX MOV DL, AH ADD DL, 30H MOV AH, 02H INT 21H LOOP LOOP2 POP DX POP CX POP BX RET CODE ENDS END START
注意事项
- 上述代码直接使用BIOS提供的计数器,不需要手动配置8253定时器、不需要修改中断向量,兼容性最高,在所有8086 DOS环境下都可以正常运行
- 如果需要更高精度的计时,可以自行挂钩
INT 1CH中断,在中断服务程序里维护自定义的计数器,但是需要注意退出程序前要恢复原中断向量,避免系统崩溃 - 随机取词逻辑使用系统计数器的低字节作为随机源,不需要额外实现随机数算法,足够满足普通测试需求
内容的提问来源于stack exchange,提问作者embedded_master
相关产品推荐
相关产品推荐

