GAME10 - Mysteries of the Forgotten Isles

game10.asm

Logic 2D game in VGA graphics, w PC Speaker sound.
Size category: 4096 bytes / 4KB
Bootloader: 512 bytes
Author: Krzysztof Krystian Jankowski
Web: smol.p1x.in/assembly/forgotten-isles/
License: MIT

MEMORY ADDRESSES

1org 0x100 2use16 3 4jmp start 5 6include 'tunes.asm' 7include 'brushes.asm' 8include 'tiles.asm' 9include 'levels.asm' 10include 'vectors.asm' 11include 'palettes.asm' 12 13 14BEEP_BITE equ 3 15BEEP_PICK equ 15 16BEEP_PUT equ 20 17BEEP_GOLD equ 5 18BEEP_STEP equ 25 19BEEP_WEB equ 30 20 22 23_BASE_ equ 0x2000 24_PLAYER_ENTITY_ID_ equ _BASE_ 25_REQUEST_POSITION_ equ _BASE_ + 0x02 26_HOLDING_ID_ equ _BASE_ + 0x04 27_SCORE_ equ _BASE_ + 0x05 28_SCORE_TARGET_ equ _BASE_ + 0x06 29_GAME_TICK_ equ _BASE_ + 0x07 30_GAME_STATE_ equ _BASE_ + 0x09 31_WEB_LOCKED_ equ _BASE_ + 0x0a 32_LAST_TICK_ equ _BASE_ + 0x0b 33_CURRENT_TUNE_ equ _BASE_ + 0x0d 34_NEXT_TUNE_ equ _BASE_ + 0x0f 35_NOTE_TIMER_ equ _BASE_ + 0x11 36_NOTE_TEMPO_ equ _BASE_ + 0x12 37_VECTOR_COLOR_ equ _BASE_ + 0x13 38_LIFE_ equ _BASE_ + 0x15 39_LIFE_TARGET_ equ _BASE_ + 0x16 40_CURRENT_LEVEL_ equ _BASE_ + 0x17 41_MAX_ENTITIES_ equ _BASE_ + 0x18 42_ENTITIES_ equ _BASE_ + 0x40 43 44_BG_BUFFER_MEMORY_ equ 0x3000 45_DBUFFER_MEMORY_ equ 0x4000 46_VGA_MEMORY_ equ 0xA000 47_TICK_ equ 1Ah 48 49_ID_ equ 0 50_POS_ equ 1 51_POS_OLD_ equ 3 52_SCREEN_POS_ equ 5 53_MIRROR_ equ 7 54_STATE_ equ 8 55_DIR_ equ 9 56_ANIM_ equ 10 57
; 2 bytes ; 2 bytes ; 1 byte ; 1 byte ; 1 byte ; 2 bytes ; 1 byte ; 1 byte ; 2 bytes ; 2 bytes ; 2 bytes ; 1 byte ; 1 byte ; 2 bytes ; 1 byte ; 1 byte ; 1 byte ; 1 byte ; 11 bytes per entity, 64 entites cap, 320 bytes ; 64k bytes ; 64k bytes ; 64k bytes ; BIOS tick ; 1 byte ; 2 bytes ; 2 bytes ; 2 bytes ; 1 byte ; 1 bytes ; 1 byte ; 1 byte ; 11 bytes total

MAGIC NUMBERS

59 60ENTITY_SIZE equ 11 61ENTITIES_SPEED equ 2 62AVAILABLE_LEVELS equ 0x3 63LEVEL_START_POSITION equ 320*68+32 64COLOR_SKY equ 0x3c3c 65COLOR_WATER equ 0x3434 66SCORE_POSITION equ 320*24+32 67INTRO_TIME equ 240 68PRE_GAME_TIME equ 64 69POST_GAME_TIME equ 32 70WEB_LOCK equ 2 71COLOR_TRANSPARENT equ 0x0F 72 73ID_PLAYER equ 0 74ID_PALM equ 1 75ID_SNAKE equ 2 76ID_ROCK equ 3 77ID_SKULL equ 4 78ID_BRIDGE equ 5 79ID_CHEST equ 6 80ID_GOLD equ 7 81ID_SPIDER equ 10 82ID_CRAB equ 11 83ID_BUSH equ 12 84 85STATE_DEACTIVATED equ 0 86STATE_FOLLOW equ 2 87STATE_STATIC equ 4 88STATE_EXPLORING equ 8 89STATE_INTERACTIVE equ 16 90 91GSTATE_INTRO equ 2 92GSTATE_PREGAME equ 4 93GSTATE_GAME equ 8 94GSTATE_POSTGAME equ 16 95GSTATE_END equ 32 96GSTATE_WIN equ 64 97GSTATE_NEXT equ 128 98GSTATE_OUTRO equ 256 99 100start: 101

INITIALIZATION

103 104 mov ax, 0x13 105 int 0x10 106 107 mov ax, cs 108 mov ss, ax 109 mov sp, 0xFFFE 110 111 set_keyboard_rate: 112 xor ax, ax 113 xor bx, bx 114 mov ah, 03h 115 mov bl, 1Fh 116 int 16h 117 118restart_game: 119 mov byte [_GAME_STATE_], GSTATE_INTRO 120 mov byte [_CURRENT_LEVEL_], 0x0 121 mov byte [_SCORE_], 0x0 122 mov byte [_LIFE_], 0x3 123 mov byte [_LIFE_TARGET_], 0x3 124 mov word [_GAME_TICK_], 0x0 125 mov byte [_HOLDING_ID_], 0x0 126 mov byte [_WEB_LOCKED_], 0x0 127 mov word [_CURRENT_TUNE_], tune_intro 128 mov word [_NEXT_TUNE_], tune_intro 129 mov byte [_NOTE_TIMER_], 0x0 130 131 132call spawn_entities 133
; Init VGA 320x200x256 ; Video BIOS interrupt ; All segments point to the same memory in .COM ; Stack grows downward from near the top of memory ; BIOS function to set typematic rate and delay ; BL = 31 (0x1F) for maximum repeat rate (30 Hz) and 0 delay ; Call BIOS ; loop intro tune

DRAW BACKGROUND

135 136push _BG_BUFFER_MEMORY_ 137pop es 138xor di,di 139xor si,si 140 141draw_bg: 142 mov ax, COLOR_SKY 143 mov dl, 0xf 144 .draw_sky: 145 mov cx, 320*2 146 rep stosw 147 inc ax 148 xchg al, ah 149 dec dl 150 jnz .draw_sky 151 152 .draw_ocean: 153 mov ax, COLOR_WATER 154 mov dl, 0x4 155 .line: 156 mov cx, 320*16 157 rep stosw 158 dec al 159 xchg al, ah 160 dec dl 161 jnz .line 162 163 mov cx, 320*6 164 rep stosw 165 166
; as target ; Set starting sky color ; 10 bars to draw ; 3 pixels high ; Write to the doublebuffer ; Increment color index for next bar ; Swap colors ; Set starting sky color ; 10 bars to draw ; 3 pixels high ; Write to the doublebuffer ; Increment color index for next bar ; Swap colors

GAME LOGIC

168 169game_loop: 170 push ds 171 172 push _BG_BUFFER_MEMORY_ 173 pop ds 174 xor si, si 175 176 push _DBUFFER_MEMORY_ 177 pop es 178 xor di, di 179 180 mov cx, 0x7D00 181 rep movsw 182 183 pop ds 184 185

INTRO

187 188test byte [_GAME_STATE_], GSTATE_INTRO 189jz skip_game_state_intro 190 191 call play_tune 192 call draw_clouds 193 194 195 mov ah, [_GAME_TICK_] 196 and ah, 0xf 197 add ah, 0x6c 198 mov al, 0x87 199 mov word [_VECTOR_COLOR_], ax 200 201 mov ax, [_GAME_TICK_] 202 cmp ax, 0xf 203 jl .skip_logo_draw 204 mov bp, 320*65+80 205 mov si, LogoVector 206 call draw_vector 207 .skip_logo_draw: 208 209 cmp ax, 0x1f 210 jl .skip_logo2_draw 211 mov si, Logo2Vector 212 call draw_vector 213 .skip_logo2_draw: 214 215 mov word [_VECTOR_COLOR_], 0x0687 216 mov si, PalmVector 217 mov bp, 320*55-18 218 call draw_vector 219 220 mov word [_VECTOR_COLOR_], 0x7876 221 mov si, PalmGreenVector 222 call draw_vector 223 224 add bp, 320*95-32 225 call draw_vector 226 227 add bp, 290 228 call draw_vector 229 230 sub bp, 320*20+20 231 call draw_vector 232 233 sub bp, 320*170-20 234 call draw_vector 235 236 mov word [_VECTOR_COLOR_], 0x1e14 237 mov si, P1XVector 238 mov bp, 320*150+270 239 call draw_vector 240 241 mov ah, 01h 242 int 16h 243 jz .no_key_press 244 .start_game: 245 246 mov byte [_GAME_STATE_], GSTATE_PREGAME 247 add byte [_GAME_STATE_], GSTATE_GAME 248 mov word [_GAME_TICK_], 0x0 249 call draw_level 250 .no_key_press: 251 252skip_game_state_intro: 253
; BIOS keyboard status function ; Call BIOS interrupt

PRE-GAME

255 256test byte [_GAME_STATE_], GSTATE_PREGAME 257jz skip_game_state_pregame 258 259pre_game: 260 mov di, 320*52 261 mov ax, [_GAME_TICK_] 262 cmp ax, PRE_GAME_TIME 263 jg .start_game 264 shr ax, 1 265 add di, ax 266 mov bx, 0x1 267 call draw_ship 268 call play_tune 269 jmp skip_game_state_pregame 270 271 .start_game: 272 mov byte [_GAME_STATE_], GSTATE_GAME 273 mov word [_GAME_TICK_], 0x0 274 275 .clear_kb_buffer: 276 mov ah, 0x01 277 int 0x16 278 jz .cleared 279 mov ah, 0x00 280 int 0x16 281 jmp .clear_kb_buffer 282 283 .cleared: 284 285skip_game_state_pregame: 286

GAME

288 289test byte [_GAME_STATE_], GSTATE_GAME 290jz skip_game_state_game 291

STOP SOUND

293stop_game_sound: 294 test byte [_GAME_STATE_], GSTATE_PREGAME 295 jnz .skip_stop_sound 296 test byte [_GAME_STATE_], GSTATE_POSTGAME 297 jnz .skip_stop_sound 298 call stop_beep 299 .skip_stop_sound: 300 301 302test byte [_GAME_STATE_], GSTATE_PREGAME 303jnz skip_keyboard 304test byte [_GAME_STATE_], GSTATE_POSTGAME 305jnz skip_keyboard 306

DRAW SHIP

308 309draw_ship_in_game: 310 mov di, 320*52+32 311 xor bx, bx 312 call draw_ship 313

KEYBOARD INPUT

cmp cl, 0x0 jz .invalid_move jmp .check_move
315 316check_keyboard: 317 mov ah, 01h 318 int 16h 319 jz .no_key_press 320 321 mov ah, 00h 322 int 16h 323 324 mov si, [_PLAYER_ENTITY_ID_] 325 mov cx, [si+_POS_] 326 mov bx, [si+_POS_OLD_] 327 cmp cx, bx 328 jnz .no_key_press 329 330 331 .check_spacebar: 332 cmp ah, 39h 333 jne .check_up 334 cmp byte [_HOLDING_ID_], 0x0 335 jz .set_request_position_to_player 336 mov byte [_HOLDING_ID_], 0x0 337 jmp .no_key_press 338 .set_request_position_to_player: 339 mov word [_REQUEST_POSITION_], cx 340 jmp .no_key_press 341 .check_up: 342 cmp ah, 48h 343 jne .check_down 344 cmp ch, 0x0 345 jz .invalid_move 346 dec ch 347 jmp .check_move 348 349 .check_down: 350 cmp ah, 50h 351 jne .check_left 352 inc ch 353 jmp .check_move 354 355 .check_left: 356 cmp ah, 4Bh 357 jne .check_right 360 dec cl 361 mov byte [si+_MIRROR_], 0x1 362 jmp .check_move 363 364 365 .check_right: 366 cmp ah, 4Dh 367 jne .no_key_press 368 inc cl 369 mov byte [si+_MIRROR_], 0x0 371 372 .check_move: 373 call check_friends 374 jz .no_move 375 call check_water_tile 376 jz .no_move 377 call check_bounds 378 jz .no_move 379 380 .move: 381 cmp byte [_WEB_LOCKED_], 0 382 jz .skip_web_check 383 dec byte [_WEB_LOCKED_] 384 mov bl, BEEP_WEB 385 call beep 386 jmp .no_move 387 .skip_web_check: 388 mov word [si+_POS_], cx 389 mov bl, BEEP_STEP 390 call beep 391 392 .no_move: 393 mov word [_REQUEST_POSITION_], cx 394 395 .no_key_press: 396 .invalid_move: 397 398skip_keyboard: 399
; BIOS keyboard status function ; Call BIOS interrupt ; Jump if Zero Flag is set (no key pressed) ; BIOS keyboard read function ; Call BIOS interrupt ; Load player position into CX (Y in CH, X in CL) ; Compare scan code with spacebar ; Compare scan code with up arrow ; Compare scan code with down arrow ; Compare scan code with left arrow ; Compare scan code with right arrow

AI ENITIES

401 402ai_entities: 403 mov si, _ENTITIES_ 404 mov byte cl, [_MAX_ENTITIES_] 405 .next_entity: 406 push cx 407 408 cmp byte [si+_STATE_], STATE_EXPLORING 409 jnz .skip_explore 410 411 mov ax, [si+_POS_] 412 mov bx, [si+_POS_OLD_] 413 cmp ax, bx 414 jnz .skip_explore 415 416 .explore: 417 mov cx, [si+_POS_] 418 mov al, [si+_DIR_] 419 420 .check_horizontal: 421 cmp al, 0 422 jnz .go_left 423 .go_right: 424 inc cl 425 jmp .check_mirror 426 .go_left: 427 cmp al, 1 428 jnz .check_vertical 429 dec cl 430 jmp .check_mirror 431 432 .check_vertical: 433 cmp al, 2 434 jnz .go_up 435 .go_down: 436 inc ch 437 jmp .check_mirror 438 .go_up: 439 cmp al, 3 440 jnz .check_mirror 441 dec ch 442 443 .check_mirror: 444 mov byte [si+_MIRROR_], 0x0 445 cmp byte cl, [si+_POS_] 446 jg .skip_mirror_x 447 mov byte [si+_MIRROR_], 0x1 448 .skip_mirror_x: 449 450 call check_bounds 451 jz .can_not_move 452 453 call check_friends 454 jz .can_not_move 455 456 call check_water_tile 457 jz .can_not_move 458 459 .move_to_new_pos: 460 mov word [si+_POS_], cx 461 jmp .after_move 462 463 .can_not_move: 464 465 .check_if_crab: 466 cmp byte [si+_ID_], ID_CRAB 467 jnz .not_a_crab 468 xor byte [si+_DIR_], 1 469 jmp .skip_random_bounce 470 .not_a_crab: 471 472 .random_bounce: 473 in al, 0x40 474 add ax, [_GAME_TICK_] 475 and al, 0x3 476 mov byte [si+_DIR_], al 477 478 .skip_random_bounce: 479 480 .check_if_player: 481 cmp cx, [_REQUEST_POSITION_] 482 jnz .no_bite 483 cmp byte [_HOLDING_ID_], 0x0 484 jnz .continue_game 485 mov byte [_HOLDING_ID_], 0xff 486 mov bl, BEEP_BITE 487 call beep 488 489 cmp byte [si+_ID_], ID_SNAKE 490 jz .snake_bite 491 cmp byte [si+_ID_], ID_SPIDER 492 jz .spider_web 493 cmp byte [si+_ID_], ID_CRAB 494 jz .crab_bite 495 jmp .continue_game 496 497 .crab_bite: 498 .snake_bite: 499 dec byte [_LIFE_] 500 jnz .continue_game 501 502 mov byte [_GAME_STATE_], GSTATE_END 503 mov word [_CURRENT_TUNE_], tune_end 504 mov word [_NEXT_TUNE_], tune_end 505 mov byte [_NOTE_TIMER_], 0x0 506 mov byte [_NOTE_TEMPO_], 0xa 507 jmp .skip_item 508 509 .spider_web: 510 mov byte [_WEB_LOCKED_] , WEB_LOCK 511 jmp .continue_game 512 513 .continue_game: 514 mov byte [_HOLDING_ID_], 0x00 515 jmp .skip_item 516 .no_bite: 517 .after_move: 518 .skip_explore: 519 520 cmp byte [si+_STATE_], STATE_INTERACTIVE 521 jnz .skip_item 522 mov cx, [si+_POS_] 523 cmp cx, [_REQUEST_POSITION_] 524 jnz .skip_item 525 526 cmp byte [si+_ID_], ID_BRIDGE 527 jz .check_bridge 528 cmp byte [si+_ID_], ID_CHEST 529 jnz .skip_check_interactions 530 531 .check_interactions: 532 cmp byte [_HOLDING_ID_], ID_GOLD 533 jnz .skip_item 534 inc byte [_SCORE_] 535 mov bl, BEEP_GOLD 536 call beep 537 jmp .clear_item 538 539 .check_bridge: 540 cmp byte [_HOLDING_ID_], ID_ROCK 541 jnz .skip_item 542 mov byte [si+_STATE_], STATE_DEACTIVATED 543 .clear_item: 544 mov byte [_HOLDING_ID_], 0xff 545 jmp .skip_item 546 .skip_check_interactions: 547 548 cmp byte [_HOLDING_ID_], 0x0 549 jnz .skip_item 550 .pick_item: 551 mov byte [si+_STATE_], STATE_FOLLOW 552 mov word [_REQUEST_POSITION_], 0x0 553 mov byte cl, [si+_ID_] 554 mov byte [_HOLDING_ID_], cl 555 mov bl, BEEP_PICK 556 call beep 557 .skip_item: 558 559 .put_item_back: 560 cmp byte [si+_STATE_], STATE_FOLLOW 561 jnz .no_follow 562 563 cmp byte [_HOLDING_ID_], 0x0 564 jnz .check_kill 565 mov byte [si+_STATE_], STATE_INTERACTIVE 566 mov word [_REQUEST_POSITION_], 0x0 567 jmp .beep 568 569 .check_kill: 570 cmp byte [_HOLDING_ID_], 0xff 571 jnz .skip_kill 572 mov byte [si+_STATE_], STATE_DEACTIVATED 573 mov byte [_HOLDING_ID_], 0x0 574 .beep: 575 mov bl, BEEP_PUT 576 call beep 577 .skip_kill: 578 .no_follow: 579 580 add si, ENTITY_SIZE 581 pop cx 582 dec cx 583 jnz .next_entity 584
; Crab ; Snake ; Spider ; Check if player is holding something

SORT ENITIES

Sort entities by Y position Expects: entities array Returns: sorted entities array
589 590sort_entities: 591 mov cl, [_MAX_ENTITIES_] 592 dec cl 593 .outer_loop: 594 push cx 595 mov si, _ENTITIES_ 596 597 .inner_loop: 598 push cx 599 mov bx, [si+_POS_] 600 mov dx, [si+ENTITY_SIZE+_POS_] 601 602 cmp bh, dh 603 jle .no_swap 604 605 mov di, si 606 add di, ENTITY_SIZE 607 608 mov ax, [_PLAYER_ENTITY_ID_] 609 cmp ax, si 610 jne .check_next_entity 611 mov [_PLAYER_ENTITY_ID_], di 612 jmp .swap_entities 613 .check_next_entity: 614 cmp ax, di 615 jne .swap_entities 616 mov [_PLAYER_ENTITY_ID_], si 617 .swap_entities: 618 619 mov cx, ENTITY_SIZE 620 .swap_loop: 621 mov al, [si] 622 xchg al, [di] 623 mov [si], al 624 inc si 625 inc di 626 loop .swap_loop 627 sub si, ENTITY_SIZE 628 629 .no_swap: 630 add si, ENTITY_SIZE 631 pop cx 632 loop .inner_loop 633 634 pop cx 635 loop .outer_loop 636
; We'll do n-1 passes ; Get Y of current entity ; Get Y of next entity ; Compare Y values

DRAW ENITIES

smooth move here
638 639draw_entities: 640 mov si, _ENTITIES_ 641 mov byte cl, [_MAX_ENTITIES_] 642 .next: 643 push cx 644 push si 645 646 cmp byte [si+_STATE_], STATE_DEACTIVATED 647 jz .skip_entity 648 649 test byte [_GAME_STATE_], GSTATE_PREGAME 650 jnz .hide_player 651 test byte [_GAME_STATE_], GSTATE_POSTGAME 652 jnz .hide_player 653 jmp .skip_hide_player 654 .hide_player: 655 cmp byte [si+_ID_], ID_PLAYER 656 jz .skip_entity 657 cmp byte [si+_ID_], ID_CHEST 658 jz .skip_entity 659 .skip_hide_player: 660 662 mov ax, [si+_POS_OLD_] 663 mov cx, [si+_POS_] 664 cmp cx, ax 665 jz .in_position 666 667 xor bx, bx 668 669 cmp al, cl 670 jl .move_left 671 jg .move_right 672 cmp ah, ch 673 jl .move_down 674 jg .move_up 675 676 jmp .in_position 677 678 .move_left: 679 add bx, ENTITIES_SPEED 680 jmp .save_move 681 682 .move_right: 683 sub bx, ENTITIES_SPEED 684 jmp .save_move 685 686 .move_down: 687 add bx, 320*ENTITIES_SPEED 688 jmp .save_move 689 690 .move_up: 691 sub bx, 320*ENTITIES_SPEED 692 jmp .save_move 693 694 .save_move: 695 mov cx, [si+_POS_] 696 call conv_pos2mem 697 mov dx, di 698 add [si+_SCREEN_POS_], bx 699 mov di, [si+_SCREEN_POS_] 700 cmp dx, di 701 jnz .pos_calculated 702 mov cx, [si+_POS_] 703 mov [si+_POS_OLD_], cx 704 mov byte [si+_ANIM_], 0x0 705 cmp byte [si+_ID_], ID_PLAYER 706 jnz .skip_step_beep 707 mov bl, BEEP_STEP 708 call beep 709 .skip_step_beep: 710 711 712 .in_position: 713 call conv_pos2mem 714 .pos_calculated: 715 cmp byte [si+_STATE_], STATE_FOLLOW 716 jnz .skip_follow 717 push si 718 mov si, [_PLAYER_ENTITY_ID_] 719 mov cx, [si+_POS_] 720 mov di, [si+_SCREEN_POS_] 721 sub di, 320*14 722 pop si 723 mov word [si+_POS_], cx 724 jmp .skip_draw_shadow 725 .skip_follow: 726 727 728 .draw_shadow: 729 test byte [si+_STATE_], STATE_EXPLORING 730 jnz .skip_draw_shadow 731 cmp byte [si+_ID_], ID_PLAYER 732 jz .skip_draw_shadow 733 cmp byte [si+_ID_], ID_BRIDGE 734 jz .skip_draw_shadow 735 push si 736 push di 737 mov si, ShadowBrush 738 add di, 320*5+1 739 call draw_sprite 740 pop di 741 pop si 742 .skip_draw_shadow: 743 744 mov byte al, [si] 745 mov ah, al 746 747 shl al, 0x2 748 mov bx, BrushRefs 749 add bl, al 750 mov dx, [bx] 751 752 push dx 753 754 add al, 0x2 755 movzx bx, al 756 add di, [BrushRefs + bx] 757 mov dl, [si+_MIRROR_] 758 759 cmp ah, ID_PLAYER 760 jnz .skip_player_anim 761 mov ax, [si+_POS_] 762 cmp ax, [si+_POS_OLD_] 763 mov ah, ID_PLAYER 764 jz .skip_player_anim 765 xor byte [si+_ANIM_], 0x1 766 .skip_player_anim: 767 768 mov dh, [si+_ANIM_] 769 770 pop si 771 call draw_sprite 772 773 cmp ah, ID_PLAYER 774 jnz .skip_player_draw 775 mov si, IndieTopBrush 776 sub di, 320*4 777 778 cmp dh, 0x1 779 jnz .skip_anim_move 780 add di, 320 781 .skip_anim_move: 782 783 cmp byte [_HOLDING_ID_], 0x0 784 jz .skip_player_holding 785 mov si, IndieTop2Brush 786 .skip_player_holding: 787 788 xor dh, dh 789 call draw_sprite 790 791 cmp byte [_WEB_LOCKED_], 0 792 jz .skip_web_draw 793 mov si, WebBrush 794 add di, 320*6 795 call draw_sprite 796 .skip_web_draw: 797 .skip_player_draw: 798 799 cmp ah, ID_GOLD 800 jnz .skip_gold_draw 801 mov ax, [_GAME_TICK_] 802 add ax, cx 803 and ax, 0x4 804 cmp ax, 0x2 805 jl .skip_gold_draw 806 xor dx, dx 807 mov si, GoldBrush 808 call draw_sprite 809 .skip_gold_draw: 810 811 cmp ah, ID_CRAB 812 jnz .skip_crab 813 mov dx, 0 814 mov si, CrabClawBrush 815 add di, 8 816 call draw_sprite 817 mov dx, 1 818 sub di, 320+16 819 call draw_sprite 820 .skip_crab: 821 822 cmp ah, ID_CHEST 823 jnz .skip_chest 824 xor dx,dx 825 cmp byte [_HOLDING_ID_], ID_GOLD 826 jnz .skip_open_chest 827 mov si, ChestTopBrush 828 sub di, 320*3+8 829 call draw_sprite 830 831 mov si, ArrowBrush 832 sub di, 320*8-8 833 mov ax, [_GAME_TICK_] 834 and ax, 0x1 835 imul ax, 320*2 836 add di, ax 837 call draw_sprite 838 jmp .skip_chest 839 .skip_open_chest: 840 mov si, ChestCloseBrush 841 sub di, 320 842 call draw_sprite 843 .skip_chest: 844 845 .skip_entity: 846 pop si 847 add si, ENTITY_SIZE 848 pop cx 849 dec cx 850 jg .next 851
; screen pos in DI ; Load player position into CX (Y in CH, X in CL) ; Move above player ; Save new position to holding item ; Get brush id in AL ; Save a copy in AH ; Get brush reference table ; Shift to ref (id*2 bytes) ; Get brush data address ; Save address for SI ; offest is at next byte (+2) ; Get address to BX ; Get shift and apply to destination position ; Get brush mirror flag ; Get anination frame ; Get address ; move head down on second frame ; no mirror

