Snake in 1K

good morning!

i wrote a linux x86-64 FASM snake program using DRM

its a little buggy and not too fun but it fits in 1024 bytes

it might not run on all setups because the DRM code isnt really done properly, just the bare minimum to get it going on my machine

someone who actually knows assembly could fit it in much less though

the "PRNG" is terrible and will deadlock, fixing it has been left as an exercise to the reader :^)

img

fits in a qr code

img

the terrible code (dont look!):

format ELF64 executable 3

GAME_SPEED = 800 * 1000 * 1000 ; 0.8 second
TILE_SIZE = 64
MAX_CONNECTORS = 64
MAX_MODES = 64
MAX_SNEK = 1024

SYS_OPEN = 2
SYS_MMAP = 9
SYS_IOCTL = 16
SYS_NANOSLEEP = 35
SYS_FCNTL = 72
SYS_CLOCK_GETTIME = 228

O_RDWR = 2
O_NONBLOCK = 2048

PROT_READ = 1
PROT_WRITE = 2
MAP_SHARED = 1

TCGETS = 0x5401

F_SETFL = 4

DRM_IOCTL_MODE_GETRESOURCES = 3225445536
DRM_IOCTL_MODE_GETCONNECTOR = 3226494119
DRM_IOCTL_MODE_CREATE_DUMB = 3223348402
DRM_IOCTL_MODE_ADDFB2 = 3228067000
DRM_IOCTL_MODE_MAP_DUMB = 3222299827
DRM_IOCTL_MODE_SETCRTC = 3228066978
DRM_FORMAT_XRGB8888 = 875713112

segment readable executable

; rax = color
; rbx = framebuffer
; rcx = y
; rdx = x
; r13 = pitch
; clobbers rax, rcx, rdi, rsi
fill_tile:
    push rax
    push rdx
    mov rax, rcx
    xor rsi, rsi
    mov sil, TILE_SIZE
    mul rsi
    mul r13
    mov rdi, rbx
    add rdi, rax
    pop rdx
    mov rax, rdx
    mul rsi
    xor rcx, rcx
    mov cl, 4
    mul rcx
    add rdi, rax
    pop rax
.l:
    push rdi
    mov cl, TILE_SIZE
    rep stosd
    pop rdi
    add rdi, r13
    dec rsi
    jnz .l
    xor rax, rax
    xor rdi, rdi
    ret

entry _start
_start:
    mov al, SYS_IOCTL
    mov si, TCGETS
    mov rdx, termios
    syscall

    and byte [rdx + 12], 0xFD
    mov al, SYS_IOCTL
    inc si ; TCSETS
    syscall

    mov al, SYS_FCNTL
    mov si, F_SETFL
    mov rdx, O_RDWR or O_NONBLOCK
    syscall

    mov al, SYS_OPEN
    mov rdi, [rsp + 16]
    mov si, dx
    syscall ; open argv[1] as card

    mov rdi, rax ; save fd
 
    mov rdx, resources
    mov qword [rdx + 8], crtc + 12 ; crtc_id_ptr
    mov qword [rdx + 16], conn_id ; conn_id_ptr
    mov ecx, 1
    mov dword [rdx + 36], ecx ; count_crtcs
    mov dword [rdx + 40], MAX_CONNECTORS ; count_connectors
    mov al, SYS_IOCTL
    mov esi, DRM_IOCTL_MODE_GETRESOURCES
    syscall

    ; loop thru all connectors and find first connected one
    mov rbx, conn_id - 4
    mov rdx, connector - 80
    mov esi, DRM_IOCTL_MODE_GETCONNECTOR
.l:
    add rbx, 4
    add rdx, 80
    mov ecx, dword [rbx] ; conn_id
    mov dword [rdx + 48], ecx ; connector_id
    mov dword [rdx + 32], MAX_MODES ; count_modes
    mov qword [rdx + 8], modes ; modes_ptr
    mov al, SYS_IOCTL
    syscall
    cmp dword [rdx + 60], 1
    jnz .l

    mov rdx, dumb
    mov r12w, word [modes + 4] ; hdisplay
    mov word [rdx + 4], r12w ; width
    mov bp, word [modes + 14] ; vdisplay
    mov word [rdx], bp ; height
    mov dword [rdx + 8], 32
    mov esi, DRM_IOCTL_MODE_CREATE_DUMB
    mov al, SYS_IOCTL
    syscall

    mov r8, rdx
    mov rdx, fb2
    push r12
    mov word [rdx + 4], r12w ; width
    mov word [rdx + 8], bp ; height
    mov dword [rdx + 12], DRM_FORMAT_XRGB8888 ; pixel_format
    mov r12d, dword [r8 + 16] ; handle
    mov dword [rdx + 20], r12d ; handles[0]
    mov r13d, dword [r8 + 20] ; pitch
    mov dword [rdx + 36], r13d ; pitches[0]
    mov al, SYS_IOCTL
    mov esi, DRM_IOCTL_MODE_ADDFB2
    syscall

    mov rdx, map
    mov dword [rdx], r12d ; handle
    mov al, SYS_IOCTL
    mov esi, DRM_IOCTL_MODE_MAP_DUMB
    syscall
    pop r12

    mov rdx, crtc
    mov ecx, dword [fb2] ; fb_id
    mov dword [rdx + 16], ecx ; fb_id
    mov qword [rdx], rbx ; set_connectors_ptr
    mov dword [rdx + 8], 1 ; count_connectors
    mov dword [rdx + 32], 1 ; mode_valid
    mov al, SYS_IOCTL
    mov esi, DRM_IOCTL_MODE_SETCRTC
    syscall

    mov al, SYS_MMAP
    mov rsi, qword [r8 + 24]
    xchg rdi, r8
    mov dl, PROT_READ or PROT_WRITE
    mov r10l, MAP_SHARED
    mov r9, qword [map + 8]
    syscall
    mov rbx, rax ; framebuffer

    xor rax, rax
    not rax
    xor rcx, rcx
    xor rdx, rdx
    mov cl, 6
    mov dl, 6
    call fill_tile

    mov byte [char], 'd'
    xor r14, r14
    xor r8, r8
    xor r10, r10
    inc r10
    mov r14l, 6
    mov r15, r14
    mov r8l, 9
    mov r9, r8

