📄 snake.asm
字号:
;; snake.asm: Copyright (C) 2001 by Brian Raiter, under the GNU;; General Public License. No warranty.;;;; To build:;; nasm -f bin -o snake snake.asm && chmod +x snake;;;; Use 4, n, or the left-arrow key to turn the snake left.;; Use 5, m, or the right-arrow key to turn the snake right.;; Eating the blocks adds to your score and makes you grow longer.;; Avoid the walls and your own tail.;;;; Note that this program assumes that it is running on a terminal;; that has the VT100 line-drawing characters as the alternate;; character set. If you're playing on a Linux console and you see a;; bunch of lowercase letters instead of lines, try typing "echo -e;; '\e(B\e)0'" before starting the game. If that doesn't help, or if;; instead you see a jumble of non-ASCII characters, you may need to;; switch to a console font with the full VT100 character set in order;; to play.BITS 32;; Macros for accessing addresses in .text and .bss via ebp.%define DATAOFF(addr) byte ebp + ((addr) - score);; The system calls used by this program.%define SC_exit 1 ; exit(bl)%define SC_read 3 ; eax = read(ebx, ecx, edx)%define SC_write 4 ; eax = write(ebx, ecx, edx)%define SC_pause 29 ; eax = pause()%define SC_ioctl 54 ; eax = ioctl(ebx, ecx, edx)%define SC_sigaction 67 ; eax = sigaction(ebx, ecx, edx)%define SC_gettimeofday 78 ; eax = gettimeofday(ebx, ecx)%define SC_setitimer 104 ; eax = setitimer(ebx, ecx, edx)%define SC_select 142 ; eax = select(ebx, ecx, edx, esi, edi);; ioctl values%define TCGETS 0x5401%define TCSETS 0x5402%define ICANON 0000002%define ECHO 0000010;; signal values%define SIGALRM 14;; ASCII character names%define NL 012q%define FF 014q%define SO 016q%define SI 017q%define ESC 033q%define CSI 233q;; How the program tracks directions. (These values are also ORed;; together to indicate the various VT-100 line-drawing characters.)%define NORTH 1%define EAST 2%define SOUTH 4%define WEST 8;; Dimensions of the screen and the playing area.%define WIDTH 78%define HEIGHT 24%define CENTERPOINT ((HEIGHT - 1) / 2) * WIDTH + (WIDTH - 1) / 2%define RIGHTCOL '79'%define BOTTOMROW '24';; The number of turns it takes for the food to decay.%define FOOD_HALFLIFE 42;; Indices for messages to the user.%define helpmsgcode 51%define endmsgcode 91%define filledmsgcode 101%define ouromsgcode 135%define philosmsgcode 172%define poetrymsgcode 209%define origin 0x08048000;; The ELF header and program segment table. org originehdr: ; Elf32_Ehdr db 0x7F, "ELF", 1, 1, 1 ; e_ident times 9 db 0 dw 2 ; e_type dw 3 ; e_machine dd 1 ; e_version dd _start ; e_entry dd phdr - $$ ; e_phoff dd 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsizephdr: ; Elf32_Phdr dw 1 ; e_phnum ; p_type dw 0 ; e_shentsize dw 0 ; e_shnum ; p_offset dw 0 ; e_shstrndxehdrsize equ $ - ehdr dd $$ ; p_vaddr dd $$ ; p_paddr dd filesz ; p_filesz dd memsz ; p_memsz dd 7 ; p_flags dd 0x1000 ; p_alignphdrsize equ $ - phdr;; getkey retrieves a single character from standard input if one is;; waiting to be read.;;;; input:;; ecx = pointer to a byte in memory at which to store the character.;;;; output:;; al = the contents of [ecx].;; [ecx] = the character input, or zero if no character was available,;; or 'q' if standard input could not be read.;; eax, ebx, and edx are altered.getkey:;; Call select, waiting on the first file descriptor only, and setting;; the time to wait to zero. If select returns an error, return 'q' to;; the caller. If select returns zero, return zero to the caller.;; Otherwise, call read. xor eax, eax xor ebx, ebx cdq mov [ecx], al pusha inc ebx mov ecx, fdset mov [ecx], ebx xor esi, esi lea edi, [DATAOFF(timer)] mov [edi], eax mov [byte edi + 4], eax mov al, 142 int 0x80 dec eax popa jl .return js .retquit mov al, SC_read inc edx int 0x80;; If read returned zero (i.e., EOF) or an error, return 'q' to the;; caller. Otherwise, return the retrieved character. dec eax jz .return.retquit: mov byte [ecx], 'q'.return: mov al, [ecx] ret;; zmove appends a cursor-positioning VT-100 escape sequence to the;; given buffer.;;;; input:;; eax = the zero-based coordinates to move the cursor to, in the form;; y * WIDTH + x.;; edi = the buffer to write the escape sequence to.;;;; output:;; edi = the position following the escape sequence.;; eax and edx are altered.zmove: mov dl, WIDTH div dl push eax inc eax aam bswap eax add eax, ESC | ('[00' << 8) stosd pop eax mov al, ah inc eax aam bswap eax shr eax, 8 add eax, ';00H' stosd ret;; addvtchar appends a VT-100 cursor-position escape sequence followed;; by a line-drawing character to the given buffer.;;;; eax = the zero-based coordinates to move the cursor to.;; dl = the line-drawing character, given as a combination of;; direction bit flags.;; edi = the buffer to write the escape sequence to.;;;; output:;; edi = the position following the line-drawing character.;; eax, ebx, and edx are altered.addvtchar: push edx mov [DATAOFF(scr) + eax], dl call zmove pop eax mov edx, eax lea ebx, [DATAOFF(vtlines)] xlatb stosb ret;; refresh writes the contents of outbuf to standard output.;;;; input:;; edi = one past the last character in outbuf to write.;;;; output:;; edi = outbuf.;; eax, ebx, ecx, and edx are altered.refresh: mov eax, ESC | ('[' << 8) | (BOTTOMROW << 16) stosd mov eax, ';0H' | (SI << 24) stosd mov edx, edi mov edi, outbuf mov ecx, edi sub edx, ecx xor ebx, ebx lea eax, [byte ebx + SC_write] inc ebx int 0x80tick: ret;; The program begins here._start:;; ebp is set to point to an address near the top of the .bss;; section. It will retain this value throughout the program. mov ebp, score;; The attributes of the tty connected to standard input are;; retrieved, and then canonical-mode and input-echoing are turned;; off. This puts the terminal in a mode similar to what ncurses calls;; cbreak mode. The original attributes are remembered so that they;; can be restored at the end. lea edx, [DATAOFF(termios)] mov ecx, TCGETS xor ebx, ebx lea eax, [byte ebx + SC_ioctl] int 0x80 mov eax, [DATAOFF(termios.lflag)] push eax and eax, byte ~(ICANON | ECHO) mov [DATAOFF(termios.lflag)], eax inc ecx lea eax, [byte ebx + SC_ioctl] int 0x80 pop dword [DATAOFF(termios.lflag)];; A do-nothing signal hander is installed for SIGALRM, and then an;; interval timer is set up to go off every 0.2 seconds. lea eax, [byte ebx + SC_sigaction] cdq mov bl, SIGALRM lea ecx, [DATAOFF(sigact)] int 0x80 lea ecx, [DATAOFF(timer)] mov eax, 200 * 1000 mov [byte ecx + 4], eax mov [byte ecx + 12], eax cdq xor ebx, ebx lea eax, [byte ebx + SC_setitimer] int 0x80;; The scr buffer is initialized by adding the outer walls of the;; playing field. lea edi, [DATAOFF(scr)] mov al, SOUTH | EAST stosb mov al, EAST | WEST push byte WIDTH - 2 pop ecx rep stosb mov al, SOUTH | WEST stosb mov esi, edi mov al, NORTH | SOUTH stosb add edi, byte WIDTH - 2 stosb mov cl, ((HEIGHT - 3) * WIDTH) & 0xFF mov ch, ((HEIGHT - 3) * WIDTH) >> 8 rep movsb mov al, NORTH | EAST stosb mov al, EAST | WEST mov cl, WIDTH - 2 rep stosb mov byte [edi], NORTH | WEST;; edi is initialized to point to the output buffer. It will continue;; to point into this buffer for the rest of the program. mov edi, outbuf;; The snake is set to begin in the middle of the playing field, and;; is set to extend by one unit. A food block is placed there as well,;; but having already expired so that the program will create a new;; food block on the first iteration of the main loop. mov eax, CENTERPOINT mov [snake], eax mov byte [DATAOFF(scr) + eax], EAST | WEST mov [DATAOFF(foodpos)], eax;; The current time is used to initialize our pseduorandom-number;; generator, and is also used to select between EAST and WEST as the;; snake's starting direction of movement. A Ctrl-L keystroke is;; inserted into the input queue, so that the program will redraw;; everything on the first iteration of the main loop. lea ebx, [DATAOFF(rndseed)] lea eax, [byte ecx + SC_gettimeofday] int 0x80 add eax, [ebx] mov al, WEST jp .gowest mov al, EAST.gowest: mov [DATAOFF(dir)], al mov byte [DATAOFF(key + 1)], FF;; The main loop, where the program will remain until the game ends.mainloop:;; Examine the input queue. If a keystroke is waiting in there, then;; remove it and use it as this iteration's keystroke. (If two;; keystrokes are waiting there, remove the first one and move the;; second one into its position.) lea ecx, [DATAOFF(key)] mov al, [byte ecx + 1] or al, al jz .readkey xor edx, edx cmp dl, [byte ecx + 2] jz .retlast xchg dl, [byte ecx + 2].retlast: mov [byte ecx + 1], dl.retkey: mov [ecx], al jmp short .endkeys;; Otherwise, check for an incoming character. If anything besides ESC;; is returned (including zero, indicating no keys have been pressed),;; then proceed with that key. Otherwise, check for a second;; character. If anything besides '[' or 'O' is returned, then put the;; character in the input queue and proceed with the ESC. Otherwise,;; check for a third character. If anything besides 'C' or 'D' is;; returned, then put it and the previous character into the input;; queue and proceed with the ESC. Otherwise, replace the arrow-key;; sequence with an 'm' or an 'n', as appropriate..readkey: call getkey cmp al, ESC jnz .endkeys inc ecx call getkey cmp al, '[' jz .getthird cmp al, 'O' jnz .endkeys.getthird: inc ecx call getkey cmp al, 'C' jz .acceptarrow cmp al, 'D' jnz .endkeys.acceptarrow: add al, 'm' - 'C' movzx eax, al mov [byte ecx - 2], eax.endkeys:;; If a 'q' was retrieved, exit the program at once. If a Ctrl-L was;; retrieved, then redraw the screen. cmp al, 'q' jz near leavegame cmp al, FF jnz .continue;; The screen is erased, and the VT-100 line-drawing character set is;; selected. The scr array is read, and each element is translated;; into one of the line-drawing characters and output, with a newline;; inserted at the end of each row. Finally, the normal character set;; is re-selected, and the program jumps to the routine to display the;; current score. mov eax, ESC | ('[H' << 8) | (ESC << 24) stosd mov eax, '[J' | (SO << 16) | (SO << 24) stosd lea esi, [DATAOFF(scr)] lea ebx, [DATAOFF(vtlines)] push byte HEIGHT pop ecx.initoutloop: mov ch, WIDTH.lineoutloop: lodsb xlatb stosb dec ch jnz .lineoutloop dec ecx jz .outloopend mov al, 10 stosb jmp short .initoutloop.outloopend: mov al, SI
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -