前言
一直没想过要去学习汇编,觉得需要用汇编的场合无非三种:
1. 与硬件结合很紧密高级语言做不到
2. 时空效率要求甚高算法层面已不能优化到
3. 逆向破解等只能用某些途径看其汇编指令
其余情况下,用汇编无异于有炮不用偏用鸟枪。
本来准备等自己需求以上场合时再去学,但因为学校举办的汇编比赛,计算机学院都要参加,也算是一个由头,于是开始了我的第一个汇编程序。
运行效果
代码只有400行,功能很简略,实现了最基本的游戏流程,看运行效果
概述
本质上用汇编去写一个贪吃蛇和用其他语言去实现没什么区别,不过是换一种方式表达,当然,是一种更加麻烦的方式。
以前用c++实现过C++贪吃蛇和C++俄罗斯方块。所以对于这类小游戏,该怎么切是知道了,剩下的事就是找家伙了。
要实现这类简易的小游戏,需要二点:
1. 定时器
2. 非阻塞接收按键
有上面两者便可以搞定一切。
但在汇编下,为这两点,耽搁了不少时间。不对,是三点,还有生成随机数(原本很容易的事都变得麻烦了起来)。
定时器
事实上,查了很多资料,但还是不知道怎样去实现定时器,所以这个需求只能变通的解决。
用空循环来控制时间。
非阻塞接受按键
getInput:
mov al, 0
mov ah, 1
int 16h;查看键盘缓冲区是否有数据
cmp ah, 1;ah为0表示有,否则没有
je getInputEnd
mov al, 0
mov ah, 0
int 16h;从缓冲区读取数据
;此时值已经在ax里了,做你想做的
getInputEnd:
ret
随机数
mov ax, 0h;间隔定时器
out 43h, al;通过端口43h
in al, 40h;
in al, 40h;
in al, 40h;访问3次,保证随机性
;如果对范围并不需要精确,可以直接与运算来获得,
;否则,用div取余
mov bl, 18
div bl
mov al, ah
mov ah, 0;此时ax的值就是0~18的
代码
assume cs:code,ds:data,ss:stack
data segment
sfood dw 0
sdct dw 0
sbody dw 400 dup(0)
data ends
stack segment
dw 20 dup(0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 20
mov ax, 0b800h
mov es, ax
mov si, 4 ;保存数据段偏移量
mov cx, 4;初始蛇长度
mov ax, 0A04h
s:;初始化蛇
mov ds:[si], ax
add si, 2
inc al
loop s
mov ax, 4d00h;向右
mov ds:[2], ax;初始化方向
call clearBg;清屏
call outBg;输出地图边框
call outBody;输出蛇
call creatFood;输出食物
mov cx, 30
Game:
push cx
call updateBody
mov cx, 0Fh
aaaa1:
push cx
mov cx, 0FFFh
bbbb:
push cx
call getInput
pop cx
loop bbbb
pop cx
loop aaaa1
;jmp GameEnd
jmp Game
GameEnd: ;退出
;call outBody
mov ax, 4c00h
int 21h
updateBody:
mov ax, ds:[2]
mov di, si
sub di, 2
cmp ax, 4800h
je shang
mov ax, ds:[2]
cmp ax, 5000h
je xia
mov ax, ds:[2]
cmp ax, 4b00h
je zuo
mov ax, ds:[2]
cmp ax, 4d00h
je you
shang:
mov ax, ds:[di]
sub ah, 1
jmp checkBody
xia:
mov ax, ds:[di]
add ah, 1
jmp checkBody
zuo:
mov ax, ds:[di]
sub al, 1
jmp checkBody
you:
mov ax, ds:[di]
add al, 1
;mov ds:[di], ax
jmp checkBody
checkBody:
push ax
;判断蛇头是否碰到地图边界
cmp ah, 0
je GameEnd
cmp ah, 20
je GameEnd
cmp al, 0
je GameEnd
cmp al, 20
je GameEnd
;判断蛇头是否碰到蛇身
mov cx, si
sub cx, 6
mov di, 4
s0:
mov bx, ds:[di]
cmp bx, ax
je GameEnd
add di, 2
sub cx, 1
loop s0
pop ax
;判断蛇头是否吃到食物
mov bx, ds:[0]
cmp ax, bx
je addBody
jmp updateStart
updateStart:
mov cx, si
sub cx, 6
mov di, 4
push ax
mov dl, ' ';字符
mov dh, 0;颜色
mov bx, ds:[di]
call outStr
s5:
mov dx, ds:[di+2]
mov ds:[di], dx
add di, 2
sub cx, 1
loop s5
mov dl, ' ';字符
mov dh, 71h;颜色
mov bx, ds:[di]
call outStr
pop ax
mov ds:[di], ax
mov dl, ' ';字符
mov dh, 44h;颜色
mov bx, ds:[di]
call outStr
updateEnd:
ret
addBody:
mov dl, ' ';字符
mov dh, 71h;颜色
mov bx, ds:[di]
call outStr
mov ax, ds:[0]
mov ds:[si], ax
mov dl, ' ';字符
mov dh, 44h;颜色
mov bx, ds:[si]
call outStr
add si, 2
call creatFood
jmp updateEnd
getInput:;获取键盘输入
mov al, 0
mov ah, 1
int 16h;接收键盘
cmp ah, 1
je getInputEnd
mov al, 0
mov ah, 0
int 16h;
mov cx, ax;键盘值在ax
;判断输入
cmp cx, 4800h
je gshang
cmp cx, 5000h
je gxia
cmp cx, 4b00h
je gzuo
cmp cx, 4d00h
je gyou
jmp getInputEnd
gshang:
mov ax, ds:[2]
cmp ax, 5000h
je getInputEnd
jmp fx
gxia:
mov ax, ds:[2]
cmp ax, 4800h
je getInputEnd
jmp fx
gzuo:
mov ax, ds:[2]
cmp ax, 4d00h
je getInputEnd
jmp fx
gyou:
mov ax, ds:[2]
cmp ax, 4b00h
je getInputEnd
jmp fx
fx:;更改方向标志
mov ds:[2], cx
getInputEnd:
ret;结束
outBody:
mov cx, si
sub cx, 6
mov di, 4
s1:
mov ax, ds:[di]
mov dl, ' ';字符
mov dh, 71h;颜色
mov bl, al;列
mov bh, ah;行
call outStr
add di, 2
sub cx, 1
loop s1
mov dl, ' '
mov dh, 44h
mov ax, ds:[di]
mov bl, al
mov bh, ah
call outStr
ret
outBg:
mov dl, ' ';字符
mov dh, 71h;颜色
mov bl, 0;列
mov bh, 0;行
mov cx, 20
row:
push cx
push bx
call outStr;上边界
pop bx
push bx
add bh, 20
call outStr;下边界
pop bx
inc bl;列加1
pop cx
loop row
mov bl, 0;
mov bh, 0;
mov cx, 21
col:
push cx
push bx
call outStr;左边界
pop bx
push bx
add bl, 20
call outStr;右边界
pop bx
inc bh;行加1
pop cx
loop col
ret
outStr: ;在指定位置输出字符
mov al, 80
mul bh;行乘以80
mov bh, 0
add bl, bl;
add ax, bx;加上列即为偏移量
push si
mov si, ax
add si, si
mov es:[si], dl
mov es:[si+1], dh
mov es:[si+2], dl
mov es:[si+3], dh
pop si
ret
creatFood:;生成食物
call getFoodPosition
mov dl, ' '
mov dh, 071h
mov bx, ds:[0]
call outStr
ret
getFoodPosition:;获取食物位置
f1:
call getRand
mov ds:[0], al
call getRand
mov ds:[1], al
mov cx, si
sub cx, 4
mov di, 4
s11:
mov ax, ds:[di]
cmp ax, ds:[0]
je f1
add di, 2
sub cx, 1
loop s11
ret
getRand:;获取随机数 范围1~19
mov ax, 0h;间隔定时器
out 43h, al;通过端口43h
in al, 40h;
in al, 40h;
in al, 40h;访问3次,保证随机性
mov bl, 18
div bl
mov al, ah
mov ah, 0
inc al
ret
clearBg: ;清屏
mov ax, 3h
int 10h
ret
code ends
end start