.game:
    mov al, SYS_CLOCK_GETTIME
    mov rsi, timespec_start
    xor rdi, rdi
    syscall

    push rsi
.input:
    xor al, al ; SYS_READ
    mov rsi, char
    mov rdx, 1
    syscall
    test rax, rax
    jg .input
    
    push r14
    push r15
    mov sil, [char]
    cmp sil, 'w'
    jz .up
    cmp sil, 's'
    jz .down
    cmp sil, 'a'
    jz .left
    cmp sil, 'd'
    jz .right
    jmp .exit

.up:
    dec r14
    jmp .d
.down:
    inc r14
    jmp .d
.left:
    dec r15
    cmp r15, 0
    jl .exit
    jmp .d
.right:
    inc r15
    mov ax, r15w
    inc ax
    xor cx, cx
    mov cl, TILE_SIZE
    mul cx
    cmp ax, r12w
    jg .exit

.d:
    cmp r14, r8
    jnz .d2
    cmp r15, r9
    jnz .d2
    inc r10

    mov dil, TILE_SIZE
    mov rax, r12
    xor rdx, rdx
    div rdi
    mov rcx, rax
    mov rax, rbp
    xor rdx, rdx
    div rdi
    mov rdi, rax

    ; rcx = maxTilesX, rdi = maxTilesY

    mov rax, 123456791
    push rax
    mul r9
    xor rdx, rdx
    div rcx
    pop rax
    mov r9, rdx
    mul r8
    xor rdx, rdx
    div rdi
    mov r8, rdx

    xor rax, rax
    not rax
    pop rdx
    pop rcx
    push rcx
    push rdx
    call fill_tile

.d2:
    xor rcx, rcx
    pop r11 ; ox
    pop rdx ; oy
.ld2:
    cmp rcx, r10
    je .d3

    lea rsi, [snek + rcx * 2]

    cmp r14, rdx
    jnz .nd
    cmp r15, r11
    jnz .nd
    jmp .exit

.nd:

    xchg byte [rsi], r11l
    xchg byte [rsi + 1], dl

    inc rcx
    jmp .ld2
.d3:
    xor rax, rax
    mov rcx, rdx
    mov rdx, r11
    call fill_tile

    not rax
    mov rcx, r14
    mov rdx, r15
    call fill_tile

    not al
    shl rax, 16
    mov rcx, r8
    mov rdx, r9
    call fill_tile
    pop rsi

    mov al, SYS_CLOCK_GETTIME
    add rsi, 16 ; timespec_end
    syscall

    mov rax, qword [rsi] ; timespec_end.tv_sec
    sub rax, qword [rsi - 16] ; timespec_start.tv_sec
    mov rcx, 1000 * 1000 * 1000
    mul rcx
    add rax, qword [rsi + 8] ; timespec_end.tv_nsec
    sub rax, qword [rsi - 8] ; timespec_start.tv_nsec
    mov rcx, GAME_SPEED
    sub rcx, rax
    mov qword [rsi + 24], rcx ; timespec_sleep.tv_nsec

    xor rax, rax
    mov al, SYS_NANOSLEEP
    lea rdi, [rsi + 16] ; timespec_sleep
    xor rsi, rsi
    syscall
    jmp .game

.exit:
    xor rax, rax
    mov rax, [rax]
    dd 0x69696969
    dw 0x6969

segment readable writeable
resources rb 64
conn_id rd MAX_CONNECTORS
connector rb 80 * MAX_CONNECTORS
dumb rb 32
fb2 rb 104
map rd 16
crtc rb 36
modes rb 69 * MAX_MODES
timespec_start rq 2
timespec_end rq 2
timespec_sleep rq 2
termios rb 60
char rb 1
snek rb MAX_SNEK * 2

i oughta fix my syntax highlighting on this