代码以及详细注释:
;=================================================================
; 猜数游戏
;=================================================================
;本程序采用数字比较的方式来实现,区别于使用字符串比较的猜数程序
;实现5位数字的比较,事实上,可以支持小于655350的正整数比较
;=================================================================
.model small
.stack
.data
mod_sel_str db 'Please select the seed generating mod(1/2): ', 0dh, 0ah, ''
db '(1) Auto random seed', 0dh, 0ah, ''
db '(2) Input seed', 0dh, 0ah, '$'
wrong_input_str db 'Input wrong! Please input again: ', '$'
string db 'Please input a number(0-99999): ', '$'
big_str db 'It is bigger!', 0dh, 0ah, '$'
small_str db 'It is smaller!', 0dh, 0ah, '$'
correct_str db 'Correct, congratulations! The num is: ', '$'
num_str db 'The rand num is: ', '$'
press_str db 'Press any key to continue....', 0dh, 0ah, '$'
try_again db 'Do you want to try again?(y/n)', 0dh, 0ah, '$'
seed_in_str db 'Please input an initial num to guess: ', 0dh, 0ah, '$'
times_str1 db 'You have tried ', '$'
times_str2 db ' times.', 0dh, 0ah, '$'
exit_str db 0dh, 0ah, 'Bye~ ', '$'
buf db 8 dup(?)
cnt dw ?
num dd ?
tmp dw ?
.code
start:
;=================================================================
; 入口函数
;=================================================================
main proc
mov ax, @data
mov ds, ax
mov ah, 9
lea dx, mod_sel_str
int 21h
select_mod: ;选择随机数产生方式
mov ah, 1
int 21h
call print_crlf
cmp al, '1'
je sel_time_seed
cmp al, '2'
je sel_input_seed
mov ah, 9
lea dx, wrong_input_str ;输入错误,重新输入
int 21h
jmp select_mod
sel_time_seed:
call time_seed ;使用时间种子
jmp sel_end
sel_input_seed:
call input_seed ;手动输入种子
sel_end:
;将初始生成的随机种子存到num中:dx(16bit),ax(16bit) -> num(32bit)
mov word ptr num[0], dx
mov word ptr num[2], ax
again:
call guess
call rand ;利用种子(num)产生随机数
mov ah, 9
lea dx, try_again
int 21h
mov ah, 1
int 21h
call print_crlf
cmp al, 'y'
mov ax, word ptr num[2]
je again
exit:
mov ah, 9 ;输出结束
lea dx, exit_str
int 21h
mov ah, 4ch ;程序返回
int 21h
main endp
;=================================================================
;=================================================================
; 封装输入函数,接收一串无符号数字
;=================================================================
input proc
push ax
push si
push bx
mov bl, '0'
xor si, si
mov ah, 01h
in1:
check_input1:
int 21h
cmp al, 27 ;用户键入ESC,退出
jne check_input2
call back
jmp exit
check_input2:
cmp al, 0dh
je end_input ;接收到换行,结束
check_input3:
cmp al, 08H
jne valid_input
or si, 0
je valid_input
dec si
call delete
jmp in1
valid_input:
cmp al, '0' ;只接受'0'-'9'
jae valid_input1
call back
jmp in1
valid_input1:
cmp al, '9'
jbe check_zero
call back
jmp in1
;==================================================================================
; 处理开头是0的情况
;==================================================================================
check_zero: ;已经读取到一个0,又输入一个0,则只保留一个0
cmp si, 1 ;读到一个字符
jne in2
cmp buf[0], bl ;且该字符是'0'
jne in2
cmp al, bl ;输入的也是'0'
jne check_zero2
call back
jmp in1 ;那么直接跳回,重新输入
check_zero2: ;如果输入的不是0,则为大于零的数,去掉第一位的0
call back
call back
dec si ;指针前移,覆盖第一位的0
push ax
push dx
mov ah, 02h
mov dl, al
int 21h ;输出去除0后的数
pop dx
pop ax
;==================================================================================
in2:
cmp si, 5 ;最多输入5个字符
jne store_asci
call back
jmp in1
store_asci:
mov buf[si], al
inc si ;输入字符数加1
jmp in1
end_input:
or si, 0 ;只接收到换行
je in1 ;重新输入
mov cnt, si ;字符个数存cnt
pop bx
pop si
pop ax
ret
input endp
;=================================================================
;=================================================================
; 封装输出函数,打印buf中的数字
;=================================================================
output proc
push cx
push si
push ax
push dx
mov cx, cnt
xor si, si
mov ah, 02h
out1:
mov dl, buf[si]
inc si
int 21h
loop out1
pop dx
pop ax
pop si
pop cx
ret
output endp
;=================================================================
;=================================================================
; 平方取中法(产生小于100000的随机数,存于num)
;=================================================================
rand proc
push ax
push bx
begin_rand:
mul ax
mov al, ah
mov ah, dl
cmp ax, 0
jne rand_next
call time_seed
jmp begin_rand
rand_next:
xor dl, dl
and dh, 1
jz over_rand
mov bx, 34464
div bx
mov ax, dx
mov dx, 1
over_rand:
mov word ptr num[2], ax
mov word ptr num[0], dx
pop bx
pop ax
ret
rand endp
;=================================================================
;=================================================================
; 打印num中存储的32位数字(0 - 2^32-1)
;=================================================================
printNum proc
push ax
push bx
push cx
push dx
begin_print:
xor bx, bx
xor cx, cx
mov bx, 10
mov ax, word ptr num[2]
mov dx, word ptr num[0]
one:
div bx
push dx ;存储余数
inc cx ;位数加一
xor dx, dx
cmp ax, 0
jne one
mov ah, 02h
two:
pop dx
add dx, 30h
int 21h
loop two
pop dx
pop cx
pop bx
pop ax
ret
printNum endp
;=================================================================
;=================================================================
; 输出换行
;=================================================================
print_crlf proc
push ax
push bx
push dx
mov ah, 02h
mov dx, 0dh
int 21h
mov dx, 0ah
int 21h
pop dx
pop bx
pop ax
ret
print_crlf endp
;=================================================================
;=================================================================
; 把buf中的字符转为数字存到dx, ax
;=================================================================
readNum proc
push bx
push cx
push si
push di
mov cx, cnt
xor bx, bx
xor ax, ax
xor si, si
xor dx, dx
mov tmp, bx
mov di, 10
read_next:
mov al, buf[bx] ;从buf中读取一位数字,从高向低读
sub al, 30h
xor ah, ah
inc bx
push cx ;各位数乘以相应的10的次幂
dec cx ;乘10的次数是当前cx的计数值减1
cmp cx, 0
jle skip_mul_10
mul_10:
mul di ;结果不会超过16位
loop mul_10
skip_mul_10:
pop cx
add si, ax
jnc if_c
inc dx
if_c:
cmp dx, 0
je start_loop
mov tmp, dx
start_loop:
loop read_next
mov ax, si
mov dx, tmp
pop di
pop si
pop cx
pop bx
ret
readNum endp
;=================================================================
;=================================================================
; 猜数函数主体
;=================================================================
guess proc
push ax
push dx
push bx
xor bx, bx
mov ah, 9
lea dx, num_str
int 21h
call printNum
call print_crlf
mov ah, 9
lea dx, press_str
int 21h
mov ah, 1
int 21h
call cls
start_guess:
mov ah, 9
lea dx, string
int 21h
call input
call readNum
cmp dx, word ptr num[0]
jb smaller
ja bigger
cmp ax, word ptr num[2]
jb smaller
ja bigger
je correct
bigger:
mov ah, 9
lea dx, big_str
int 21h
inc bx
jmp start_guess
smaller:
mov ah, 9
lea dx, small_str
int 21h
inc bx
jmp start_guess
correct:
mov ah, 9
lea dx, correct_str
int 21h
inc bx
call printNum
call print_crlf
;=====================================
; 输出总尝试次数
;=====================================
push word ptr num[0]
push word ptr num[2]
mov word ptr num[2], bx
xor bx, bx
mov word ptr num[0], bx
lea dx, times_str1
int 21h
call printNum ;输出num中的数字
;=====================================
pop word ptr num[2]
pop word ptr num[0]
lea dx, times_str2
int 21h
pop bx
pop dx
pop ax
ret
guess endp
;=================================================================
;=================================================================
; 清屏
;=================================================================
cls proc
mov ah, 15
int 10h
mov ah, 0
int 10h
ret
cls endp
;=================================================================
;=================================================================
; 获取系统时间产生种子放在dx, ax
;=================================================================
time_seed proc
push cx
;push dx
mov ah, 02h ;ch→hours cl→minutes dh→seconds,均为BCD码格式
int 1Ah
mov dl, dh
add cx, dx
mov ax, cx
;=================================================
; 调整dx,ax使之不超过100000即0001_86A0H
;=================================================
cmp dx, 01h ;如果dx>=1,则需要把dx设为1,而且对ax求模86A1H
jb time_end
mov cx, 086A1H
div cx
mov ax, dx
mov dx, 01h
;=================================================
time_end:
;pop dx
pop cx
ret
time_seed endp
;=================================================================
;=================================================================
; 手动输入随机数种子
;=================================================================
input_seed proc
mov ah, 9
lea dx, seed_in_str
int 21h
call input
; call output
call readNum
ret
input_seed endp
;=================================================================
;=================================================================
; 退格
;=================================================================
;具体操作是先光标左移,再输出空格,再光标左移
back proc
push ax
push bx
push cx
push dx
mov ah, 03h ;读取光标位置
int 10h
dec dl ;左移光标
push dx
mov ah, 02h
int 10h
mov dl, ' ' ;输出空格
int 21h
pop dx
int 10h ;左移光标
pop dx
pop cx
pop bx
pop ax
ret
back endp
;=================================================================
;=================================================================
; 删除
;=================================================================
;具体操作是先输出空格,再光标左移
delete proc
push ax
push bx
push cx
push dx
mov ah, 02h
mov dl, ' ' ;输出空格
int 21h
mov ah, 03h ;读取光标位置->dl
int 10h
mov ah, 02h ;左移光标
dec dl
int 10h
pop dx
pop cx
pop bx
pop ax
ret
delete endp
;=================================================================
end start