CHECK SCORE

853 854test byte [_GAME_STATE_], GSTATE_PREGAME 855jnz skip_score 856test byte [_GAME_STATE_], GSTATE_POSTGAME 857jnz skip_score 858 859check_score: 860 mov di, SCORE_POSITION 861 mov al, [_SCORE_TARGET_] 862 mov ah, [_SCORE_] 863 cmp al, ah 864 jg .continue_game 865 add byte [_GAME_STATE_], GSTATE_POSTGAME 866 mov word [_GAME_TICK_], 0x0 867 mov word [_CURRENT_TUNE_], tune_win 868 mov word [_NEXT_TUNE_], tune_win 869 mov byte [_NOTE_TIMER_], 0x0 870 mov byte [_NOTE_TEMPO_], 0x2 871 .continue_game: 872

DRAW SCORE

874draw_score_ui: 875mov si, CoinVector 876mov bp, 320*10+100 877xor cl, cl 878 .draw_spot: 879 mov word [_VECTOR_COLOR_], 0x1213 880 cmp cl, ah 881 jge .skip_gold_draw 882 mov word [_VECTOR_COLOR_], 0x4344 883 .skip_gold_draw: 884 add bp, 22 885 call draw_vector 886 add di, 0xa 887 inc cl 888 cmp al, cl 889 jnz .draw_spot 890 891 892draw_life_ui: 893 mov ah, [_LIFE_] 894 mov al, [_LIFE_TARGET_] 895 896 mov si, LifeUIVector 897 mov bp, 320*10+10 898 xor cl, cl 899 .draw_life_spot: 900 mov word [_VECTOR_COLOR_], 0x8482 901 cmp cl, ah 902 jge .skip_life_draw 903 mov word [_VECTOR_COLOR_], 0x2321 904 .skip_life_draw: 905 add bp, 22 906 call draw_vector 907 add di, 0xa 908 inc cl 909 cmp al, cl 910 jnz .draw_life_spot 911 912skip_score: 913skip_game_state_game: 914 915

POST GAME

917 918test byte [_GAME_STATE_], GSTATE_POSTGAME 919jz skip_game_state_postgame 920draw_post_game: 921 mov ax, [_GAME_TICK_] 922 shr ax, 1 923 cmp ax, POST_GAME_TIME 924 jnz .move_ship 925 inc byte [_CURRENT_LEVEL_] 926 cmp byte [_CURRENT_LEVEL_], AVAILABLE_LEVELS 927 jz restart_game 928 mov byte [_GAME_STATE_], GSTATE_GAME+GSTATE_PREGAME 929 mov byte [_SCORE_], 0x0 930 mov word [_GAME_TICK_], 0x0 931 mov byte [_HOLDING_ID_], 0x0 932 mov byte [_WEB_LOCKED_], 0x0 933 mov word [_CURRENT_TUNE_], tune_intro 934 mov word [_NEXT_TUNE_], tune_intro 935 mov byte [_NOTE_TIMER_], 0x0 936 call draw_level 937 call spawn_entities 938 .move_ship: 939 mov di, 320*52+32 940 add di, ax 941 mov bx, 0x1 942 call draw_ship 943 call play_tune 944 945 946skip_game_state_postgame: 947
; loop intro tune

GAME END

949 950test byte [_GAME_STATE_], GSTATE_END 951jz skip_game_state_end 952 call play_tune 953 mov di, 320*100+154 954 mov si, SkullBrush 955 test byte [_GAME_STATE_], GSTATE_WIN 956 jz .draw_icon 957 mov si, GoldBrush 958 .draw_icon: 959 call draw_sprite 960 961 mov ah, 01h 962 int 16h 963 jz .no_key_press 964 .restart_game: 965 test byte [_GAME_STATE_], GSTATE_WIN 966 jz restart_game 967 968 .no_key_press: 969skip_game_state_end: 970
; BIOS keyboard status function ; Call BIOS interrupt

VGA BLIT PROCEDURE

972 973vga_blit: 974 push es 975 push ds 976 977 push _VGA_MEMORY_ 978 pop es 979 push _DBUFFER_MEMORY_ 980 pop ds 981 xor si,si 982 xor di,di 983 984 mov cx,0x7D00 985 rep movsw 986 987 pop ds 988 pop es 989 990
; Set VGA memory ; as target ; Set doublebuffer memory ; as source ; Clear SI ; Clear DI ; Half of 320x200 pixels ; Push words (2x pixels)

GAME TICK

992 993wait_for_tick: 994 xor ax, ax 995 int _TICK_ 996 mov bx, dx 997.wait_loop: 998 int _TICK_ 999 cmp dx, bx 1000 je .wait_loop 1001 1002inc word [_GAME_TICK_] 1003
; Function 00h: Read system timer counter ; Returns tick count in CX:DX ; Store the current tick count ; Read the tick count again ; Loop until the tick count changes ; Increment game tick

