汇编(DosBox)贪吃蛇游戏:如何基于定时器生成伪随机苹果
贪吃蛇游戏:用系统定时器生成苹果伪随机数的入门讲解
我正在为学校制作贪吃蛇游戏,需要用伪随机数生成苹果的位置。教材指出要基于定时器、使用内存地址0040:006Ch实现,但我搞不懂它的讲解方式,希望有人用最简单的方式解释。我是汇编入门新手,使用DosBox环境,当前代码仅实现了用WASD控制'*'移动及边界限制,代码如下:
IDEAL MODEL small STACK 100h DATASEG ; -------------------------- ; Your variables here ; -------------------------- saveal db ' ' CODESEG proc black body: mov [es:si], ax add si, 2 cmp si, 25*80*2 jnz body ret endp black proc up mov di, 80*2 cmp si, di jb not_move_up mov ah, 0 mov al, ' ' mov [es:si], ax sub si, 80*2 mov ah, 156 mov al, '*' mov [es:si], ax not_move_up: ret endp up proc down mov di, (24*80*2)-1 cmp si, di jg not_move_down mov ah, 0 mov al, ' ' mov [es:si], ax add si, 80*2 mov ah, 156 mov al, '*' mov [es:si], ax not_move_down: ret endp down proc left mov dx, 0 mov bx, si mov ax, si mov si, 80*2 div si mov si, bx cmp dx,0 jz not_move_left mov ah, 0 mov al, ' ' mov [es:si], ax sub si, 2 mov ah, 156 mov al, '*' mov [es:si], ax not_move_left: ret endp left proc right mov dx, 0 mov bx, si mov ax, si mov si, 80*2 div si mov si, bx cmp dx,158 jz not_move_right mov ah, 0 mov al, ' ' mov [es:si], ax add si, 2 mov ah, 156 mov al, '*' mov [es:si], ax not_move_right: ret endp right start: mov ax, @data mov ds, ax ; -------------------------- ; Your code here ; -------------------------- mov ax, 0b800h mov es, ax mov si,0 mov al, ' ' mov ah, 0 call black mov si, (12*80+40)*2 mov al, '*' mov ah, 156 mov [es:si], ax yesOrno: mov ah, 1h int 21h mov [byte ptr saveal], al cmp [byte ptr saveal], 'w' jz w cmp [byte ptr saveal], 'a' jz a cmp [byte ptr saveal], 's' jz s cmp [byte ptr saveal], 'd' jz d cmp [byte ptr saveal], 'q' jmp exit w: call up jmp yesOrno s: call down jmp yesOrno a: call left jmp yesOrno d: call right jmp yesOrno exit: mov ax, 4c00h int 21h END start
一、为什么用0040:006Ch?
- 这个内存地址是DOS系统里系统定时器的计时值,是个16位的数,会以每秒约18.2次的速度自动递增,涨到65535后循环归零。
- 它的变化完全无规律(对我们的程序来说),所以拿来当伪随机数的“种子”刚好——每次读它的值,都能得到一个看起来随机的数。
二、怎么用它生成苹果位置?
1. 读取定时器数值
要访问这个远地址,用这段代码:
mov ax, 0040h ; 定时器所在的段地址是0040h mov es, ax ; 把段地址加载到es寄存器 mov ax, [es:006Ch] ; 读取0040:006Ch的16位值到ax
现在ax里就是当前的随机源数值。
2. 转换成合法的屏幕坐标
屏幕是25行×80列,行号范围024,列号范围079,用除法取余数把随机数压缩到这个范围:
生成随机行号:
mov bx, 25 ; 总行数 div bx ; ax除以bx,dx里就是余数(0~24,刚好是合法行号) mov apple_row, dx ; 把行号存到自定义变量里
生成随机列号:
mov ax, [es:006Ch] ; 可以再读一次定时器,或者把之前的ax做个小变换(比如ax=ax+100),避免行和列完全绑定 mov bx, 80 ; 总列数 div bx ; dx里是余数(0~79,合法列号) mov apple_col, dx ; 存列号
注意:要在DATASEG里先定义apple_row db ?和apple_col db ?这两个变量
3. 绘制苹果到屏幕
显存段是0B800h,每个字符占2字节(字符+属性),计算偏移量后写入苹果字符:
mov ax, 0B800h mov es, ax mov ax, apple_row mov bx, 80 mul bx ; ax = 行号×80 add ax, apple_col ; ax = 行号×80 + 列号 shl ax, 1 ; ax = 上面的结果×2(每个字符占2字节) mov si, ax ; si指向苹果的显存位置 mov al, 'O' ; 苹果用'O'表示,你可以换其他字符 mov ah, 127 ; 苹果的显示属性(亮白色,可自行调整) mov [es:si], ax ; 把苹果写到屏幕上
三、额外注意
- 生成苹果后要检查位置是否和蛇身重叠,如果重叠就重新生成一次。
- 不要在极短时间内连续读取同一个定时器值生成行和列,否则行和列会有固定关联,随机性变差。
内容的提问来源于stack exchange,提问作者yarden
相关产品推荐
相关产品推荐

