Skip to content

Commit

Permalink
slow the game around without adding bytes (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
donno2048 authored Dec 5, 2024
1 parent e6c82c9 commit cc3d089
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 24 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ An **empty** C program generated with `gcc -Os -w -xc - <<< "main;"` on linux-x8
<br/>

```
c53000b80000cd108b3f8d22e5402
1c3300fbbd0077af5e4606bc00ad4
14d5449801c739dfad10257bd9893
a74de880f83eb5079f95b88277bd8
c53800b80000cd108b3789fcba0a2
0e54021c3000fbbd00778f5e460f6
ead414d5449801f039d8ab96d01c7
1d672e029d3880f75fa5b883779db
```
</details>

Expand Down
2 changes: 1 addition & 1 deletion demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ xhr.open('GET', 'snake.com', true);
xhr.responseType = 'arraybuffer';

xhr.onload = _ =>
Dos(canvas, { cycles: 2, onprogress: ()=>{} }).ready((fs, main) =>
Dos(canvas, { cycles: 6, onprogress: ()=>{} }).ready((fs, main) =>
fs.extract(URL.createObjectURL(new Blob([zip(new Uint8Array(xhr.response))]))).then(() =>
main(["main.com"]).then(ci => {
swipedetect(swipedir => swipedir && ci.simulateKeyPress(36 + swipedir));
Expand Down
Binary file modified demo/qr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/snake.com
Binary file not shown.
41 changes: 22 additions & 19 deletions snake.asm
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
; register usage during main loop
; DS: 0xB800, segment of screen buffer
; BX: 0x7D0, screen size (40x25x2 bytes), used in food generation, edge checks, also used for screen accesses but constantly reinitialized
; DI: position of the snake head
; SI: pointer to memory location where the current position of the snake head is stored (actual pointer is BP+SI because it defaults to SS)
lds si, [bx+si] ; SI=0x100 and BX=0x0 at program start in most DOS versions, this initializes DS and SI (machine code at 0x100 is c5 30 00 b8)
; DX: 0x200A, DH used for clearing old snake tiles, DL used for magic IMUL and DX used for wall building
; SI: position of the snake head
; DI: pointer to memory location where the current position of the snake head is stored
; SP: pointer to memory location where the current position of the snake tail is stored
lds di, [bx+si] ; SI=0x100 and BX=0x0 at program start, this initializes DS and DI (machine code at 0x100 is c5 38 00 b8)
db 0x0 ; dummy byte for LDS. this with 'mov ax, 0x0' is actually 'add [bx+si+0x0], bh' but player dies immediately and loop returns to start
start: ; reset game
mov ax, 0x0 ; set video mode (AH=0x00) to mode 0 (AL=0x0), text mode 40x25 16 colors
int 0x10 ; using BIOS interrupt call, also clears the screen
mov di, [bx] ; reset head position, BX always points to a valid screen position containing 0x720 after setting video mode
lea sp, [bp+si] ; set stack pointer (tail) to current head pointer
mov si, [bx] ; reset head position, BX always points to a valid screen position containing 0x720 after setting video mode
mov sp, di ; set SP to current head pointer
mov dx, 0x200A ; DH set to empty space, DL to 0xA, DX satisfies DX*8%0x10000=0x50 so good for the wall building
.food: ; create new food item
in ax, 0x40 ; read 16 bit timer counter into AX for randomization
and bx, ax ; mask with BX to make divisible by 4 and less than or equal to screen size
xor [bx], cl ; place food item and check if position was empty by applying XOR with CL (assumed to be 0xFF)
add [bx], cl ; place food item and check if position was empty by applying ADD with CL (assumed to be 0xFF)
.input: ; handle keyboard input
mov bx, 0x7D0 ; initialize BX
jp .food ; if position was occupied by snake or wall in food generation => try again, if we came from main loop PF=0
js .food ; if position was occupied by snake or wall in food generation => try again, if we came from main loop SF=0
in al, 0x60 ; read scancode from keyboard controller - bit 7 is set in case key was released
imul ax, BYTE 0xA ; we want to map scancodes for arrow up (0x48/0xC8), left (0x4B/0xCB), right (0x4D/0xCD), down (0x50/0xD0) to movement offsets
imul dl ; we want to map scancodes for arrow up (0x48/0xC8), left (0x4B/0xCB), right (0x4D/0xCD), down (0x50/0xD0) to movement offsets
aam 0x14 ; IMUL (AH is irrelevant here), AAM and AAD with some magic constants maps up => -80, left => -2, right => 2, down => 80
aad 0x44 ; using arithmetic instructions is more compact than checks and conditional jumps
cbw ; but causes weird snake movements though with other keys
add di, ax ; add offset to head position
cmp di, bx ; check if head crossed vertical edge by comparing against screen size in BX
lodsw ; load 0x2007 into AX from off-screen screen buffer and advance head pointer
adc [di], ah ; ADC head position with 0x20 to set snake character
jnp start ; if it already had snake or wall in it or if it crossed a vertical edge, PF=0 from ADC => game over
mov [bp+si], di ; store head position, use BP+SI to default to SS
jz .food ; if food was consumed, ZF=1 from ADC => generate new food
add ax, si ; set AX to new head position
cmp ax, bx ; check if head crossed vertical edge by comparing against BX
stosw ; store head position (SS=ES by default) and advance head pointer
xchg ax, si ; save new head position to SI
rcr BYTE [si], 0x1 ; RCR head position to set snake character
jno start ; if it already had snake or wall in it or if it crossed a vertical edge, OF=0 from RCR => game over
jc .food ; if food was consumed, CF=1 from RCR => generate new food
.wall: ; draw an invisible wall on the left side
sub bx, dx ; go one line and 0x2000 bytes backwards (the added bytes wrap nicely as 0x10000%0x2000=0)
mov [bx], cl ; store wall character
sub bx, BYTE 0x50 ; go one line backwards
jns .wall ; jump to draw the next wall
jnz .wall ; jump to draw the next wall
pop bx ; no food was consumed so pop tail position into BX
mov [bx], ah ; clear old tail position on screen
jnp .input ; loop to keyboard input, PF=0 from SUB
mov [bx], dh ; clear old tail position on screen
jns .input ; loop to keyboard input, SF=0 from SUB

0 comments on commit cc3d089

Please sign in to comment.