ESC OR LOOP

1005 1006 in al, 0x60 1007 dec al 1008 jnz game_loop 1009
; Read keyboard ; Decrement AL (esc is 1, after decrement is 0) ; If not zero, loop again

TERMINATE PROGRAM

1011 1012 exit: 1013

BEEP STOP

1015 1016 call stop_beep 1017 mov ax, 0x4c00 1018 int 0x21 1019 ret 1020 1021
; Return to BIOS/DOS

BEEP PC SPEAKER

Set the speaker frequency Expects: BX - frequency value Return: - xor bh, bh
1026 1027beep: 1029 mov al, 0xB6 1030 out 0x43, al 1031 mov ah, bl 1032 out 0x42, al 1033 mov al, ah 1034 out 0x42, al 1035 in al, 0x61 1036 or al, 0x03 1037 out 0x61, al 1038ret 1039 1040stop_beep: 1041 in al, 0x61 1042 and al, 0x0FC 1043 out 0x61, al 1044ret 1045
; Command to set the speaker frequency ; Write the command to the PIT chip ; Frequency value ; Write the low byte of the frequency value ; Write the high byte of the frequency value ; Read the PIC chip ; Set bit 0 to enable the speaker ; Write the updated value back to the PIC chip ; Read the PIC chip ; Clear bit 0 to disable the speaker ; Write the updated value back to the PIC chip

PLAY TUNE

1047play_tune: 1048 cmp byte [_NOTE_TIMER_], 0x0 1049 jz .new_note 1050 dec byte [_NOTE_TIMER_] 1051 jmp .done 1052 .new_note: 1053 inc word [_CURRENT_TUNE_] 1054 mov si, [_CURRENT_TUNE_] 1055 mov bl, [si] 1056 cmp bl, 0 1057 jnz .skip_loop 1058 mov ax, [_NEXT_TUNE_] 1059 mov word [_CURRENT_TUNE_], ax 1060 mov si, ax 1061 mov bl, [si] 1062 .skip_loop: 1063 mov byte al, [_NOTE_TEMPO_] 1064 mov byte [_NOTE_TIMER_], al 1065 call beep 1066 .done: 1067ret 1068 1069draw_clouds: 1070 mov word [_VECTOR_COLOR_], 0x5456 1071 mov bp, [_GAME_TICK_] 1072 shr bp, 4 1073 mov si, CloudsVector 1074 call draw_vector 1075ret
; Loop to begining of the tune

CNVERT XY TO MEM

Expects: CX - position YY/XX Return: DI memory position
1079 1080conv_pos2mem: 1081 mov di, LEVEL_START_POSITION 1082 xor ax, ax 1083 mov al, ch 1084 imul ax, 320*8 1085 xor dh, dh 1086 mov dl, cl 1087 shl dx, 3 1088 add ax, dx 1089 add di, ax 1090ret 1091 1092
; Clear AX ; Move Y coordinate to AL ; Clear DH ; Move X coordinate to DL ; DX = X * 8 ; AX = Y * 2560 + X * 8 ; Move result to DI

SPAWN ENTITIES

Expects: entities array from level data Returns: entities in memory array
1096 1097spawn_entities: 1098 mov si, Levels 1099 mov ax, [_CURRENT_LEVEL_] 1100 shl ax, 1 1101 add si, ax 1102 mov ax, [si] 1103 add ax, 128 1104 mov si, ax 1105 mov di, _ENTITIES_ 1106 mov byte [_MAX_ENTITIES_], 0x0 1107 1108 .next_entity: 1109 mov bl, [si] 1110 cmp bl, 0x0 1111 jz .done 1112 1113 1114 1115 dec bl 1116 1117 inc si 1118 mov al, [si] 1119 inc si 1120 mov cl, al 1121 1122 cmp bl, ID_GOLD 1123 jnz .not_gold 1124 mov [_SCORE_TARGET_], cl 1125 .not_gold: 1126 1127 .next_in_group: 1128 mov byte [di], bl 1129 mov ax, [si] 1130 mov [di+_POS_], ax 1131 mov [di+_POS_OLD_], ax 1132 1133 push cx 1134 push di 1135 mov cx, ax 1136 call conv_pos2mem 1137 mov ax, di 1138 pop di 1139 pop cx 1140 mov [di+_SCREEN_POS_], ax 1141 1142 mov byte [di+_MIRROR_], 0x0 1143 mov byte [di+_STATE_], STATE_STATIC 1144 mov byte [di+_DIR_], 0x0 1145 mov byte [di+_ANIM_], 0x0 1146 1147 cmp bl, ID_SNAKE 1148 jz .set_explore 1149 cmp bl, ID_CRAB 1150 jz .set_explore 1151 cmp bl, ID_SPIDER 1152 jz .set_explore 1153 jmp .skip_explore 1154 .set_explore: 1155 mov byte [di+_STATE_], STATE_EXPLORING 1156 .skip_explore: 1157 1158 cmp bl, ID_PALM 1159 jz .set_rand_mirror 1160 cmp bl, ID_BUSH 1161 jz .set_rand_mirror 1162 jnz .skip_mirror 1163 .set_rand_mirror: 1164 xor al, ah 1165 and al, 0x01 1166 mov byte [di+_MIRROR_], al 1167 .skip_mirror: 1168 1169 cmp bl, ID_BRIDGE 1170 jz .set_interactive 1171 cmp bl, ID_GOLD 1172 jz .set_interactive 1173 cmp bl, ID_ROCK 1174 jz .set_interactive 1175 cmp bl, ID_CHEST 1176 jz .set_interactive 1177 jmp .skip_interactive 1178 .set_interactive: 1179 mov byte [di+_STATE_], STATE_INTERACTIVE 1180 .skip_interactive: 1181 1182 add si, 0x02 1183 add di, ENTITY_SIZE 1184 inc byte [_MAX_ENTITIES_] 1185 loop .next_in_group 1186 jmp .next_entity 1187 .done: 1188 1189 mov word [_PLAYER_ENTITY_ID_], _ENTITIES_ 1190 1191ret 1192
; Get first word (ID) ; Check for last entity marker ; Conv level id to game id ; Get amount in group ; mov to the next word (first entitie in group) ; Set loop ; Check if gold coin ; Count each gold as score target ; Save sprite id ; Get position ; Save position ; Save old position ; Save mirror (none) ; Save basic state ; Save basic state ; Set explore state to alive entities ; Random X mirror, for foliage ; Set interactive entities ; Move to the next entity in code ; Move to the next entity in memory ; Set player entity id to first entity

