├── .gitattributes ├── .gitignore ├── README.md ├── assets ├── exit.xcf ├── gem.xcf ├── rock.xcf ├── soil.xcf ├── sprites.svg └── wall.xcf ├── build.sh ├── data ├── exit.bmp ├── font.bmp ├── gem.bmp ├── player.bmp ├── rock.bmp ├── soil.bmp └── wall.bmp ├── src ├── draw.asm ├── levels.asm ├── main.asm └── util.asm └── tools └── cleanbmp ├── .gitignore ├── CMakeLists.txt ├── README.md ├── include └── cpputils │ ├── array.hpp │ ├── bitmap.hpp │ └── exception.hpp └── src ├── bitmap.cpp └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bmp filter=lfs diff=lfs merge=lfs -text 2 | assets/** filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gems'n'Rocks 2 | ============ 3 | 4 | A BoulderDash-like game written in x86_64 assembly without any libraries (not even the C standard lib), just using the linux system call interface. Drawing to the screen is done by writing directly to /dev/fb0. 5 | 6 | Build 7 | ----- 8 | 9 | ### Prerequisites 10 | 11 | You need to have git-lfs installed to get the game's data files. If you installed git-lfs after cloning the repo, make sure to do a `git lfs pull`. The game will crash if the data files aren't present. 12 | 13 | Download and build nasm from source and place on your PATH or install globally. I use nasm version 2.16.03. There is 14 | a bug in 2.15 that makes debugging difficult. 15 | 16 | ### Compile 17 | 18 | ``` 19 | ./build.sh 20 | ``` 21 | 22 | Run the application 23 | ------------------- 24 | 25 | The app needs permission to write to /dev/fb0. Add your user to the video group. 26 | 27 | ``` 28 | sudo adduser $USER video 29 | ``` 30 | 31 | Before running the app, switch to TTY mode with ctrl + alt + f1. You can switch between TTYs with alt + arrow keys. 32 | 33 | Run the application from the project directory. 34 | 35 | ``` 36 | ./build/gems 37 | ``` 38 | 39 | The app works best on a screen resolution of 1920x1080. -------------------------------------------------------------------------------- /assets/exit.xcf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3d7fde9d9efe5fa5578dc227a4641bf601dfb88cb0f4fa8ab0f0128f24ef1793 3 | size 91334 4 | -------------------------------------------------------------------------------- /assets/gem.xcf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:944e1f83ee56b817ad76d9bb72a732b4bad23cc8f3b0f3bda932966eb5f0fa4a 3 | size 180781 4 | -------------------------------------------------------------------------------- /assets/rock.xcf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:afd56e5c53b3d3cd0664726d0c8e7d6fcaa8748afc3f8ae3b4e05820c07d9d95 3 | size 178063 4 | -------------------------------------------------------------------------------- /assets/soil.xcf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2d5cf3f258e3c40f3d2ad75a3aa75ed393d5b40c25f94046b1cc4f5a1822dfb8 3 | size 53129 4 | -------------------------------------------------------------------------------- /assets/sprites.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:704d4fc0106eb51e3c50c2d4a1e9b486a472b86ec6a154c849dbbe6f9fed411b 3 | size 791566 4 | -------------------------------------------------------------------------------- /assets/wall.xcf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0735522d92bb3b9ffb68f61563aa1cfb91d1d98e2cf5fc32b87361085e0b48a0 3 | size 12961 4 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | mkdir -p build 2 | 3 | nasm -f elf64 -gdwarf -o build/util.o src/util.asm 4 | nasm -f elf64 -gdwarf -o build/draw.o src/draw.asm 5 | nasm -f elf64 -gdwarf -o build/levels.o src/levels.asm 6 | nasm -f elf64 -gdwarf -o build/main.o src/main.asm 7 | 8 | ld -o build/gems build/util.o build/draw.o build/main.o build/levels.o 9 | -------------------------------------------------------------------------------- /data/exit.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:70b8de05d2f45083484fee3e2830b711ea9438e4246868b30d891b4d5bdb932c 3 | size 262198 4 | -------------------------------------------------------------------------------- /data/font.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5eefa7cfbdfc78b495b0bd455362774c38205bd4ed7dec507197c92088770249 3 | size 524214 4 | -------------------------------------------------------------------------------- /data/gem.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a451f37715c34c52d6d2b665745ceab45c38ae59977eb39097a4a168148cca09 3 | size 393270 4 | -------------------------------------------------------------------------------- /data/player.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f19b0d4ce4e404044d19fa246cf627198c36e9296b69021bef1b86e440055dfb 3 | size 786486 4 | -------------------------------------------------------------------------------- /data/rock.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5d0d7f319b4d0aa63bf727290089118de7c916c0df902b1014cc68002cb9a30a 3 | size 262198 4 | -------------------------------------------------------------------------------- /data/soil.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:27b13d44d1b3db2a1c5ddc31187a50362fe8437cb54bd8ade0dedd87a0f4408e 3 | size 131126 4 | -------------------------------------------------------------------------------- /data/wall.bmp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:41bd6bb71f74eaf9bd92d3064434bb051f387b61a61341eb3cf46ff642453bf8 3 | size 16438 4 | -------------------------------------------------------------------------------- /src/draw.asm: -------------------------------------------------------------------------------- 1 | SECTION .data 2 | 3 | %define FONT_CHAR_W 30 4 | %define FONT_CHAR_H 48 5 | %define SPACE_CHAR_CODE_PT 32 6 | 7 | drw_fb_path db '/dev/fb0', 0 8 | drw_fbfd dd 0 9 | drw_fb_w dd 0 10 | drw_fb_h dd 0 11 | drw_buf dq 0 12 | 13 | SECTION .text 14 | 15 | global drw_fb_w 16 | global drw_fb_h 17 | global drw_init 18 | global drw_term 19 | global drw_draw 20 | global drw_load_bmp 21 | global drw_fill 22 | global drw_darken 23 | global drw_draw_text 24 | global drw_flush 25 | 26 | extern util_alloc 27 | 28 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | drw_init: 30 | ; Initialise the draw system 31 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 | ; Open /dev/fb0 and get a file descriptor 33 | mov rax, 2 ; sys_open 34 | lea rdi, [rel drw_fb_path] 35 | mov rsi, 2 ; O_RDWR 36 | mov rdx, 0 ; flags 37 | syscall 38 | mov [drw_fbfd], eax 39 | 40 | sub rsp, 160 41 | 42 | ; Get screen resolution 43 | mov rax, 16 ; sys_ioctl 44 | mov rdi, [drw_fbfd] 45 | mov rsi, 0x4600 ; FBIOGET_VSCREENINFO 46 | mov rdx, rsp 47 | syscall 48 | 49 | mov r11d, [rsp] 50 | mov [drw_fb_w], r11d ; width 51 | mov r11d, [rsp + 4] 52 | mov [drw_fb_h], r11d ; height 53 | 54 | ; Allocate space for screen buffer 55 | mov edi, [drw_fb_w] 56 | mov r11d, [drw_fb_h] 57 | imul rdi, r11 58 | shl rdi, 2 59 | call util_alloc 60 | mov [drw_buf], rax 61 | 62 | add rsp, 160 63 | 64 | ret 65 | 66 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 67 | drw_term: 68 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 69 | mov edi, [drw_fbfd] 70 | mov rax, 3 ; sys_close 71 | 72 | ret 73 | 74 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 75 | drw_load_bmp: 76 | ; Load a .bmp file into the given buffer 77 | ; 78 | ; rdi path 79 | ; rsi buffer 80 | ; rdx w 81 | ; rcx h 82 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 83 | push rsi 84 | push rdx 85 | push rcx 86 | 87 | ; Open file 88 | mov rax, 2 ; sys_open 89 | mov rsi, 0 ; O_RDONLY 90 | mov rdx, 0 ; flags 91 | syscall 92 | mov rdi, rax ; file descriptor 93 | 94 | pop rcx ; h 95 | pop rdx ; w 96 | pop rsi ; buffer 97 | 98 | ; Store width and height at beginning of buffer 99 | mov [rsi], edx 100 | add rsi, 4 101 | mov [rsi], ecx 102 | add rsi, 4 103 | 104 | ; Load data from file 105 | mov rdx, rdx ; w 106 | imul rdx, rcx ; w * h 107 | shl rdx, 2 ; num bytes to read 108 | 109 | xor r11, r11 ; bytes read so far 110 | .loop: 111 | mov rax, 17 ; sys_pread64 112 | mov r10, 54 ; offset 113 | add r10, r11 114 | push rdi ; file descriptor 115 | push rsi ; buffer 116 | push rdx ; bytes remaining 117 | push r11 ; bytes read so far 118 | syscall 119 | pop r11 ; bytes read so far 120 | pop rdx ; bytes remaining 121 | pop rsi ; buffer 122 | pop rdi ; file descriptor 123 | add r11, rax ; bytes read so far 124 | add rsi, rax ; advance pointer 125 | sub rdx, rax ; bytes remaining 126 | cmp rdx, 0 127 | jg .loop 128 | 129 | ret 130 | 131 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 132 | drw_draw_text: 133 | ; rdi text 134 | ; rsi font image 135 | ; rdx x 136 | ; rcx y 137 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 138 | push rbp 139 | push r12 140 | push r13 141 | mov rbp, rsp 142 | sub rsp, 32 143 | 144 | mov r13, rdi ; text 145 | mov [rbp - 8], rsi ; font image 146 | mov [rbp - 16], rdx ; x 147 | mov [rbp - 24], rcx ; y 148 | 149 | xor r11, r11 ; count 150 | .loop: 151 | movzx r8, byte [r13] 152 | cmp r8, 0 153 | je .end 154 | 155 | push r11 ; count 156 | 157 | mov rdi, [rbp - 8] 158 | mov r10, r11 159 | imul r10, FONT_CHAR_W 160 | mov rsi, r10 161 | add rsi, [rbp - 16] ; dstX 162 | mov rdx, [rbp - 24] ; dstY 163 | mov rcx, r8 164 | sub rcx, SPACE_CHAR_CODE_PT 165 | imul rcx, FONT_CHAR_W ; srcX 166 | mov r8, 0 ; srcY 167 | mov r9, FONT_CHAR_W ; w 168 | mov r12, FONT_CHAR_H 169 | sub rsp, 16 170 | mov [rsp], r12 ; h 171 | call drw_draw 172 | add rsp, 16 173 | 174 | pop r11 ; count 175 | 176 | inc r13 ; advance text pointer 177 | inc r11 ; advance counter 178 | jmp .loop 179 | .end: 180 | mov rsp, rbp 181 | pop r13 182 | pop r12 183 | pop rbp 184 | 185 | ret 186 | 187 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 188 | drw_draw: 189 | ; Copy pixels from src image to frame buffer 190 | ; 191 | ; rdi image 192 | ; rsi dstX (screen space) 193 | ; rdx dstY (screen space) 194 | ; rcx srcX 195 | ; r8 srcY 196 | ; r9 w 197 | ; h 198 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 199 | push rbp 200 | mov rbp, rsp 201 | sub rsp, 80 202 | 203 | mov r10d, [rdi] 204 | mov [rbp - 64], r10 ; srcW 205 | mov r10d, [rdi + 4] 206 | mov [rbp - 72], r10 ; srcH 207 | 208 | add rdi, 8 ; start of pixel data 209 | 210 | mov [rbp - 8], rdi ; src 211 | mov [rbp - 16], rsi ; dstX 212 | mov [rbp - 24], rdx ; dstY 213 | mov [rbp - 32], rcx ; srcX 214 | mov [rbp - 40], r8 ; srcY 215 | mov [rbp - 48], r9 ; w 216 | mov r10, [rbp + 16] 217 | mov [rbp - 56], r10 ; h 218 | 219 | push r12 220 | push r13 221 | push r14 222 | push r15 223 | 224 | xor r13, r13 ; row 225 | .loop_row: 226 | ; src offset = 4 * (srcW * (row + srcY) + srcX + col) 227 | ; dst offset = 4 * (drw_fb_w * (row + dstY) + dstx + col) 228 | 229 | mov r8, r13 230 | add r8, [rbp - 40] ; row + srcY 231 | imul r8, [rbp - 64] ; srcW * (row + srcY) 232 | add r8, [rbp - 32] ; srcW * (row + srcY) + srcX 233 | 234 | mov r9, r13 235 | add r9, [rbp - 24] ; row + dstY 236 | cmp r9, 0 237 | jl .skip_row 238 | cmp r9d, [drw_fb_h] 239 | jge .skip_row 240 | imul r9d, [drw_fb_w] ; drw_fb_w * (row + dstY) 241 | add r9, [rbp - 16] ; drw_fb_w * (row + dstY) + dstX 242 | 243 | xor r14, r14 ; col 244 | .loop_col: 245 | mov r15, r14 246 | add r15, [rbp - 16] 247 | cmp r15, 0 248 | jl .skip_col 249 | cmp r15d, [drw_fb_w] 250 | jge .skip_col 251 | 252 | mov r10, r8 253 | add r10, r14 ; srcW * (row + srcY) + srcX + col 254 | shl r10, 2 ; src offset 255 | 256 | mov r11, r9 257 | add r11, r14 ; drw_fb_w * (row + dstY) + dstX + col 258 | shl r11, 2 ; dst offset 259 | 260 | mov rdi, [rbp - 8] ; src 261 | add rdi, r10 ; src + src offset 262 | 263 | mov rsi, [drw_buf] ; dst 264 | add rsi, r11 ; dst + dst offset 265 | 266 | mov r15d, [rdi] ; src pixel 267 | 268 | ; Copy pixel if alpha component is non-zero 269 | mov rax, r15 270 | mov rdx, 0xFF000000 271 | and rax, rdx 272 | jz .skip_col 273 | mov [rsi], r15d ; copy pixel 274 | .skip_col: 275 | inc r14 276 | cmp r14, [rbp - 48] 277 | jl .loop_col 278 | .skip_row: 279 | inc r13 280 | cmp r13, [rbp - 56] 281 | jl .loop_row 282 | 283 | pop r15 284 | pop r14 285 | pop r13 286 | pop r12 287 | mov rsp, rbp 288 | pop rbp 289 | 290 | ret 291 | 292 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 293 | drw_flush: 294 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 295 | mov rax, 18 ; sys_pwrite64 296 | mov rdi, [drw_fbfd] 297 | mov rsi, [drw_buf] 298 | mov edx, [drw_fb_w] 299 | imul edx, [drw_fb_h] 300 | shl rdx, 2 ; num bytes 301 | mov r10, 0 ; destination offset 302 | syscall 303 | 304 | ret 305 | 306 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 307 | drw_darken: 308 | ; Darkens the rectangular region 309 | ; 310 | ; rdi dstX 311 | ; rsi dstY 312 | ; rdx w 313 | ; rcx h 314 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 315 | push rbp 316 | push r12 317 | push r13 318 | push r14 319 | push r15 320 | mov rbp, rsp 321 | sub rsp, 64 322 | 323 | mov [rbp - 8], rdi ; dstX 324 | mov [rbp - 16], rsi ; dstY 325 | mov [rbp - 24], rdx ; w 326 | mov [rbp - 32], rcx ; h 327 | 328 | xor r13, r13 ; row 329 | .loop_row: 330 | xor r14, r14 ; column 331 | .loop_col: 332 | mov r15, r13 333 | add r15, [rbp - 16] ; dstY 334 | imul r15d, [drw_fb_w] 335 | add r15, [rbp - 8] ; dstX 336 | add r15, r14 337 | shl r15, 2 ; dst offset 338 | 339 | mov rdi, [drw_buf] 340 | add rdi, r15 341 | 342 | mov r8d, [rdi] 343 | mov r11, 0xFF000000 344 | 345 | ; red 346 | mov r12, r8 347 | and r12, 0x00FF0000 348 | shr r12, 17 349 | shl r12, 16 350 | or r11, r12 351 | 352 | ; green 353 | mov r12, r8 354 | and r12, 0x0000FF00 355 | shr r12, 9 356 | shl r12, 8 357 | or r11, r12 358 | 359 | ; blue 360 | mov r12, r8 361 | and r12, 0x000000FF 362 | shr r12, 1 363 | or r11, r12 364 | 365 | mov [rdi], r11d 366 | 367 | inc r14 368 | cmp r14, [rbp - 24] 369 | jl .loop_col 370 | 371 | inc r13 372 | cmp r13, [rbp - 32] 373 | jl .loop_row 374 | 375 | mov rsp, rbp 376 | pop r15 377 | pop r14 378 | pop r13 379 | pop r12 380 | pop rbp 381 | 382 | ret 383 | 384 | 385 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 386 | drw_fill: 387 | ; Fills the rectangular region with a colour 388 | ; 389 | ; rdi dstX 390 | ; rsi dstY 391 | ; rdx w 392 | ; rcx h 393 | ; r8 colour 394 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 395 | push rbp 396 | push r12 397 | push r13 398 | push r14 399 | push r15 400 | mov rbp, rsp 401 | sub rsp, 64 402 | 403 | mov [rbp - 8], rdi ; dstX 404 | mov [rbp - 16], rsi ; dstY 405 | mov [rbp - 24], rdx ; w 406 | mov [rbp - 32], rcx ; h 407 | 408 | xor r13, r13 ; row 409 | .loop_row: 410 | xor r14, r14 ; column 411 | .loop_col: 412 | mov r15, r13 413 | add r15, [rbp - 16] ; dstY 414 | imul r15d, [drw_fb_w] 415 | add r15, [rbp - 8] ; dstX 416 | add r15, r14 417 | shl r15, 2 ; dst offset 418 | 419 | mov rdi, [drw_buf] 420 | add rdi, r15 421 | mov [rdi], r8d 422 | 423 | inc r14 424 | cmp r14, [rbp - 24] 425 | jl .loop_col 426 | 427 | inc r13 428 | cmp r13, [rbp - 32] 429 | jl .loop_row 430 | 431 | mov rsp, rbp 432 | pop r15 433 | pop r14 434 | pop r13 435 | pop r12 436 | pop rbp 437 | 438 | ret 439 | 440 | -------------------------------------------------------------------------------- /src/levels.asm: -------------------------------------------------------------------------------- 1 | SECTION .data 2 | 3 | global num_levels 4 | global levels 5 | 6 | num_levels dd 3 7 | levels db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 8 | db 4, 0, 2, 2, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 3, 4 9 | db 4, 0, 0, 0, 0, 1, 0, 3, 4, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 4 10 | db 4, 2, 2, 0, 0, 0, 0, 2, 4, 0, 0, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4 11 | db 4, 0, 0, 2, 0, 2, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4 12 | db 4, 0, 0, 0, 2, 2, 0, 0, 4, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 3, 4 13 | db 4, 0, 3, 3, 0, 0, 0, 0, 4, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 14 | db 4, 0, 0, 0, 0, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4 15 | db 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4 16 | db 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4 17 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4 18 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 19 | db 4, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4 20 | db 4, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 4 21 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4 22 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 4, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 4 23 | db 4, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 24 | db 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 25 | db 4, 3, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 26 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 27 | 28 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 29 | db 4, 0, 2, 2, 0, 0, 0, 0, 4, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 2, 3, 3, 0, 0, 3, 4 30 | db 4, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 2, 0, 2, 0, 0, 0, 4 31 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 2, 4 32 | db 4, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 0, 0, 0, 4 33 | db 4, 0, 0, 2, 0, 2, 0, 0, 4, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 4, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 0, 2, 0, 4 34 | db 4, 0, 0, 0, 2, 2, 0, 0, 4, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 4, 3, 2, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 2, 0, 0, 3, 4 35 | db 4, 0, 3, 3, 0, 0, 0, 0, 4, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 0, 0, 0, 4 36 | db 4, 0, 0, 0, 0, 2, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 2, 4 37 | db 4, 0, 0, 0, 0, 3, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4 38 | db 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4 39 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4 40 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 41 | db 4, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 3, 3, 2, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4 42 | db 4, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4 43 | db 4, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 4 44 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 4 45 | db 4, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 2, 4 46 | db 4, 3, 0, 0, 3, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4 47 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 48 | 49 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 50 | db 4, 3, 2, 0, 0, 0, 0, 0, 4, 5, 0, 0, 2, 0, 2, 4, 2, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 0, 2, 3, 4 51 | db 4, 0, 0, 0, 0, 0, 0, 3, 4, 4, 0, 3, 3, 0, 0, 4, 0, 0, 0, 0, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 4 52 | db 4, 0, 2, 0, 0, 0, 0, 2, 2, 0, 0, 3, 3, 2, 0, 4, 0, 0, 0, 0, 4, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4 53 | db 4, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 2, 0, 4, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4 54 | db 4, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 3, 2, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 2, 2, 0, 3, 4 55 | db 4, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4 56 | db 4, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4 57 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 3, 4 58 | db 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4 59 | db 4, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4 60 | db 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 2, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 61 | db 4, 2, 0, 2, 0, 0, 0, 2, 2, 0, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 2, 2, 4 62 | db 4, 2, 3, 2, 0, 0, 0, 0, 2, 0, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 2, 0, 4, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 2, 0, 0, 4 63 | db 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 2, 0, 2, 2, 2, 0, 2, 0, 4, 2, 2, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 0, 0, 4 64 | db 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 2, 3, 0, 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 4 65 | db 4, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 3, 3, 2, 0, 2, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4 66 | db 4, 0, 2, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 4 67 | db 4, 3, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 3, 2, 3, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 2, 2, 4, 0, 0, 0, 0, 0, 4 68 | db 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 -------------------------------------------------------------------------------- /src/main.asm: -------------------------------------------------------------------------------- 1 | %define CELL_SZ 64 2 | %define GRID_W 40 3 | %define GRID_H 20 4 | %define HUD_H 64 5 | %define BACKGROUND_COLOUR 0xFF332211 6 | %define HUD_COLOUR 0xFF111111 7 | 8 | SECTION .data 9 | 10 | hide_cursor db 0x1b, '[?25l', 0 11 | show_cursor db 0x1b, '[?25h', 0 12 | 13 | %define STR_GOODBYE_LEN 33 14 | str_goodbye db "Thanks for playing Gems'n'Rocks!", 10 15 | str_you_died db 'You died!', 0 16 | str_success db 'Success!', 0 17 | str_victory db 'You are victorious!', 0 18 | str_ent_to_q db 'Press enter to quit', 0 19 | str_continue db 'Press enter to continue', 0 20 | str_num_gems db 'Gems remaining:', 0 21 | 22 | img_font_path db './data/font.bmp', 0 23 | img_plyr_path db './data/player.bmp', 0 24 | img_soil_path db './data/soil.bmp', 0 25 | img_rock_path db './data/rock.bmp', 0 26 | img_gem_path db './data/gem.bmp', 0 27 | img_wall_path db './data/wall.bmp', 0 28 | img_exit_path db './data/exit.bmp', 0 29 | 30 | %define DIR_RIGHT 0 31 | %define DIR_LEFT 1 32 | %define DIR_UP 2 33 | %define DIR_DOWN 3 34 | 35 | %define PLYR_ANIM_DEATH 4 36 | %define PLYR_ANIM_WIN 5 37 | %define EXIT_ANIM_OPEN 1 38 | %define GEM_ANIM_COLLECT 2 39 | 40 | unit_vecs dd 1, 0 ; right 41 | dd -1, 0 ; left 42 | dd 0, -1 ; up 43 | dd 0, 1 ; down 44 | 45 | next_obj_addr resq 0 46 | 47 | ; Top-left corner of the screen in world space 48 | camera_x dd 0 49 | camera_y dd 0 50 | 51 | %define GAME_ST_ALIVE 0 52 | %define GAME_ST_DEAD 1 53 | %define GAME_ST_SUCCESS 2 54 | %define GAME_ST_VICTORIOUS 3 55 | 56 | level dd 0 57 | num_gems dd 0 58 | game_state dd GAME_ST_ALIVE 59 | player_dir dd -1 60 | pending_move dd -1 61 | 62 | %define OBJ_TYPE_PLYR 1 63 | %define OBJ_TYPE_SOIL 0 64 | %define OBJ_TYPE_ROCK 2 65 | %define OBJ_TYPE_GEM 3 66 | %define OBJ_TYPE_WALL 4 67 | %define OBJ_TYPE_EXIT 5 68 | 69 | SECTION .bss 70 | 71 | %define OBJ_SIZE 104 72 | %define OBJ_OFFSET_TYPE 0 73 | %define OBJ_OFFSET_X 8 74 | %define OBJ_OFFSET_Y 16 75 | %define OBJ_OFFSET_IMG 24 ; Pointer to sprite sheet 76 | %define OBJ_OFFSET_IMG_ROW 32 ; Current row (animation) 77 | %define OBJ_OFFSET_FRAME 40 ; Current column (frame) 78 | %define OBJ_OFFSET_ANIM_ST 48 ; 1 = playing, 0 = paused 79 | %define OBJ_OFFSET_DX 56 80 | %define OBJ_OFFSET_DY 64 81 | %define OBJ_OFFSET_FLAGS 72 82 | %define OBJ_OFFSET_GRID_X 80 83 | %define OBJ_OFFSET_GRID_Y 88 84 | %define OBJ_OFFSET_QUEUED_ANIM 96 85 | 86 | %define OBJ_FLAG_CAN_FALL 1 87 | %define OBJ_FLAG_STACKABLE 2 88 | %define OBJ_FLAG_ANIMATED 4 89 | 90 | %define ANIM_NUM_FRAMES 8 91 | 92 | objects resb OBJ_SIZE * GRID_W * GRID_H 93 | grid resq GRID_W * GRID_H ; Pointers to game objects 94 | pending_destr resq GRID_W * GRID_H 95 | player resq 1 96 | exit resq 1 97 | 98 | termios_old resb 60 ; Original terminal settings 99 | termios_new resb 60 ; Modified terminal settings 100 | stdin_flags resq 1 ; Original stdin flags 101 | 102 | ; First 8 bytes contains width and height 103 | %define IMG_FONT_W 2730 104 | %define IMG_FONT_H 48 105 | img_font resb 8 + IMG_FONT_W * IMG_FONT_H * 4 106 | 107 | %define IMG_PLYR_W 512 108 | %define IMG_PLYR_H 384 109 | img_plyr resb 8 + IMG_PLYR_W * IMG_PLYR_H * 4 110 | 111 | %define IMG_SOIL_W 512 112 | %define IMG_SOIL_H 64 113 | img_soil resb 8 + IMG_SOIL_W * IMG_SOIL_H * 4 114 | 115 | %define IMG_ROCK_W 512 116 | %define IMG_ROCK_H 128 117 | img_rock resb 8 + IMG_ROCK_W * IMG_ROCK_H * 4 118 | 119 | %define IMG_GEM_W 512 120 | %define IMG_GEM_H 192 121 | img_gem resb 8 + IMG_GEM_W * IMG_GEM_H * 4 122 | 123 | %define IMG_WALL_W 64 124 | %define IMG_WALL_H 64 125 | img_wall resb 8 + IMG_WALL_W * IMG_WALL_H * 4 126 | 127 | %define IMG_EXIT_W 512 128 | %define IMG_EXIT_H 128 129 | img_exit resb 8 + IMG_EXIT_W * IMG_EXIT_H * 4 130 | 131 | SECTION .text 132 | 133 | extern drw_init 134 | extern drw_draw 135 | extern drw_fill 136 | extern drw_darken 137 | extern drw_draw_text 138 | extern drw_load_bmp 139 | extern drw_term 140 | extern drw_flush 141 | extern drw_fb_w 142 | extern drw_fb_h 143 | 144 | extern util_min 145 | extern util_max 146 | extern util_int_to_str 147 | extern util_print 148 | extern util_assert_fail 149 | 150 | extern levels 151 | extern num_levels 152 | 153 | global _start 154 | 155 | ; Calling convention 156 | ; ------------------ 157 | ; 158 | ; To call a function, fill registers in order. 159 | ; For integers and pointers: rdi, rsi, rdx, rcx, r8, r9 160 | ; For floating-point (float, double): xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 161 | ; For system calls, the order is: rdi, rsi, rdx, r10, r8, r9 162 | ; Push remaining args to stack (in order right-to-left for C functions) 163 | ; The call instruction will then push the return address 164 | ; Functions shouldn't change: rbp, rbx, r12, r13, r14, r15 165 | ; Functions return integers in rax and floats in xmm0 166 | 167 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 168 | load_images: 169 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 170 | lea rdi, [rel img_font_path] 171 | lea rsi, [rel img_font] 172 | mov rdx, IMG_FONT_W 173 | mov rcx, IMG_FONT_H 174 | call drw_load_bmp 175 | 176 | lea rdi, [rel img_plyr_path] 177 | lea rsi, [rel img_plyr] 178 | mov rdx, IMG_PLYR_W 179 | mov rcx, IMG_PLYR_H 180 | call drw_load_bmp 181 | 182 | lea rdi, [rel img_soil_path] 183 | lea rsi, [rel img_soil] 184 | mov rdx, IMG_SOIL_W 185 | mov rcx, IMG_SOIL_H 186 | call drw_load_bmp 187 | 188 | lea rdi, [rel img_rock_path] 189 | lea rsi, [rel img_rock] 190 | mov rdx, IMG_ROCK_W 191 | mov rcx, IMG_ROCK_H 192 | call drw_load_bmp 193 | 194 | lea rdi, [rel img_gem_path] 195 | lea rsi, [rel img_gem] 196 | mov rdx, IMG_GEM_W 197 | mov rcx, IMG_GEM_H 198 | call drw_load_bmp 199 | 200 | lea rdi, [rel img_wall_path] 201 | lea rsi, [rel img_wall] 202 | mov rdx, IMG_WALL_W 203 | mov rcx, IMG_WALL_H 204 | call drw_load_bmp 205 | 206 | lea rdi, [rel img_exit_path] 207 | lea rsi, [rel img_exit] 208 | mov rdx, IMG_EXIT_W 209 | mov rcx, IMG_EXIT_H 210 | call drw_load_bmp 211 | 212 | ret 213 | 214 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 215 | construct_scene: 216 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 217 | call load_images 218 | 219 | lea r10, [rel levels] 220 | mov r8d, [level] 221 | imul r8, GRID_W * GRID_H ; level offset 222 | add r10, r8 223 | 224 | mov r8, 0 225 | mov [num_gems], r8d 226 | 227 | lea r8, [rel objects] 228 | mov [next_obj_addr], r8 229 | 230 | xor r8, r8 ; row 231 | .loop_row: 232 | xor r9, r9 ; col 233 | .loop_col: 234 | mov r11, r8 235 | imul r11, GRID_W 236 | add r11, r9 237 | add r11, r10 238 | movzx rdx, byte [r11] ; object type 239 | 240 | mov rdi, r9 241 | mov rsi, r8 242 | 243 | push r8 244 | push r9 245 | push r10 246 | 247 | cmp rdx, OBJ_TYPE_PLYR 248 | je .type_plyr 249 | cmp rdx, OBJ_TYPE_EXIT 250 | je .type_exit 251 | cmp rdx, OBJ_TYPE_GEM 252 | je .type_gem 253 | cmp rdx, OBJ_TYPE_ROCK 254 | je .type_rock 255 | cmp rdx, OBJ_TYPE_SOIL 256 | je .type_soil 257 | cmp rdx, OBJ_TYPE_WALL 258 | je .type_wall 259 | .type_exit: 260 | call construct_exit 261 | jmp .end 262 | .type_gem: 263 | call construct_gem 264 | inc dword [num_gems] 265 | jmp .end 266 | .type_plyr: 267 | call construct_player 268 | jmp .end 269 | .type_rock: 270 | call construct_rock 271 | jmp .end 272 | .type_soil: 273 | call construct_soil 274 | jmp .end 275 | .type_wall: 276 | call construct_wall 277 | jmp .end 278 | .end: 279 | pop r10 280 | pop r9 281 | pop r8 282 | 283 | inc r9 284 | cmp r9, GRID_W 285 | jl .loop_col 286 | 287 | inc r8 288 | cmp r8, GRID_H 289 | jl .loop_row 290 | 291 | mov [game_state], dword GAME_ST_ALIVE 292 | mov [pending_move], dword -1 293 | 294 | ret 295 | 296 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 297 | construct_player: 298 | ; rdi gridX 299 | ; rsi gridY 300 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 301 | mov rdx, rsi 302 | mov rsi, rdi 303 | mov rdi, OBJ_TYPE_PLYR 304 | lea rcx, [rel img_plyr] 305 | mov r8, OBJ_FLAG_STACKABLE | OBJ_FLAG_ANIMATED 306 | call construct_object 307 | mov [player], rax 308 | 309 | ret 310 | 311 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 312 | construct_exit: 313 | ; rdi gridX 314 | ; rsi gridY 315 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 316 | mov rdx, rsi 317 | mov rsi, rdi 318 | mov rdi, OBJ_TYPE_EXIT 319 | lea rcx, [rel img_exit] 320 | mov r8, OBJ_FLAG_STACKABLE | OBJ_FLAG_ANIMATED 321 | call construct_object 322 | mov [exit], rax 323 | 324 | ret 325 | 326 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 327 | construct_gem: 328 | ; rdi gridX 329 | ; rsi gridY 330 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 331 | mov rdx, rsi 332 | mov rsi, rdi 333 | mov rdi, OBJ_TYPE_GEM 334 | lea rcx, [rel img_gem] 335 | mov r8, OBJ_FLAG_CAN_FALL | OBJ_FLAG_ANIMATED 336 | call construct_object 337 | 338 | ret 339 | 340 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 341 | construct_rock: 342 | ; rdi gridX 343 | ; rsi gridY 344 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 345 | mov rdx, rsi 346 | mov rsi, rdi 347 | mov rdi, OBJ_TYPE_ROCK 348 | lea rcx, [rel img_rock] 349 | mov r8, OBJ_FLAG_CAN_FALL | OBJ_FLAG_ANIMATED 350 | call construct_object 351 | 352 | ret 353 | 354 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 355 | construct_soil: 356 | ; rdi gridX 357 | ; rsi gridY 358 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 359 | mov rdx, rsi 360 | mov rsi, rdi 361 | mov rdi, OBJ_TYPE_SOIL 362 | lea rcx, [rel img_soil] 363 | mov r8, OBJ_FLAG_STACKABLE | OBJ_FLAG_ANIMATED 364 | call construct_object 365 | 366 | ret 367 | 368 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 369 | construct_wall: 370 | ; rdi gridX 371 | ; rsi gridY 372 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 373 | mov rdx, rsi 374 | mov rsi, rdi 375 | mov rdi, OBJ_TYPE_WALL 376 | lea rcx, [rel img_wall] 377 | mov r8, OBJ_FLAG_STACKABLE | OBJ_FLAG_ANIMATED 378 | call construct_object 379 | 380 | ret 381 | 382 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 383 | centre_cam: 384 | ; Centre the camera around the player 385 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 386 | mov r8, [player] 387 | mov rdi, [r8 + OBJ_OFFSET_X] 388 | mov rsi, [r8 + OBJ_OFFSET_Y] 389 | 390 | push rsi 391 | 392 | mov r8d, [drw_fb_w] 393 | shr r8, 1 394 | sub rdi, r8 ; playerX - fb_w / 2 395 | 396 | mov rsi, 0 397 | call util_max 398 | 399 | mov rdi, rax ; max(0, playerX - fb_w / 2) 400 | mov rsi, GRID_W 401 | imul rsi, CELL_SZ 402 | sub esi, [drw_fb_w] 403 | call util_min 404 | 405 | mov [camera_x], eax ; min(GRID_W * CELL_SZ - fb_w, max(0, playerX - fb_w / 2)) 406 | 407 | pop rsi ; playerY 408 | 409 | mov r8d, [drw_fb_h] 410 | sub r8, HUD_H 411 | shr r8, 1 412 | sub rsi, r8 ; playerY - fb_h / 2 413 | 414 | mov rdi, 0 415 | call util_max 416 | 417 | mov rdi, rax ; max(0, playerY - fb_h / 2) 418 | mov rsi, GRID_H 419 | imul rsi, CELL_SZ 420 | sub esi, [drw_fb_h] 421 | add rsi, HUD_H 422 | call util_min 423 | 424 | mov [camera_y], eax ; min(GRID_H * CELL_SZ - fb_h, max(0, playerY - fb_h / 2)) 425 | 426 | ret 427 | 428 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 429 | initialise: 430 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 431 | mov rax, 72 ; sys_fcntl 432 | mov rdi, 0 ; stdin 433 | mov rsi, 3 ; F_GETFL (get file status flags) 434 | syscall 435 | mov [stdin_flags], rax 436 | 437 | mov rdi, 0 ; stdin 438 | mov rsi, 4 ; F_SETFL (set file status flags) 439 | mov rdx, rax ; Copy old flags to rdx 440 | or rdx, 0x800 ; O_NONBLOCK = 0x800 (set non-blocking flag) 441 | mov rax, 72 ; sys_fcntl 442 | syscall 443 | 444 | ; Get current terminal settings 445 | mov rax, 16 ; sys_ioctl 446 | mov rdi, 0 ; stdin 447 | mov rsi, 0x5401 ; TCGETS 448 | mov rdx, termios_old 449 | syscall 450 | 451 | ; Make a copy 452 | mov rax, 16 ; sys_ioctl 453 | mov rdi, 0 ; stdin 454 | mov rsi, 0x5401 ; TCGETS 455 | mov rdx, termios_new 456 | syscall 457 | 458 | ; Disable ICANON and ECHO 459 | and byte [termios_new + 12], 0xF5 460 | 461 | ; Set modified terminal settings 462 | mov rax, 16 ; sys_ioctl 463 | mov rdi, 0 ; stdin 464 | mov rsi, 0x5402 ; TCSETS 465 | mov rdx, termios_new 466 | syscall 467 | 468 | mov rax, 1 ; sys_write 469 | mov rdi, 1 ; stdout 470 | mov rsi, hide_cursor 471 | mov rdx, 6 472 | syscall 473 | 474 | call drw_init 475 | 476 | ret 477 | 478 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 479 | terminate: 480 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 481 | call drw_term 482 | 483 | ; Restore old terminal settings 484 | mov rax, 16 ; sys_ioctl 485 | mov rdi, 0 ; stdin 486 | mov rsi, 0x5402 ; TCSETS 487 | mov rdx, termios_old 488 | syscall 489 | 490 | mov rdi, 0 ; stdin 491 | mov rsi, 4 ; F_SETFL (set file status flags) 492 | mov rdx, [stdin_flags] ; Copy old flags to rdx 493 | mov rax, 72 ; sys_fcntl 494 | syscall 495 | 496 | mov rax, 1 ; sys_write 497 | mov rdi, 1 ; stdout 498 | mov rsi, show_cursor 499 | mov rdx, 6 500 | syscall 501 | 502 | ret 503 | 504 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 505 | grid_insert: 506 | ; rdi gridX 507 | ; rsi gridY 508 | ; rdx object 509 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 510 | mov r11, rsi 511 | imul r11, GRID_W 512 | add r11, rdi 513 | shl r11, 3 514 | lea r8, [rel grid] 515 | add r8, r11 516 | mov [r8], rdx 517 | 518 | cmp rdx, 0 519 | je .end 520 | mov [rdx + OBJ_OFFSET_GRID_X], rdi 521 | mov [rdx + OBJ_OFFSET_GRID_Y], rsi 522 | .end: 523 | ret 524 | 525 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 526 | grid_erase: 527 | ; rdi gridX 528 | ; rsi gridY 529 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 530 | mov rdx, 0 531 | call grid_insert 532 | 533 | ret 534 | 535 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 536 | grid_at: 537 | ; rdi gridX 538 | ; rsi gridY 539 | ; 540 | ; Returns 541 | ; rax object 542 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 543 | mov r11, rsi 544 | imul r11, GRID_W 545 | add r11, rdi 546 | shl r11, 3 547 | lea r8, [rel grid] 548 | add r8, r11 549 | mov rax, [r8] 550 | 551 | cmp rax, 0 552 | je .ok 553 | 554 | ; Check the object really is at its grid coords 555 | mov r8, [rax + OBJ_OFFSET_GRID_X] 556 | mov r9, [rax + OBJ_OFFSET_GRID_Y] 557 | cmp r8, rdi 558 | jne .error 559 | cmp r9, rsi 560 | je .ok 561 | .error: 562 | call util_assert_fail 563 | .ok: 564 | ret 565 | 566 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 567 | obj_erase: 568 | ; rdi object 569 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 570 | mov r8, [rdi + OBJ_OFFSET_GRID_X] 571 | mov r9, [rdi + OBJ_OFFSET_GRID_Y] 572 | 573 | imul r9, GRID_W 574 | add r9, r8 575 | shl r9, 3 ; offset 576 | 577 | ; Erase from grid 578 | lea r8, [rel grid] 579 | add r8, r9 580 | mov r10, [r8] 581 | ; But only if the object is actually in the grid 582 | cmp rdi, r10 583 | jne .skip 584 | xor r11, r11 585 | mov [r8], r11 586 | .skip: 587 | ; Add to pending_destr 588 | lea r8, [rel pending_destr] 589 | add r8, r9 590 | mov [r8], rdi 591 | 592 | ret 593 | 594 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 595 | obj_update: 596 | ; rdi object 597 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 598 | mov rdx, [rdi + OBJ_OFFSET_ANIM_ST] 599 | cmp rdx, 0 600 | je .end 601 | 602 | ; Move to next frame 603 | mov rax, [rdi + OBJ_OFFSET_FRAME] 604 | mov r8, ANIM_NUM_FRAMES 605 | inc rax 606 | div r8 607 | mov [rdi + OBJ_OFFSET_FRAME], rdx 608 | 609 | ; Move the object 610 | mov r8, [rdi + OBJ_OFFSET_DX] 611 | add [rdi + OBJ_OFFSET_X], r8 612 | mov r8, [rdi + OBJ_OFFSET_DY] 613 | add [rdi + OBJ_OFFSET_Y], r8 614 | 615 | ; Stop the animation if it's reached the end 616 | cmp rdx, 0 617 | jne .end 618 | mov rcx, 0 619 | mov [rdi + OBJ_OFFSET_ANIM_ST], rcx 620 | 621 | ; The animation has just finished 622 | ; If there's another one queued, start it right away 623 | mov rsi, [rdi + OBJ_OFFSET_QUEUED_ANIM] 624 | cmp rsi, -1 625 | je .end 626 | mov rdx, 0 627 | mov rcx, 0 628 | call obj_play_anim 629 | mov r8, -1 630 | mov [rdi + OBJ_OFFSET_QUEUED_ANIM], r8 631 | 632 | .end: 633 | ret 634 | 635 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 636 | update_scene: 637 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 638 | xor rcx, rcx 639 | .loop: 640 | mov r8, rcx 641 | shl r8, 3 ; offset 642 | 643 | lea r9, [rel grid] 644 | add r9, r8 645 | mov rdi, [r9] 646 | cmp rdi, 0 647 | je .skip1 648 | 649 | push rcx 650 | push r8 651 | call obj_update 652 | pop r8 653 | pop rcx 654 | .skip1: 655 | lea r9, [rel pending_destr] 656 | add r9, r8 657 | mov rdi, [r9] 658 | cmp rdi, 0 659 | je .skip2 660 | 661 | push rcx 662 | call obj_update 663 | pop rcx 664 | .skip2: 665 | 666 | inc rcx 667 | cmp rcx, GRID_H * GRID_W 668 | jl .loop 669 | 670 | cmp [num_gems], dword 0 671 | jne .end 672 | mov rdi, [exit] 673 | mov rsi, EXIT_ANIM_OPEN 674 | mov rdx, 0 675 | mov rcx, 0 676 | call obj_play_anim 677 | .end: 678 | 679 | ret 680 | 681 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 682 | construct_object: 683 | ; rdi type 684 | ; rsi gridX 685 | ; rdx gridY 686 | ; rcx image 687 | ; r8 flags 688 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 689 | push rbp 690 | mov rbp, rsp 691 | sub rsp, 56 692 | 693 | mov [rbp - 8], rdi ; type 694 | mov [rbp - 16], rsi ; gridX 695 | mov [rbp - 24], rdx ; gridY 696 | mov [rbp - 32], rcx ; image 697 | mov [rbp - 48], r8 ; flags 698 | 699 | mov r11, OBJ_SIZE 700 | mov rax, [next_obj_addr] 701 | add [next_obj_addr], r11 702 | 703 | mov r11, rax ; pointer 704 | 705 | mov rdi, [rbp - 8] ; type 706 | mov [r11 + OBJ_OFFSET_TYPE], rdi 707 | 708 | mov rdi, [rbp - 16] ; gridX 709 | imul rdi, CELL_SZ ; worldX 710 | mov [r11 + OBJ_OFFSET_X], rdi 711 | mov rdi, [rbp - 24] ; gridY 712 | imul rdi, CELL_SZ ; worldY 713 | mov [r11 + OBJ_OFFSET_Y], rdi 714 | 715 | mov rdi, [rbp - 32] ; image 716 | mov [r11 + OBJ_OFFSET_IMG], rdi 717 | 718 | mov rdi, 0 719 | mov [r11 + OBJ_OFFSET_IMG_ROW], rdi 720 | mov [r11 + OBJ_OFFSET_FRAME], rdi 721 | mov [r11 + OBJ_OFFSET_ANIM_ST], rdi 722 | mov rdi, [rbp - 48] ; flags 723 | mov [r11 + OBJ_OFFSET_FLAGS], rdi 724 | mov rdi, -1 725 | mov [r11 + OBJ_OFFSET_QUEUED_ANIM], rdi 726 | 727 | mov rdi, [rbp - 16] ; gridX 728 | mov rsi, [rbp - 24] ; gridY 729 | mov rdx, r11 ; pointer 730 | call grid_insert 731 | 732 | mov rsp, rbp 733 | pop rbp 734 | 735 | ret 736 | 737 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 738 | obj_is_falling: 739 | ; rdi object 740 | ; 741 | ; Returns 742 | ; rax whether the object is falling 743 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 744 | ; To save having to add some kind of IS_FALLING flag, assume an object is falling if: 745 | ; - its animation state is 1 746 | ; - its dy is positive 747 | ; - OBJ_FLAG_ANIMATED is unset, so it's moving but not animating 748 | 749 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 750 | cmp r8, 0 751 | je .skip 752 | 753 | mov r8, [rdi + OBJ_OFFSET_DY] 754 | cmp r8, 0 755 | jle .skip 756 | 757 | mov r8, [rdi + OBJ_OFFSET_FLAGS] 758 | and r8, OBJ_FLAG_ANIMATED 759 | cmp r8, 0 760 | jne .skip 761 | 762 | mov rax, 1 763 | ret 764 | .skip: 765 | mov rax, 0 766 | ret 767 | 768 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 769 | obj_play_anim: 770 | ; rdi object 771 | ; rsi animation ID (row of sprite sheet) 772 | ; rdx dx 773 | ; rcx dy 774 | ; 775 | ; Returns 776 | ; rax 1 if playing the animation succeeded, 0 otherwise 777 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 778 | ; Skip to end if there's already an animation playing 779 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 780 | cmp r8, 1 781 | je .failure 782 | 783 | ; Set animated flag 784 | mov r9, [rdi + OBJ_OFFSET_FLAGS] 785 | or r9, OBJ_FLAG_ANIMATED 786 | mov [rdi + OBJ_OFFSET_FLAGS], r9 787 | 788 | mov r9, 1 789 | mov [rdi + OBJ_OFFSET_ANIM_ST], r9 790 | mov [rdi + OBJ_OFFSET_IMG_ROW], rsi 791 | mov [rdi + OBJ_OFFSET_DX], rdx 792 | mov [rdi + OBJ_OFFSET_DY], rcx 793 | .success: 794 | mov rax, 1 795 | ret 796 | .failure: 797 | mov rax, 0 798 | ret 799 | 800 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 801 | obj_queue_anim: 802 | ; Queued animations don't have deltas 803 | ; 804 | ; rdi object 805 | ; rsi animation ID (row of sprite sheet) 806 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 807 | ; Try to play the animation first 808 | mov rdx, 0 809 | mov rcx, 0 810 | call obj_play_anim 811 | 812 | cmp rax, 1 813 | je .end 814 | 815 | ; Only queue the animation if it failed to play 816 | mov [rdi + OBJ_OFFSET_QUEUED_ANIM], rsi 817 | .end: 818 | ret 819 | 820 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 821 | obj_play_transform: 822 | ; rdi object 823 | ; rsi animation ID (row of sprite sheet) 824 | ; rdx dx 825 | ; rcx dy 826 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 827 | ; Skip to end if there's already an animation playing 828 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 829 | cmp r8, 1 830 | je .end 831 | 832 | ; Unset animated flag 833 | mov r9, [rdi + OBJ_OFFSET_FLAGS] 834 | mov r10, OBJ_FLAG_ANIMATED 835 | not r10 836 | and r9, r10 837 | mov [rdi + OBJ_OFFSET_FLAGS], r9 838 | 839 | mov r9, 1 840 | mov [rdi + OBJ_OFFSET_ANIM_ST], r9 841 | mov [rdi + OBJ_OFFSET_DX], rdx 842 | mov [rdi + OBJ_OFFSET_DY], rcx 843 | .end: 844 | ret 845 | 846 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 847 | obj_draw: 848 | ; rdi object 849 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 850 | push rbp 851 | push r12 852 | push r13 853 | 854 | mov rbp, rsp 855 | sub rsp, 16 856 | 857 | mov r13, [rdi + OBJ_OFFSET_FLAGS] 858 | and r13, OBJ_FLAG_ANIMATED 859 | 860 | mov r11, rdi 861 | mov rdi, [r11 + OBJ_OFFSET_IMG] 862 | mov rsi, [r11 + OBJ_OFFSET_X] 863 | mov r12d, [camera_x] 864 | sub rsi, r12 865 | mov rdx, [r11 + OBJ_OFFSET_Y] 866 | mov r12d, [camera_y] 867 | sub rdx, r12 868 | add rdx, HUD_H 869 | xor rcx, rcx 870 | cmp r13, 0 871 | je .not_animated 872 | mov rcx, [r11 + OBJ_OFFSET_FRAME] 873 | imul rcx, CELL_SZ ; srcX 874 | .not_animated: 875 | mov r8, [r11 + OBJ_OFFSET_IMG_ROW] 876 | imul r8, CELL_SZ ; srcY 877 | mov r9, CELL_SZ ; w 878 | mov r10, CELL_SZ ; h 879 | mov [rbp - 16], r10 880 | call drw_draw 881 | 882 | mov rsp, rbp 883 | pop r13 884 | pop r12 885 | pop rbp 886 | 887 | ret 888 | 889 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 890 | clear_screen: 891 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 892 | mov rdi, 0 893 | mov rsi, 0 894 | mov edx, [drw_fb_w] 895 | mov ecx, [drw_fb_h] 896 | mov r8, BACKGROUND_COLOUR 897 | call drw_fill 898 | 899 | ret 900 | 901 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 902 | render_hud: 903 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 904 | mov rdi, 0 905 | mov rsi, 0 906 | mov edx, [drw_fb_w] 907 | mov rcx, CELL_SZ 908 | mov r8, HUD_COLOUR 909 | call drw_fill 910 | 911 | lea rdi, [rel str_num_gems] 912 | lea rsi, [rel img_font] 913 | mov rdx, 10 914 | mov rcx, 10 915 | call drw_draw_text 916 | 917 | sub rsp, 16 918 | mov edi, [num_gems] 919 | mov rsi, rsp 920 | call util_int_to_str 921 | mov rdi, rsp 922 | lea rsi, [rel img_font] 923 | mov rdx, 500 924 | mov rcx, 10 925 | call drw_draw_text 926 | add rsp, 16 927 | 928 | ret 929 | 930 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 931 | render_box: 932 | ; rdi string 1 933 | ; rsi string 2 934 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 935 | push rbp 936 | push r12 937 | push r13 938 | mov rbp, rsp 939 | sub rsp, 48 940 | 941 | mov r8d, [drw_fb_w] 942 | mov r9d, [drw_fb_h] 943 | 944 | mov r10, r8 ; fb_w 945 | shr r10, 3 ; x = fb_w / 8 946 | mov r12, r10 ; x 947 | shl r12, 1 ; 2 * x 948 | mov r11, r8 ; fb_w 949 | sub r11, r12 ; w = fb_w - 2 * x 950 | 951 | mov r12, r9 ; fb_h 952 | shr r12, 2 ; y = fb_h / 4 953 | mov rcx, r12 ; y 954 | shl rcx, 1 ; 2 * y 955 | mov r13, r9 ; fb_h 956 | sub r13, rcx ; h = fb_h - 2 * y 957 | 958 | mov [rbp - 8], r10 ; x 959 | mov [rbp - 16], r12 ; y 960 | mov [rbp - 24], r11 ; w 961 | mov [rbp - 32], r13 ; h 962 | mov [rbp - 40], rdi ; string 1 963 | mov [rbp - 48], rsi ; string 2 964 | 965 | mov rdi, r10 ; x 966 | mov rsi, r12 ; y 967 | mov rdx, r11 ; w 968 | mov rcx, r13 ; h 969 | call drw_darken 970 | 971 | mov rdi, [rbp - 40] ; string 1 972 | lea rsi, [rel img_font] 973 | mov rdx, [rbp - 8] ; x 974 | add rdx, 40 975 | mov rcx, [rbp - 16] ; y 976 | add rcx, 40 977 | call drw_draw_text 978 | 979 | mov rdi, [rbp - 48] ; string 2 980 | lea rsi, [rel img_font] 981 | mov rdx, [rbp - 8] ; x 982 | add rdx, 40 983 | mov rcx, [rbp - 16] ; y 984 | add rcx, 150 985 | call drw_draw_text 986 | 987 | mov rsp, rbp 988 | pop r13 989 | pop r12 990 | pop rbp 991 | 992 | ret 993 | 994 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 995 | render_death_box: 996 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 997 | lea rdi, [rel str_you_died] 998 | lea rsi, [rel str_continue] 999 | call render_box 1000 | 1001 | ret 1002 | 1003 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1004 | render_success_box: 1005 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1006 | lea rdi, [rel str_success] 1007 | lea rsi, [rel str_continue] 1008 | call render_box 1009 | 1010 | ret 1011 | 1012 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1013 | render_victorious_box: 1014 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1015 | lea rdi, [rel str_victory] 1016 | lea rsi, [rel str_ent_to_q] 1017 | call render_box 1018 | 1019 | ret 1020 | 1021 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1022 | render_scene: 1023 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1024 | push rbp 1025 | push r12 1026 | push r13 1027 | push r14 1028 | push r15 1029 | mov rbp, rsp 1030 | sub rsp, 16 1031 | 1032 | call clear_screen 1033 | 1034 | mov r14, CELL_SZ 1035 | 1036 | mov edi, [drw_fb_w] 1037 | add edi, [camera_x] 1038 | mov rsi, GRID_W * CELL_SZ 1039 | call util_min 1040 | xor rdx, rdx 1041 | div r14 1042 | ; Increment xMax if there's a remainder 1043 | cmp rdx, 0 1044 | je .no_inc_x 1045 | inc rax 1046 | .no_inc_x: 1047 | mov [rbp - 8], rax ; xMax 1048 | 1049 | mov edi, [drw_fb_h] 1050 | sub rdi, HUD_H 1051 | add edi, [camera_y] 1052 | mov rsi, GRID_H * CELL_SZ 1053 | call util_min 1054 | xor rdx, rdx 1055 | div r14 1056 | ; Increment yMax if there's a remainder 1057 | cmp rdx, 0 1058 | je .no_inc_y 1059 | inc rax 1060 | .no_inc_y: 1061 | mov [rbp - 16], rax ; yMax 1062 | 1063 | mov eax, [camera_y] 1064 | xor rdx, rdx 1065 | div r14 1066 | mov r8, rax ; row 1067 | 1068 | .loop_row: 1069 | mov eax, [camera_x] 1070 | xor rdx, rdx 1071 | div r14 1072 | mov r9, rax ; col 1073 | .loop_col: 1074 | mov r10, r8 1075 | imul r10, GRID_W 1076 | add r10, r9 1077 | shl r10, 3 ; offset 1078 | 1079 | lea r12, [rel grid] 1080 | add r12, r10 1081 | mov rdi, [r12] 1082 | cmp rdi, 0 1083 | je .skip1 1084 | 1085 | push r8 1086 | push r9 1087 | push r10 1088 | call obj_draw 1089 | pop r10 1090 | pop r9 1091 | pop r8 1092 | .skip1: 1093 | lea r11, [rel pending_destr] 1094 | add r11, r10 1095 | mov rdi, [r11] 1096 | cmp rdi, 0 1097 | je .skip2 1098 | 1099 | push r8 1100 | push r9 1101 | call obj_draw 1102 | pop r9 1103 | pop r8 1104 | .skip2: 1105 | inc r9 1106 | cmp r9, [rbp - 8] 1107 | jl .loop_col 1108 | 1109 | inc r8 1110 | cmp r8, [rbp - 16] 1111 | jl .loop_row 1112 | 1113 | call render_hud 1114 | 1115 | cmp [game_state], dword GAME_ST_DEAD 1116 | je .st_dead 1117 | cmp [game_state], dword GAME_ST_SUCCESS 1118 | je .st_success 1119 | cmp [game_state], dword GAME_ST_VICTORIOUS 1120 | je .st_victorious 1121 | cmp [game_state], dword GAME_ST_ALIVE 1122 | je .st_endif 1123 | .st_success: 1124 | call render_success_box 1125 | jmp .st_endif 1126 | .st_victorious: 1127 | call render_victorious_box 1128 | jmp .st_endif 1129 | .st_dead: 1130 | call render_death_box 1131 | .st_endif: 1132 | 1133 | call drw_flush 1134 | 1135 | mov rsp, rbp 1136 | pop r15 1137 | pop r14 1138 | pop r13 1139 | pop r12 1140 | pop rbp 1141 | 1142 | ret 1143 | 1144 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1145 | push_exit: 1146 | ; rdi object 1147 | ; rsi direction 1148 | ; 1149 | ; Returns 1150 | ; rax block player = 1, allow player = 0 1151 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1152 | cmp [num_gems], dword 0 1153 | jne .closed 1154 | 1155 | mov r8, [level] 1156 | inc r8 1157 | cmp r8d, [num_levels] 1158 | jl .success 1159 | mov [game_state], dword GAME_ST_VICTORIOUS 1160 | jmp .endif 1161 | .success: 1162 | mov [game_state], dword GAME_ST_SUCCESS 1163 | .endif: 1164 | mov rax, 0 1165 | ret 1166 | .closed: 1167 | mov rax, 1 1168 | ret 1169 | 1170 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1171 | push_gem: 1172 | ; rdi object 1173 | ; rsi direction 1174 | ; 1175 | ; Returns 1176 | ; rax block player = 1, allow player = 0 1177 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1178 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 1179 | cmp r8, 1 1180 | je .block_player 1181 | 1182 | push rdi 1183 | 1184 | mov rsi, GEM_ANIM_COLLECT ; animation ID 1185 | mov rdx, 0 ; dx 1186 | mov rcx, 0 ; dy 1187 | call obj_play_anim 1188 | 1189 | pop rdi ; object 1190 | call obj_erase 1191 | 1192 | dec dword [num_gems] 1193 | 1194 | mov rax, 0 1195 | jmp .end 1196 | 1197 | .block_player: 1198 | mov rax, 1 ; block player movement 1199 | .end: 1200 | ret 1201 | 1202 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1203 | push_rock: 1204 | ; rdi object 1205 | ; rsi direction 1206 | ; 1207 | ; Returns 1208 | ; rax block player = 1, allow player = 0 1209 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1210 | push rbp 1211 | mov rbp, rsp 1212 | sub rsp, 16 1213 | 1214 | push r12 1215 | push r13 1216 | push r14 1217 | push r15 1218 | 1219 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 1220 | cmp r8, 1 1221 | je .block_player 1222 | 1223 | mov [rbp - 8], rdi ; object 1224 | mov [rbp - 16], rsi ; direction 1225 | 1226 | mov r8, [rdi + OBJ_OFFSET_GRID_X] 1227 | mov r9, [rdi + OBJ_OFFSET_GRID_Y] 1228 | 1229 | lea r14, [rel unit_vecs] 1230 | mov r11, [rbp - 16] ; direction 1231 | shl r11, 3 1232 | add r14, r11 1233 | movsx r12, dword [r14] ; dx 1234 | movsx r13, dword [r14 + 4] ; dy 1235 | 1236 | cmp r13, 0 ; don't allow pushing vertically 1237 | jne .block_player 1238 | 1239 | mov rdi, r8 1240 | add rdi, r12 ; gridX + dx 1241 | mov rsi, r13 1242 | add rsi, r9 ; gridY + dy 1243 | call grid_at 1244 | 1245 | cmp rax, 0 1246 | jne .block_player 1247 | 1248 | mov rdi, [rbp - 8] 1249 | mov rsi, [rbp - 16] 1250 | mov rdx, 1 1251 | call obj_move 1252 | 1253 | mov rax, 0 ; allow player movement 1254 | jmp .end 1255 | 1256 | .block_player: 1257 | mov rax, 1 ; block player movement 1258 | .end: 1259 | 1260 | pop r15 1261 | pop r14 1262 | pop r13 1263 | pop r12 1264 | 1265 | mov rsp, rbp 1266 | pop rbp 1267 | 1268 | ret 1269 | 1270 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1271 | push_soil: 1272 | ; rdi object 1273 | ; rsi direction 1274 | ; 1275 | ; Returns 1276 | ; rax block player = 1, allow player = 0 1277 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1278 | push rdi 1279 | 1280 | mov rsi, 0 ; animation ID 1281 | mov rdx, 0 ; dx 1282 | mov rcx, 0 ; dy 1283 | call obj_play_anim 1284 | 1285 | pop rdi ; object 1286 | call obj_erase 1287 | 1288 | mov rax, 0 1289 | 1290 | ret 1291 | 1292 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1293 | push_wall: 1294 | ; rdi object 1295 | ; rsi direction 1296 | ; 1297 | ; Returns 1298 | ; rax block player = 1, allow player = 0 1299 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1300 | mov rax, 1 1301 | 1302 | ret 1303 | 1304 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1305 | obj_push: 1306 | ; Push the object in the given direction 1307 | ; 1308 | ; rdi object 1309 | ; rsi direction 1310 | ; 1311 | ; Returns 1312 | ; rax block player = 1, allow player = 0 1313 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1314 | mov r8, [rdi + OBJ_OFFSET_TYPE] 1315 | 1316 | cmp r8, OBJ_TYPE_EXIT 1317 | je .type_exit 1318 | cmp r8, OBJ_TYPE_GEM 1319 | je .type_gem 1320 | cmp r8, OBJ_TYPE_ROCK 1321 | je .type_rock 1322 | cmp r8, OBJ_TYPE_SOIL 1323 | je .type_soil 1324 | cmp r8, OBJ_TYPE_WALL 1325 | je .type_wall 1326 | .type_exit: 1327 | call push_exit 1328 | jmp .end 1329 | .type_gem: 1330 | call push_gem 1331 | jmp .end 1332 | .type_rock: 1333 | call push_rock 1334 | jmp .end 1335 | .type_soil: 1336 | call push_soil 1337 | jmp .end 1338 | .type_wall: 1339 | call push_wall 1340 | jmp .end 1341 | .end: 1342 | ret 1343 | 1344 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1345 | grid_push_obj: 1346 | ; Push the object in the given direction 1347 | ; 1348 | ; rdi direction 1349 | ; 1350 | ; Returns 1351 | ; rax block player = 1, allow player = 0 1352 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1353 | push r12 1354 | push r13 1355 | push r14 1356 | 1357 | mov rax, [player] 1358 | mov r8, [rax + OBJ_OFFSET_GRID_X] 1359 | mov r9, [rax + OBJ_OFFSET_GRID_Y] 1360 | 1361 | mov r11, rdi ; direction 1362 | push r11 1363 | 1364 | lea r14, [rel unit_vecs] 1365 | shl r11, 3 1366 | add r14, r11 1367 | movsx r12, dword [r14] ; dx 1368 | movsx r13, dword [r14 + 4] ; dy 1369 | 1370 | ; player grid coords + delta 1371 | add r8, r12 1372 | add r9, r13 1373 | 1374 | mov rdi, r8 1375 | mov rsi, r9 1376 | call grid_at 1377 | 1378 | pop r11 ; direction 1379 | 1380 | cmp rax, 0 1381 | je .skip 1382 | 1383 | mov rdi, rax 1384 | mov rsi, r11 1385 | call obj_push 1386 | .skip: 1387 | 1388 | pop r14 1389 | pop r13 1390 | pop r12 1391 | 1392 | ret 1393 | 1394 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1395 | grid_move_obj: 1396 | ; rdi object 1397 | ; rsi direction 1398 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1399 | push rbp 1400 | mov rbp, rsp 1401 | sub rsp, 32 1402 | 1403 | mov r8, [rdi + OBJ_OFFSET_GRID_X] 1404 | mov r9, [rdi + OBJ_OFFSET_GRID_Y] 1405 | 1406 | mov [rbp - 8], rdi ; object 1407 | mov [rbp - 16], rsi ; direction 1408 | mov [rbp - 24], r8 ; gridX 1409 | mov [rbp - 32], r9 ; gridY 1410 | 1411 | mov rdi, r8 1412 | mov rsi, r9 1413 | call grid_at 1414 | 1415 | ; Check the object really is at its grid coords 1416 | cmp rax, [rbp - 8] 1417 | je .ok 1418 | call util_assert_fail 1419 | .ok: 1420 | mov rdi, [rbp - 24] 1421 | mov rsi, [rbp - 32] 1422 | call grid_erase 1423 | 1424 | lea r8, [rel unit_vecs] 1425 | mov r11, [rbp - 16] ; direction 1426 | shl r11, 3 ; size of vector is 8 bytes 1427 | add r8, r11 1428 | movsx rdi, dword [r8] ; dx 1429 | movsx rsi, dword [r8 + 4] ; dy 1430 | 1431 | add rdi, [rbp - 24] 1432 | add rsi, [rbp - 32] 1433 | mov rdx, [rbp - 8] 1434 | call grid_insert 1435 | 1436 | mov rsp, rbp 1437 | pop rbp 1438 | 1439 | ret 1440 | 1441 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1442 | obj_move: 1443 | ; rdi object 1444 | ; rsi direction 1445 | ; rdx animate 1446 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1447 | ; Check if object is currently moving 1448 | mov r8, [rdi + OBJ_OFFSET_ANIM_ST] 1449 | cmp r8, 1 1450 | je .end ; exit function if object is moving 1451 | 1452 | push rdi ; object 1453 | push rsi ; direction 1454 | push rdx ; animate 1455 | 1456 | call grid_move_obj 1457 | 1458 | pop rdx ; animate 1459 | pop rsi ; direction 1460 | pop rdi ; object 1461 | 1462 | lea r8, [rel unit_vecs] 1463 | mov r11, rsi ; direction 1464 | shl r11, 3 ; size of vector is 8 bytes 1465 | add r8, r11 1466 | movsx r9, dword [r8] ; dx 1467 | movsx r10, dword [r8 + 4] ; dy 1468 | 1469 | cmp rdx, 0 1470 | je .no_animate 1471 | ; Play animation 1472 | mov rdx, r9 ; dx 1473 | imul rdx, CELL_SZ / ANIM_NUM_FRAMES 1474 | mov rcx, r10 ; dy 1475 | imul rcx, CELL_SZ / ANIM_NUM_FRAMES 1476 | call obj_play_anim 1477 | jmp .end 1478 | .no_animate: 1479 | ; Play transformation 1480 | mov rdx, r9 ; dx 1481 | imul rdx, CELL_SZ / ANIM_NUM_FRAMES 1482 | mov rcx, r10 ; dy 1483 | imul rcx, CELL_SZ / ANIM_NUM_FRAMES 1484 | call obj_play_transform 1485 | .end: 1486 | ret 1487 | 1488 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1489 | plyr_move: 1490 | ; rdi direction 1491 | ; 1492 | ; Returns 1493 | ; rax 1 if player is already moving, 0 otherwise 1494 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1495 | mov r8, rdi 1496 | 1497 | ; Check if player is currently moving 1498 | mov r9, [player] 1499 | mov r10, [r9 + OBJ_OFFSET_ANIM_ST] 1500 | cmp r10, 1 1501 | je .already_moving ; exit function if already moving 1502 | 1503 | push r8 1504 | call grid_push_obj 1505 | pop r8 1506 | cmp rax, 1 1507 | je .blocked ; exit function if blocked by object 1508 | 1509 | mov rdi, [player] 1510 | mov rsi, r8 1511 | mov rdx, 1 1512 | call obj_move 1513 | 1514 | ; If the game state is SUCCESS or VICTORIOUS, we must have just pushed into the exit 1515 | cmp [game_state], dword GAME_ST_SUCCESS 1516 | je .level_complete 1517 | cmp [game_state], dword GAME_ST_VICTORIOUS 1518 | je .level_complete 1519 | jmp .success 1520 | .level_complete: 1521 | mov rdi, [player] 1522 | mov rsi, PLYR_ANIM_WIN 1523 | call obj_queue_anim 1524 | 1525 | mov rdi, [player] 1526 | call obj_erase 1527 | 1528 | mov r8, [player] 1529 | mov rdi, [r8 + OBJ_OFFSET_GRID_X] 1530 | mov rsi, [r8 + OBJ_OFFSET_GRID_Y] 1531 | mov rdx, [exit] 1532 | call grid_insert 1533 | 1534 | jmp .success 1535 | .already_moving: 1536 | mov rax, 1 1537 | ret 1538 | .blocked: 1539 | .success: 1540 | mov rax, 0 1541 | ret 1542 | 1543 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1544 | obj_fall: 1545 | ; rdi object 1546 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1547 | mov rsi, DIR_DOWN 1548 | mov rdx, 0 1549 | call obj_move 1550 | 1551 | ret 1552 | 1553 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1554 | obj_try_fall: 1555 | ; rdi object 1556 | ; rsi gridX 1557 | ; rdx gridY 1558 | ; 1559 | ; Returns 1560 | ; rax 0 = didn't fall, 1 = did fall 1561 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1562 | mov rcx, [rdi + OBJ_OFFSET_FLAGS] 1563 | and rcx, OBJ_FLAG_CAN_FALL 1564 | cmp rcx, 0 1565 | je .end ; skip non-fallable objects 1566 | 1567 | mov r8, rsi ; gridX 1568 | mov r9, rdx ; gridY 1569 | 1570 | push rdi ; object 1571 | 1572 | ; Get the object below this one 1573 | mov rdi, r8 1574 | mov rsi, r9 1575 | inc rsi 1576 | call grid_at 1577 | 1578 | pop rdi ; object 1579 | 1580 | cmp rax, 0 1581 | jne .end ; if there's an object below this one 1582 | 1583 | call obj_fall 1584 | mov rax, 1 1585 | ret 1586 | .end: 1587 | mov rax, 0 1588 | ret 1589 | 1590 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1591 | grid_space_to_fall_sideways: 1592 | ; rdi gridX 1593 | ; rsi gridY 1594 | ; rdx direction 1595 | ; 1596 | ; Returns 1597 | ; rax 0 = false, 1 = true 1598 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1599 | lea r8, [rel unit_vecs] 1600 | mov r11, rdx ; direction 1601 | shl r11, 3 1602 | add r8, r11 1603 | movsx r10, dword [r8] ; dx 1604 | 1605 | mov r8, rdi ; gridX 1606 | mov r9, rsi ; gridY 1607 | push r8 1608 | push r9 1609 | push r10 1610 | 1611 | add rdi, r10 1612 | call grid_at 1613 | mov rdx, rax ; adjacent cell object 1614 | 1615 | pop r10 ; dx 1616 | pop r9 ; gridY 1617 | pop r8 ; gridX 1618 | 1619 | push rdx ; adjacent cell object 1620 | 1621 | mov rdi, r8 1622 | add rdi, r10 1623 | mov rsi, r9 1624 | inc rsi 1625 | call grid_at 1626 | 1627 | pop rdx ; adjacent cell object 1628 | or rax, rdx 1629 | cmp rax, 0 ; if both down-adjacent and adjacent cells are empty 1630 | je .true 1631 | 1632 | mov rax, 0 1633 | ret 1634 | .true: 1635 | mov rax, 1 1636 | ret 1637 | 1638 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1639 | obj_try_fall_sideways: 1640 | ; rdi object 1641 | ; rsi gridX 1642 | ; rdx gridY 1643 | ; 1644 | ; Returns 1645 | ; rax 0 = didn't fall, 1 = did fall 1646 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1647 | push rbp 1648 | mov rbp, rsp 1649 | sub rsp, 32 1650 | 1651 | mov [rbp - 8], rdi ; object 1652 | mov [rbp - 16], rsi ; gridX 1653 | mov [rbp - 24], rdx ; gridY 1654 | 1655 | mov rcx, [rdi + OBJ_OFFSET_FLAGS] 1656 | and rcx, OBJ_FLAG_CAN_FALL 1657 | cmp rcx, 0 1658 | je .no_move ; skip non-fallable objects 1659 | 1660 | ; Get the object below this one 1661 | mov rdi, [rbp - 16] 1662 | mov rsi, [rbp - 24] 1663 | inc rsi 1664 | call grid_at 1665 | 1666 | cmp rax, 0 1667 | je .no_move ; if there's no object below this one 1668 | 1669 | mov r10, [rax + OBJ_OFFSET_FLAGS] 1670 | and r10, OBJ_FLAG_STACKABLE 1671 | cmp r10, 0 1672 | jne .no_move ; don't fall if the object below is stackable 1673 | 1674 | mov rdi, [rbp - 16] ; gridX 1675 | mov rsi, [rbp - 24] ; gridY 1676 | mov rdx, DIR_LEFT 1677 | call grid_space_to_fall_sideways 1678 | cmp rax, 0 1679 | je .try_fall_right 1680 | 1681 | mov rdi, [rbp - 8] ; object 1682 | mov rsi, DIR_LEFT 1683 | mov rdx, 1 1684 | call obj_move 1685 | jmp .moved 1686 | .try_fall_right: 1687 | mov rdi, [rbp - 16] ; gridX 1688 | mov rsi, [rbp - 24] ; gridY 1689 | mov rdx, DIR_RIGHT 1690 | call grid_space_to_fall_sideways 1691 | cmp rax, 0 1692 | je .no_move 1693 | 1694 | mov rdi, [rbp - 8] 1695 | mov rsi, DIR_RIGHT 1696 | mov rdx, 1 1697 | call obj_move 1698 | .moved: 1699 | mov rax, 1 1700 | jmp .end 1701 | .no_move: 1702 | mov rax, 0 1703 | .end: 1704 | mov rsp, rbp 1705 | pop rbp 1706 | 1707 | ret 1708 | 1709 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1710 | physics: 1711 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1712 | lea r11, [rel grid] 1713 | 1714 | mov r8, 0 ; row 1715 | .loop_row: 1716 | mov r9, 0 ; col 1717 | .loop_col: 1718 | mov r10, r8 1719 | imul r10, GRID_W 1720 | add r10, r9 1721 | shl r10, 3 1722 | add r10, r11 ; pointer to object pointer 1723 | 1724 | mov rdi, [r10] ; object 1725 | cmp rdi, 0 1726 | je .skip ; skip if null 1727 | 1728 | push r8 1729 | push r9 1730 | push r11 1731 | mov rsi, r9 1732 | mov rdx, r8 1733 | call obj_try_fall 1734 | pop r11 1735 | pop r9 1736 | pop r8 1737 | cmp rax, 1 1738 | je .skip 1739 | 1740 | push r8 1741 | push r9 1742 | push r11 1743 | mov rsi, r9 1744 | mov rdx, r8 1745 | call obj_try_fall_sideways 1746 | pop r11 1747 | pop r9 1748 | pop r8 1749 | .skip: 1750 | inc r9 1751 | cmp r9, GRID_W 1752 | jl .loop_col 1753 | 1754 | inc r8 1755 | cmp r8, GRID_H - 1 1756 | jl .loop_row 1757 | 1758 | ret 1759 | 1760 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1761 | death_condition: 1762 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1763 | mov r8, [player] 1764 | mov rdi, [r8 + OBJ_OFFSET_GRID_X] 1765 | mov rsi, [r8 + OBJ_OFFSET_GRID_Y] 1766 | dec rsi 1767 | call grid_at 1768 | cmp rax, 0 1769 | je .still_alive 1770 | 1771 | mov rdi, rax 1772 | call obj_is_falling 1773 | cmp rax, 0 1774 | je .still_alive 1775 | 1776 | mov rdi, [player] 1777 | mov rsi, PLYR_ANIM_DEATH 1778 | mov rdx, 0 1779 | mov rcx, 0 1780 | call obj_queue_anim 1781 | mov [game_state], dword GAME_ST_DEAD 1782 | ret 1783 | .still_alive: 1784 | ret 1785 | 1786 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1787 | keyboard: 1788 | ; Returns 1789 | ; rax change of game state: 1790 | ; 0 no change 1791 | ; 1 quit 1792 | ; 2 restart level 1793 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1794 | sub rsp, 32 1795 | 1796 | cmp [game_state], dword GAME_ST_ALIVE 1797 | jne .skip_pending_move 1798 | cmp [pending_move], dword DIR_RIGHT 1799 | je .key_right 1800 | cmp [pending_move], dword DIR_LEFT 1801 | je .key_left 1802 | cmp [pending_move], dword DIR_UP 1803 | je .key_up 1804 | cmp [pending_move], dword DIR_DOWN 1805 | je .key_down 1806 | .skip_pending_move: 1807 | 1808 | xor r8, r8 ; bytes read 1809 | .loop: 1810 | mov rax, 0 ; sys_read 1811 | mov rdi, 0 ; stdin 1812 | mov rsi, rsp 1813 | add rsi, r8 1814 | mov rdx, 1 ; num bytes to read 1815 | syscall 1816 | cmp rax, 0 1817 | jle .done 1818 | add r8, rax ; we got data 1819 | cmp r8, 32 1820 | je .done 1821 | jmp .loop 1822 | .done: 1823 | cmp r8, 0 1824 | je .end 1825 | 1826 | mov r9, rsp 1827 | add r9, r8 1828 | mov rdi, r8 1829 | mov rsi, 3 1830 | call util_min 1831 | sub r9, rax ; pointer to (no more than) last 3 bytes 1832 | 1833 | cmp [game_state], dword GAME_ST_ALIVE 1834 | je .st_alive 1835 | cmp [game_state], dword GAME_ST_DEAD 1836 | je .st_dead 1837 | cmp [game_state], dword GAME_ST_SUCCESS 1838 | je .st_success 1839 | cmp [game_state], dword GAME_ST_VICTORIOUS 1840 | je .st_victorious 1841 | .st_alive: 1842 | cmp byte [r9], 0x0A ; new line 1843 | je .restart 1844 | cmp byte [r9], 0x1B ; esc sequence 1845 | jne .end 1846 | cmp byte [r9 + 1], 0x5B ; [ character 1847 | jne .quit 1848 | cmp byte [r9 + 2], 0x41 1849 | je .key_up 1850 | cmp byte [r9 + 2], 0x42 1851 | je .key_down 1852 | cmp byte [r9 + 2], 0x43 1853 | je .key_right 1854 | cmp byte [r9 + 2], 0x44 1855 | je .key_left 1856 | jmp .end 1857 | .st_dead: 1858 | .st_success: 1859 | .st_victorious: 1860 | cmp byte [r9], 0x0A ; new line 1861 | je .restart 1862 | cmp byte [r9], 0x1B ; esc sequence 1863 | jne .end 1864 | cmp byte [r9 + 1], 0x5B ; [ character 1865 | jne .quit 1866 | jmp .end 1867 | .key_up: 1868 | mov rdi, DIR_UP 1869 | jmp .arrow_key 1870 | .key_down: 1871 | mov rdi, DIR_DOWN 1872 | jmp .arrow_key 1873 | .key_right: 1874 | mov rdi, DIR_RIGHT 1875 | jmp .arrow_key 1876 | .key_left: 1877 | mov rdi, DIR_LEFT 1878 | .arrow_key: 1879 | push rdi 1880 | call plyr_move 1881 | pop rdi 1882 | cmp rax, 1 ; if player was already moving 1883 | jne .plyr_moved_or_blocked 1884 | cmp [player_dir], edi 1885 | je .end 1886 | mov [pending_move], edi 1887 | jmp .end 1888 | .quit: 1889 | add rsp, 32 1890 | mov rax, 1 1891 | ret 1892 | .restart: 1893 | add rsp, 32 1894 | mov rax, 2 1895 | ret 1896 | .plyr_moved_or_blocked: 1897 | mov [pending_move], dword -1 ; clear pending movement 1898 | mov [player_dir], edi 1899 | add rsp, 32 1900 | mov rax, 0 1901 | ret 1902 | .end: 1903 | add rsp, 32 1904 | mov rax, 0 1905 | ret 1906 | 1907 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1908 | delete_pending: 1909 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1910 | xor rcx, rcx 1911 | .loop: 1912 | lea r9, [rel pending_destr] 1913 | mov r8, rcx 1914 | shl r8, 3 ; offset 1915 | mov r10, r9 1916 | add r10, r8 1917 | mov rdi, [r10] 1918 | cmp rdi, 0 1919 | je .skip 1920 | 1921 | mov r11, [rdi + OBJ_OFFSET_ANIM_ST] 1922 | cmp r11, 1 1923 | je .skip ; skip if animation is still playing 1924 | ; Erase from pending_destr 1925 | add r8, r9 1926 | xor r11, r11 1927 | mov [r8], r11 1928 | .skip: 1929 | inc rcx 1930 | cmp rcx, GRID_H * GRID_W 1931 | jl .loop 1932 | 1933 | ret 1934 | 1935 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1936 | sleep: 1937 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1938 | sub rsp, 16 1939 | mov rdi, rsp 1940 | mov r8, 0 ; seconds 1941 | mov [rsp], r8 1942 | mov r8, 1000000000/60 ; nanoseconds 1943 | mov [rsp + 8], r8 1944 | mov rsi, 0 1945 | mov rax, 35 ; sys_nanosleep 1946 | syscall 1947 | add rsp, 16 1948 | 1949 | ret 1950 | 1951 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1952 | _start: 1953 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1954 | call initialise 1955 | 1956 | jmp .restart 1957 | .next_level: 1958 | inc dword [level] 1959 | .restart: 1960 | call construct_scene 1961 | 1962 | ; Game loop 1963 | .loop: 1964 | cmp [game_state], dword GAME_ST_ALIVE 1965 | je .st_alive 1966 | cmp [game_state], dword GAME_ST_SUCCESS 1967 | je .st_success 1968 | cmp [game_state], dword GAME_ST_VICTORIOUS 1969 | je .st_victorious 1970 | cmp [game_state], dword GAME_ST_DEAD 1971 | je .st_dead 1972 | .st_alive: 1973 | call centre_cam 1974 | call render_scene 1975 | call sleep 1976 | call death_condition 1977 | call physics 1978 | call keyboard 1979 | cmp rax, 1 1980 | je .exit 1981 | cmp rax, 2 1982 | je .restart 1983 | call update_scene 1984 | call delete_pending 1985 | jmp .loop 1986 | .st_dead: 1987 | call centre_cam 1988 | call render_scene 1989 | call sleep 1990 | call physics 1991 | call keyboard 1992 | cmp rax, 1 1993 | je .exit 1994 | cmp rax, 2 1995 | je .restart 1996 | call update_scene 1997 | 1998 | ; Bit hacky, but force the death animation to stay on the last frame 1999 | mov r8, [player] 2000 | mov r9, [r8 + OBJ_OFFSET_ANIM_ST] 2001 | cmp r9, 0 2002 | jne .skip 2003 | mov r9, ANIM_NUM_FRAMES - 1 2004 | mov [r8 + OBJ_OFFSET_FRAME], r9 2005 | .skip: 2006 | 2007 | call delete_pending 2008 | jmp .loop 2009 | .st_success: 2010 | call centre_cam 2011 | call render_scene 2012 | call sleep 2013 | call physics 2014 | call keyboard 2015 | cmp rax, 1 2016 | je .exit 2017 | cmp rax, 2 2018 | je .next_level 2019 | call update_scene 2020 | call delete_pending 2021 | jmp .loop 2022 | .st_victorious: 2023 | call centre_cam 2024 | call render_scene 2025 | call sleep 2026 | call physics 2027 | call keyboard 2028 | cmp rax, 1 2029 | je .exit 2030 | cmp rax, 2 2031 | je .exit 2032 | call update_scene 2033 | call delete_pending 2034 | jmp .loop 2035 | .exit: 2036 | 2037 | ; Clear the screen 2038 | mov rdi, 0 2039 | mov rsi, 0 2040 | mov edx, [drw_fb_w] 2041 | mov ecx, [drw_fb_h] 2042 | mov r8, 0 2043 | call drw_fill 2044 | call drw_flush 2045 | 2046 | lea rdi, [rel str_goodbye] 2047 | mov rsi, STR_GOODBYE_LEN 2048 | call util_print 2049 | 2050 | call terminate 2051 | 2052 | mov rax, 60 ; sys_exit 2053 | xor rdi, rdi 2054 | syscall 2055 | -------------------------------------------------------------------------------- /src/util.asm: -------------------------------------------------------------------------------- 1 | SECTION .data 2 | 3 | %define STR_ERROR_LEN 18 4 | str_error db 'Assertion failed!', 10 5 | 6 | SECTION .text 7 | 8 | global util_alloc 9 | global util_int_to_str 10 | global util_max 11 | global util_min 12 | global util_print 13 | global util_assert_fail 14 | 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | util_max: 17 | ; rdi a 18 | ; rsi b 19 | ; 20 | ; Returns 21 | ; rax greatest of a and b 22 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 23 | cmp rdi, rsi 24 | jg .a_is_larger 25 | 26 | mov rax, rsi 27 | ret 28 | .a_is_larger: 29 | mov rax, rdi 30 | ret 31 | 32 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 | util_min: 34 | ; rdi a 35 | ; rsi b 36 | ; 37 | ; Returns 38 | ; rax lowest of a and b 39 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 40 | cmp rdi, rsi 41 | jl .a_is_less 42 | 43 | mov rax, rsi 44 | ret 45 | .a_is_less: 46 | mov rax, rdi 47 | ret 48 | 49 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 50 | util_int_to_str: 51 | ; Convert 2 digit number to string 52 | ; 53 | ; rdi integer 54 | ; rsi buffer 55 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 56 | mov rax, rdi 57 | xor rdx, rdx 58 | mov r8, 10 59 | div r8 60 | add rax, '0' 61 | mov [rsi], al 62 | add rdx, '0' 63 | mov [rsi + 1], dl 64 | xor r8, r8 65 | mov [rsi + 2], r8 66 | 67 | ret 68 | 69 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 70 | util_print: 71 | ; rdi string 72 | ; rsi length 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | mov r8, rdi 75 | mov r9, rsi 76 | 77 | mov rax, 1 ; sys_write 78 | mov rdi, 1 ; stdout 79 | mov rsi, r8 80 | mov rdx, r9 ; length 81 | syscall 82 | 83 | ret 84 | 85 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 86 | util_assert_fail: 87 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 88 | lea rdi, [rel str_error] 89 | mov rsi, STR_ERROR_LEN 90 | call util_print 91 | 92 | ret 93 | 94 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 95 | util_alloc: 96 | ; Allocate memory 97 | ; 98 | ; rdi num bytes 99 | ; 100 | ; Returns 101 | ; rax pointer to memory 102 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 103 | mov r11, rdi 104 | 105 | mov rax, 9 ; sys_mmap 106 | mov rdi, 0 ; addr 107 | mov rsi, r11 ; num bytes 108 | mov rdx, 0b11 ; PROT_READ | PROT_WRITE 109 | mov r10, 0b00100010 ; MAP_PRIVATE | MAP_ANONYMOUS 110 | mov r8, -1 ; file descriptor 111 | mov r9, 0 ; offset 112 | 113 | push rdi 114 | syscall 115 | pop rdi 116 | 117 | ; Zero memory 118 | xor r9, r9 119 | xor rcx, rcx 120 | mov r8, rax 121 | .loop: 122 | add r8, rcx 123 | mov [r8], r9 124 | 125 | inc rcx 126 | cmp rcx, rdi 127 | 128 | ret 129 | -------------------------------------------------------------------------------- /tools/cleanbmp/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /tools/cleanbmp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.27) 2 | 3 | set(PROJECT_NAME CppUtils) 4 | 5 | project(${PROJECT_NAME}) 6 | 7 | set(TARGET_NAME cleanbmp) 8 | 9 | file(GLOB 10 | CPP_SOURCES 11 | "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" 12 | ) 13 | 14 | add_executable(${TARGET_NAME} ${CPP_SOURCES}) 15 | 16 | target_include_directories(${TARGET_NAME} 17 | PUBLIC 18 | "${CMAKE_CURRENT_SOURCE_DIR}/include" 19 | ) 20 | 21 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 22 | set(COMPILE_FLAGS -Wextra -Wall) 23 | set(DEBUG_COMPILE_FLAGS ${COMPILE_FLAGS} -g) 24 | set(RELEASE_COMPILE_FLAGS ${COMPILE_FLAGS} -O3) 25 | set(LINK_FLAGS) 26 | set(DEBUG_LINK_FLAGS ${LINK_FLAGS}) 27 | set(RELEASE_LINK_FLAGS ${LINK_FLAGS}) 28 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") 29 | set(COMPILE_FLAGS /W4) 30 | set(DEBUG_COMPILE_FLAGS ${COMPILE_FLAGS}) 31 | set(RELEASE_COMPILE_FLAGS ${COMPILE_FLAGS} /O2) 32 | set(LINK_FLAGS) 33 | set(DEBUG_LINK_FLAGS ${LINK_FLAGS}) 34 | set(RELEASE_LINK_FLAGS ${LINK_FLAGS}) 35 | endif() 36 | 37 | target_compile_options(${TARGET_NAME} 38 | PUBLIC 39 | "$<$:${DEBUG_COMPILE_FLAGS}>" 40 | "$<$:${RELEASE_COMPILE_FLAGS}>" 41 | ) 42 | 43 | target_link_libraries(${TARGET_NAME} 44 | PUBLIC 45 | "$<$:${DEBUG_LINK_FLAGS}>" 46 | "$<$:${RELEASE_LINK_FLAGS}>" 47 | ) 48 | -------------------------------------------------------------------------------- /tools/cleanbmp/README.md: -------------------------------------------------------------------------------- 1 | cleanbmp 2 | ======== 3 | 4 | Converts bmp file into a bmp that is compatible with gemsnrocks_asm. 5 | 6 | gemsnrocks_asm assumes that the pixel data immediately follows the 54 byte header and is compacted, 7 | with no padding at the end of each row. It also assumes the image is stored top-down, rather than 8 | bottom-up, i.e. the first row of pixels in the file should correspond to the top of the image. 9 | 10 | This program loads the bitmap, flips it vertically, and re-saves it. 11 | 12 | The input image should have 4 bytes per pixel. 13 | 14 | Build and run 15 | ------------- 16 | 17 | ``` 18 | mkdir -p build && cd - 19 | cmake -G "Unix Makefiles" .. 20 | make -j8 21 | ``` 22 | 23 | Run from the build directory 24 | 25 | ``` 26 | ./cleanbmp ./input.bmp ./output.bmp 27 | ``` 28 | -------------------------------------------------------------------------------- /tools/cleanbmp/include/cpputils/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace cpputils { 8 | 9 | template 10 | class ContigMultiArray { 11 | friend class ContigMultiArray; 12 | 13 | public: 14 | T* data; 15 | const size_t dimensionality = D; 16 | 17 | ContigMultiArray(const ContigMultiArray& cpy) = delete; 18 | 19 | ContigMultiArray(ContigMultiArray&& mv) { 20 | data = mv.data; 21 | memcpy(m_size, mv.m_size, sizeof(size_t) * D); 22 | m_numElements = mv.m_numElements; 23 | m_stride = mv.m_stride; 24 | m_isTopLevel = mv.m_isTopLevel; 25 | 26 | mv.data = nullptr; 27 | } 28 | 29 | ContigMultiArray(std::initializer_list> arr) 30 | : m_isTopLevel(true) { 31 | 32 | m_size[0] = arr.size(); 33 | memcpy(&m_size[1], arr.begin()->m_size, (D - 1) * sizeof(size_t)); 34 | 35 | m_numElements = 1; 36 | for (size_t i = 0; i < D; ++i) { 37 | m_numElements *= m_size[i]; 38 | } 39 | 40 | m_stride = computeStride(); 41 | data = new T[m_numElements]; 42 | 43 | size_t slice = 0; 44 | for (auto& sub : arr) { 45 | memcpy(data + slice * m_stride, sub.data, sub.numElements() * sizeof(T)); 46 | ++slice; 47 | } 48 | } 49 | 50 | ContigMultiArray(const size_t size[D]) 51 | : data(nullptr), 52 | m_isTopLevel(true) { 53 | 54 | m_numElements = 1; 55 | for (size_t i = 0; i < D; ++i) { 56 | m_size[i] = size[i]; 57 | m_numElements *= size[i]; 58 | } 59 | 60 | data = new T[m_numElements]; 61 | 62 | m_stride = computeStride(); 63 | } 64 | 65 | ContigMultiArray(T* data, const size_t size[D], bool isTopLevel = true) 66 | : data(data), 67 | m_isTopLevel(isTopLevel) { 68 | 69 | memcpy(m_size, size, sizeof(size_t) * D); 70 | 71 | m_numElements = 1; 72 | for (size_t i = 0; i < D; ++i) { 73 | m_numElements *= size[i]; 74 | } 75 | 76 | m_stride = computeStride(); 77 | } 78 | 79 | const size_t* size() const { 80 | return m_size; 81 | } 82 | 83 | size_t numElements() const { 84 | return m_numElements; 85 | } 86 | 87 | ContigMultiArray operator[](size_t slice) const { 88 | if (slice >= m_size[0]) { 89 | throw std::runtime_error("Subscript out of range"); 90 | } 91 | 92 | size_t offset = slice * m_stride; 93 | 94 | return ContigMultiArray(data + offset, &m_size[1], false); 95 | } 96 | 97 | ~ContigMultiArray() { 98 | if (m_isTopLevel) { 99 | delete[] data; 100 | } 101 | } 102 | 103 | private: 104 | size_t m_size[D]; 105 | size_t m_numElements; 106 | size_t m_stride; 107 | bool m_isTopLevel; 108 | 109 | size_t computeStride() const { 110 | size_t stride = 1; 111 | for (size_t i = 1; i < D; ++i) { 112 | stride *= m_size[i]; 113 | } 114 | return stride; 115 | } 116 | }; 117 | 118 | template 119 | class ContigMultiArray { 120 | friend class ContigMultiArray; 121 | 122 | public: 123 | T* data; 124 | 125 | ContigMultiArray(const ContigMultiArray& cpy) = delete; 126 | 127 | ContigMultiArray(ContigMultiArray&& mv) { 128 | data = mv.data; 129 | m_size[0] = mv.m_size[0]; 130 | m_isTopLevel = mv.m_isTopLevel; 131 | 132 | mv.data = nullptr; 133 | } 134 | 135 | ContigMultiArray(std::initializer_list elems) 136 | : m_isTopLevel(true) { 137 | 138 | data = new T[elems.size()]; 139 | 140 | int i = 0; 141 | for (auto& e : elems) { 142 | data[i] = e; 143 | ++i; 144 | } 145 | 146 | m_size[0] = elems.size(); 147 | } 148 | 149 | ContigMultiArray(size_t size[1]) 150 | : m_isTopLevel(true) { 151 | 152 | m_size[0] = *size; 153 | data = new T[m_size[0]]; 154 | } 155 | 156 | ContigMultiArray(T* data, const size_t size[1], bool isTopLevel = true) 157 | : data(data), 158 | m_isTopLevel(isTopLevel) { 159 | 160 | memcpy(m_size, size, sizeof(size_t)); 161 | } 162 | 163 | const size_t* size() const { 164 | return m_size; 165 | } 166 | 167 | size_t numElements() const { 168 | return m_size[0]; 169 | } 170 | 171 | const T& operator[](size_t i) const { 172 | if (i >= m_size[0]) { 173 | throw std::runtime_error("Subscript out of range"); 174 | } 175 | 176 | return data[i]; 177 | } 178 | 179 | T& operator[](size_t i) { 180 | if (i >= m_size[0]) { 181 | throw std::runtime_error("Subscript out of range"); 182 | } 183 | 184 | return data[i]; 185 | } 186 | 187 | ~ContigMultiArray() { 188 | if (m_isTopLevel) { 189 | delete[] data; 190 | } 191 | } 192 | 193 | private: 194 | size_t m_size[1]; 195 | bool m_isTopLevel; 196 | }; 197 | 198 | } 199 | -------------------------------------------------------------------------------- /tools/cleanbmp/include/cpputils/bitmap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cpputils/array.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace cpputils { 9 | 10 | using Bitmap = ContigMultiArray; 11 | 12 | const uint32_t BMP_HEADER_SIZE = 54; 13 | 14 | #pragma pack(push, 1) 15 | struct BmpFileHeader { 16 | BmpFileHeader(uint32_t w, uint32_t h, uint32_t channels) 17 | : size(BMP_HEADER_SIZE + h * static_cast(ceil(0.25 * w * channels)) * 4) {} 18 | 19 | char type[2] = {'B', 'M'}; 20 | uint32_t size; 21 | uint16_t reserved1 = 0; 22 | uint16_t reserved2 = 0; 23 | uint32_t offset = BMP_HEADER_SIZE; 24 | }; 25 | 26 | struct BmpImgHeader { 27 | BmpImgHeader(uint32_t w, uint32_t h, uint16_t channels, uint32_t rawSize) 28 | : width(w), 29 | height(h), 30 | bitCount(channels * 8), 31 | imgSize(rawSize) {} 32 | 33 | uint32_t size = 40; 34 | uint32_t width; 35 | uint32_t height; 36 | uint16_t planes = 1; 37 | uint16_t bitCount; 38 | uint32_t compression = 0; 39 | uint32_t imgSize; 40 | uint32_t xPxPerMetre = 0; 41 | uint32_t yPxPerMetre = 0; 42 | uint32_t colMapEntriesUsed = 0; 43 | uint32_t numImportantColours = 0; 44 | }; 45 | 46 | struct BmpHeader { 47 | BmpHeader(uint32_t imgW, uint32_t imgH, uint16_t channels, uint32_t rawSize) 48 | : fileHdr(imgW, imgH, channels), 49 | imgHdr(imgW, imgH, channels, rawSize) {} 50 | 51 | BmpFileHeader fileHdr; 52 | BmpImgHeader imgHdr; 53 | }; 54 | #pragma pack(pop) 55 | 56 | Bitmap loadBitmap(const std::filesystem::path& path); 57 | void saveBitmap(const Bitmap& bitmap, const std::filesystem::path& path); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /tools/cleanbmp/include/cpputils/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define EXCEPTION(msg) { \ 7 | std::stringstream ss; \ 8 | ss << msg; \ 9 | throw std::runtime_error(ss.str()); \ 10 | } 11 | -------------------------------------------------------------------------------- /tools/cleanbmp/src/bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpputils/bitmap.hpp" 4 | #include "cpputils/exception.hpp" 5 | 6 | namespace cpputils { 7 | 8 | Bitmap loadBitmap(const std::filesystem::path& path) { 9 | BmpHeader bmpHeader(0, 0, 0, 0); 10 | 11 | size_t headerSize = sizeof(BmpHeader); 12 | 13 | std::ifstream stream(path, std::ios::binary); 14 | if (!stream.good()) { 15 | EXCEPTION("Error loading bitmap from " << path); 16 | } 17 | 18 | stream.read(reinterpret_cast(&bmpHeader), headerSize); 19 | 20 | uint32_t channels = bmpHeader.imgHdr.bitCount / 8; 21 | 22 | size_t size[3]; 23 | size[0] = bmpHeader.imgHdr.height; // Rows 24 | size[1] = bmpHeader.imgHdr.width; // Columns 25 | size[2] = channels; 26 | 27 | size_t bytes = size[0] * size[1] * size[2]; 28 | uint8_t* data = new uint8_t[bytes]; 29 | 30 | stream.seekg(bmpHeader.fileHdr.offset); 31 | 32 | size_t rowBytes = size[1] * channels; 33 | size_t paddedRowBytes = static_cast(ceil(0.25 * rowBytes)) * 4; 34 | size_t rowPadding = paddedRowBytes - rowBytes; 35 | 36 | char* ptr = reinterpret_cast(data); 37 | for (size_t row = 0; row < size[0]; ++row) { 38 | stream.read(ptr, rowBytes); 39 | stream.ignore(rowPadding); 40 | ptr += rowBytes; 41 | } 42 | 43 | return Bitmap(data, size); 44 | } 45 | 46 | void saveBitmap(const Bitmap& bitmap, const std::filesystem::path& path) { 47 | std::ofstream stream(path, std::ios::binary); 48 | if (!stream.good()) { 49 | EXCEPTION("Error saving bitmap at " << path); 50 | } 51 | 52 | uint32_t rows = static_cast(bitmap.size()[0]); 53 | uint32_t cols = static_cast(bitmap.size()[1]); 54 | uint16_t channels = static_cast(bitmap.size()[2]); 55 | uint32_t paddedRowSize = static_cast(ceil(0.25 * cols * channels)) * 4; 56 | uint32_t rowPadding = paddedRowSize - cols * channels; 57 | uint32_t rawSize = rows * paddedRowSize * channels; 58 | 59 | BmpHeader bmpHeader(cols, rows, channels, rawSize); 60 | stream.write(reinterpret_cast(&bmpHeader), sizeof(bmpHeader)); 61 | 62 | char zeros[4]; 63 | memset(zeros, 0, 4); 64 | 65 | const uint8_t* ptr = bitmap.data; 66 | for (size_t row = 0; row < rows; ++row) { 67 | stream.write(reinterpret_cast(ptr), cols * channels); 68 | ptr += cols * channels; 69 | 70 | assert(rowPadding <= 4); 71 | stream.write(zeros, rowPadding); 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /tools/cleanbmp/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "cpputils/bitmap.hpp" 2 | #include 3 | #include 4 | 5 | using namespace cpputils; 6 | 7 | void invert(Bitmap& bitmap) { 8 | auto pixels = reinterpret_cast(bitmap.data); 9 | const size_t h = bitmap.size()[0]; 10 | const size_t w = bitmap.size()[1]; 11 | 12 | std::vector buf(w); 13 | 14 | for (size_t i = 0; i < h / 2; ++i) { 15 | memcpy(buf.data(), pixels + i * w, w * sizeof(uint32_t)); 16 | memcpy(pixels + i * w, pixels + (h - 1 - i) * w, w * sizeof(uint32_t)); 17 | memcpy(pixels + (h - 1 - i) * w, buf.data(), w * sizeof(uint32_t)); 18 | } 19 | } 20 | 21 | int main(int argc, char** argv) { 22 | if (argc < 3) { 23 | std::cout << "Usage: " << argv[0] << " input_bmp output_bmp" << std::endl; 24 | return 1; 25 | } 26 | 27 | Bitmap bitmap = loadBitmap(argv[1]); 28 | invert(bitmap); 29 | 30 | saveBitmap(bitmap, argv[2]); 31 | 32 | return 0; 33 | } 34 | --------------------------------------------------------------------------------