CHECK BOUNDS

Expects: CX - Position YY/XX (CH: Y coordinate, CL: X coordinate) Returns: AX - Zero if hit bound, 1 if no bounds at this location
1196 1197check_bounds: 1198 xor ax, ax 1199 cmp ch, 0x0f 1200 ja .return 1201 cmp cl, 0x1f 1202 ja .return 1203 inc ax 1204.return: 1205 test ax, 1 1206 ret 1207
; Assume bound hit (AX = 0) ; No bound hit (AX = 1) ; Set flags based on AX

CHECK FRIEDS

Expects: CX - Position YY/XX DL - skip check Return: AX - Zero if hit entity, 1 if clear
1212 1213check_friends: 1214 push si 1215 push cx 1216 xor bx, bx 1217 mov ax, cx 1218 1219 mov byte cl, [_MAX_ENTITIES_] 1220 mov si, _ENTITIES_ 1221 .next_entity: 1222 cmp byte [si+_STATE_], STATE_FOLLOW 1223 jle .skip_this_entity 1224 cmp word [si+_POS_], ax 1225 jz .hit 1226 .skip_this_entity: 1227 add si, ENTITY_SIZE 1228 loop .next_entity 1229 1230 jmp .done 1231 1232 .hit: 1233 mov bx, 0x1 1234 cmp bx, 0x1 1235 1236 .done: 1237 pop cx 1238 pop si 1239 1240ret 1241

CHECK WATER TILE

Expects: CX - Player position (CH: Y 0-15, CL: X 0-31) Returns: AL - 0 if water (0xF), 1 otherwise
1245 1246check_water_tile: 1247 mov ax, cx 1248 shr ah, 1 1249 shr al, 1 1250 movzx bx, ah 1251 shl bx, 4 1252 add bl, al 1253 add bx, LevelData 1254 mov al, [bx] 1255 test al, 0x40 1256ret 1257 1258
; Copy position to AX ; Y / 2 ; X / 2 to convert to tile position ; Multily by 16 tiles wide ; Y / 2 * 16 + X / 2 ; Read tile ; Check if movable (7th bit set)

DRAWING LEVEL

1260draw_level: 1261 push es 1262 push _BG_BUFFER_MEMORY_ 1263 pop es 1264 1265 mov si, LevelData 1266 mov di, LEVEL_START_POSITION 1267 xor cx, cx 1268 .next_meta_tile: 1269 push cx 1270 push si 1271 1272 mov byte al, [si] 1273 mov bl, al 1274 shr bl, 0x4 1275 and bl, 0x3 1276 1277 and ax, 0xf 1278 jnz .not_empty 1279 add di, 16 1280 jmp .skip_meta_tile 1281 .not_empty: 1282 1283 mov si, MetaTiles 1284 shl ax, 0x2 1285 add si, ax 1286 1287 mov dx, 0x0123 1288 .check_y: 1289 test bl, 2 1290 jz .check_x 1291 xchg dh, dl 1292 .check_x: 1293 test bl, 1 1294 jz .push_tiles 1295 ror dh, 4 1296 ror dl, 4 1297 1298 .push_tiles: 1299 mov cx, 4 1300 .next_tile_push: 1301 push dx 1302 ror dx, 4 1303 loop .next_tile_push 1304 1305 mov cx, 0x4 1306 .next_tile: 1307 pop dx 1308 and dx, 0x7 1309 push si 1310 add si, dx 1311 mov byte al, [si] 1312 pop si 1313 mov bh, al 1314 shr bh, 4 1315 and bh, 3 1316 1317 xor bh, bl 1318 mov dl, bh 1319 1320 and ax, 0xf 1321 dec ax 1322 imul ax, 18 1323 1324 push si 1325 mov si, TerrainTiles 1326 add si, ax 1327 call draw_sprite 1328 pop si 1329 1330 add di, 8 1331 1332 cmp cx, 0x3 1333 jnz .skip_set_new_line 1334 add di, 320*8-16 1335 .skip_set_new_line: 1336 1337 loop .next_tile 1338 sub di, 320*8 1339 .skip_meta_tile: 1340 1341 pop si 1342 inc si 1343 pop cx 1344 inc cx 1345 test cx, 0xf 1346 jnz .no_new_line 1347 add di, 320*16-(16*16) 1348 .no_new_line: 1349 1350 cmp cx, 0x80 1351 jl .next_meta_tile 1352 1353 pop es 1354ret 1355 1356
; as target ; Read level cell ; Make a copy ; Remove first nible ; Read XY mirroring - BL ; Read first nibble - AX ; ID*4 Move to position; 4 bytes per tile ; Meta-tile address ; Default order: 0, 1, 2, 3 ; Swap top and bottom rows (Order: 2, 3, 0, 1) ; Swap nibbles in dh (tiles in positions 0 and 1) ; Swap nibbles in dl (tiles in positions 2 and 3) ; 4 tiles to push ; Push the tile ID ; Rotate dx to get the next tile ID in place ; 2x2 tiles ; Get tile order ; Read meta-tile with order ; Extract the upper 4 bits ; Mask to get the mirror flags (both X and Y) ; invert original tile mirror by meta-tile mirror ; set final mirror for tile ; First nibble ; We do not have tile 0, shifting values ; Move to position ; Word wrap ; Move to the next display line ; 128 = 16*8

DRAWING SHIP

in: bx - wiosla
1359 1360draw_ship: 1361 xor dx, dx 1362 1363 mov ax, [_GAME_TICK_] 1364 and ax, 0xf 1365 cmp ax, 0x6 1366 jl .skip_wave 1367 sub di, 320 1368 .skip_wave: 1369 mov si, ShipBackBrush 1370 call draw_sprite 1371 add di, 8 + 320*4 1372 mov si, ShipMiddleBrush 1373 call draw_sprite 1374 add di, 8 1375 mov si, ShipMiddleBrush 1376 call draw_sprite 1377 add di, 8 1378 mov si, ShipFrontBrush 1379 call draw_sprite 1380 1381 mov si, ShipSailBrush 1382 sub di, 12 + 320*5 1383 call draw_sprite 1384 add di, 320*2 - 4 1385 call draw_sprite 1386 sub di, 320*7 - 3 1387 call draw_sprite 1388 add di, 320*2 - 4 1389 call draw_sprite 1390 sub di, 320*6 - 1 1391 call draw_sprite 1392 1393 add di, 320*10 + 10 1394 call draw_sprite 1395 sub di, 320*4 + 2 1396 call draw_sprite 1397 1398 cmp bx, 0x1 1399 jnz .skip_wiosla 1400 xor dx, dx 1401 mov ax, [_GAME_TICK_] 1402 shr ax, 3 1403 and ax, 0x1 1404 add dl, al 1405 sub di, ax 1406 sub di, ax 1407 sub di, ax 1408 mov si, WioslaBrush 1409 add di, 320*15-7 1410 call draw_sprite 1411 add di, 8 1412 call draw_sprite 1413 .skip_wiosla: 1414ret 1415

DRAW VECTOR

shadow double line
1417draw_vector: 1418 pusha 1419 .read_group: 1420 xor cx, cx 1421 mov cl, [si] 1422 cmp cl, 0x0 1423 jz .done 1424 1425 inc si 1426 1427 .read_line: 1428 push cx 1429 1430 xor ax, ax 1431 mov al, [si] 1432 add ax, bp 1433 1434 xor bx, bx 1435 mov bl, [si+2] 1436 add bx, bp 1437 mov dl, [si+1] 1438 mov dh, [si+3] 1439 mov cx, [_VECTOR_COLOR_] 1440 mov ch, cl 1441 1443 call draw_line 1444 1445 mov cx, [_VECTOR_COLOR_] 1446 mov cl, ch 1447 dec ax 1448 dec bx 1449 dec dh 1450 dec dl 1451 call draw_line 1452 1454 inc cl 1455 inc ch 1456 dec ax 1457 dec bx 1458 call draw_line 1459 1460 add si, 2 1461 pop cx 1462 loop .read_line 1463 add si, 2 1464 jmp .read_group 1465 .done: 1466 popa 1467 ret 1468
; shadow color ; line color

DRAWING LINE

axx0

bxx1

dly0,

dhy1,

clcol

Spektre @ https://stackoverflow.com/questions/71390507/line-drawing-algorithm-in-assembly#71391899
1476draw_line: 1477 pusha 1478 push ax 1479 mov si,bx 1480 sub si,ax 1481 sub ah,ah 1482 mov al,dl 1483 mov bx,ax 1484 mov al,dh 1485 sub ax,bx 1486 mov di,ax 1487 mov ax,320 1488 sub dh,dh 1489 mul dx 1490 pop bx 1491 add ax,bx 1492 mov bp,ax 1493 mov ax,1 1494 mov bx,320 1495 cmp si,32768 1496 jb .r0 1497 neg si 1498 neg ax 1499 .r0: cmp di,32768 1500 jb .r1 1501 neg di 1502 neg bx 1503 .r1: cmp si,di 1504 ja .r2 1505 xchg ax,bx 1506 xchg si,di 1507 .r2: mov [.ct],si 1508 .l0: mov word [es:bp], cx 1509 add bp,ax 1510 sub dx,di 1511 jnc .r3 1512 add dx,si 1513 add bp,bx 1514 .r3: dec word [.ct] 1515 jnz .l0 1516 popa 1517 ret 1518 .ct: dw 0 1519 1520

DRAW SPRITE PROCEDURE

Expects: DI - positon (linear) DL - settings: 0 normal, 1 mirrored x, 2 mirrored y, 3 mirrored x&y DH - frame number Return: -
1527 1528draw_sprite: 1529 pusha 1530 1531 xor cx, cx 1532 mov byte cl, [si] 1533 1534 inc si 1535 1536 xor ax, ax 1537 mov byte al, [si] 1538 inc si 1539 1540 shl ax, 0x2 1541 mov bp, ax 1542 add bp, PaletteSets 1543 1544 movzx ax, dh 1545 imul ax, cx 1546 shl ax, 0x1 1547 add si, ax 1548 1549 test dl, 0x1 1550 jz .no_x_mirror 1551 add di, 0x7 1552 .no_x_mirror: 1553 1554 test dl, 0x2 1555 jz .no_y_mirror 1556 add si, cx 1557 add si, cx 1558 sub si, 0x2 1559 .no_y_mirror: 1560 1561 .plot_line: 1562 push cx 1563 mov ax, [si] 1564 xor bx, bx 1565 mov cl, 0x08 1566 push si 1567 .draw_pixel: 1568 push cx 1569 1570 rol ax, 2 1571 mov bx, ax 1572 and bx, 0x3 1573 1574 mov si, bp 1575 add si, bx 1576 mov byte bl, [si] 1577 1578 cmp bl, 0x0 1579 jz .skip_pixel 1580 mov byte [es:di], bl 1581 .skip_pixel: 1582 1583 inc di 1584 1585 mov bl, dl 1586 and bl, 0x1 1587 jz .no_x_mirror2 1588 dec di 1589 dec di 1590 .no_x_mirror2: 1591 1592 pop cx 1593 loop .draw_pixel 1594 1595 pop si 1596 add si, 0x2 1597 1598 mov bl, dl 1599 and bl, 0x2 1600 jz .no_y_mirror2 1601 sub si, 0x4 1602 .no_y_mirror2: 1603 1604 add di, 312 1605 1606 mov bl, dl 1607 and bl, 0x1 1608 jz .no_x_mirror3 1609 add di, 0x10 1610 .no_x_mirror3: 1611 1612 pop cx 1613 loop .plot_line 1614 popa 1615ret 1616
; lines ; Palette ; each set is 4x 1 byte ; Get frame ; muliply by lines ; *2 bytes per line ; Mobe to the frame memory position ; Check x mirror ; Move point to the last right pixel ; Check ; Move to the last position ; Back one word ; Save lines couter ; Get sprite line ; 8 pixels in line ; Palette Set ; Palette color ; Get color from the palette ; transparency ; Write pixel color ; Or skip this pixel - alpha color ; Move destination to next pixel (+1) ; Jump if not ; Remove previous shift (now it's 0) ; Move destination 1px left (-1) ; Move to the next sprite line data ; Move to next line in destination ; If mirrored adjust next line position ; Restore line counter

THE END

Thanks for reading the source code! Visit http://smol.p1x.in for more.
1620 1621 1622Logo: 1623db "P1X" 1624
; Use HEX viewer to see P1X at the end of binary