Last active
February 20, 2025 07:54
-
-
Save finnmglas/8157f644b56c3343f6c99aa4d06ac662 to your computer and use it in GitHub Desktop.
A cool json list dataset of 512 byte / boot sector games / binaries (games in strings base64 encoded). Keys contained: link, binary base64 encoded, assembly code; Total: 33 games à 512 Bytes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"tetros": { | |
"link": "https://github.com/daniel-e/tetros", | |
"data": "McCO2DHAzRC0AbkHJs0QtgO5EgBR/sayDbkOALt4AOi2AID+FXQJQrkMADHb6KgAWeLhxgYAf2S0As0aoAJ/MdCzH/fjQKICfzHSuwcA9/PA4gOSuhIE6OUAdf7o0gAxyYoOAH9RYDHJurgLtIbNFWFQtAHNFonBWHRF6K8AgP1LdBGA/Uh0HoD9TXQQxgYAfwrrI0ropwB0HULrGkLongB0FErrEYjDQECoB3UCLAjojAB0AojY6HcAUDDkzRZYWeKi6GcA/sbodAB0iv7O6F8A6BYA6Vv/tALNELggCc0Qw7QCzRC0CM0Qw2C2Ff7OdDkx27kMALIO6Ob/wOwEdAJDQuL0gPsMdeRgsg65DABR/s7ozP/+xojjsQHouf9CWeLtYf7OdeLowP9hwzHb6wmIw8DrA0PA4wRDid/rA78AAGAx24jDi4eGfTHbuQQAUbEE9sSAdB1QCf90DmCJ+zDAuQEA6HD/YesJ6HT/wOwEdAFDWNHgQuLZgOoE/sZZ4s4I22HDREQA8EREAPBgIgDiQGQAjmBEAC4gYgDoAGYAZgBmAGYAxkAmAMZAJgBOQEwA5ICMAGxAjABsQIyAAAEAFwACAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"description": "Tetris Clone. Full color, no score. This was one of the older boot sector games out there. ", | |
"asm": "; Tetris\n\torg 7c00h\n\n; ==============================================================================\n; DEBUGGING MACROS\n; ==============================================================================\n\n%ifdef DEBUG\n%macro print_reg 1\n\tmov dx, %1\n\tmov cx, 16\nprint_reg_loop:\n\tpush cx\n\tmov al, '0'\n\ttest dh, 10000000b\n\tjz print_reg_do\n\tmov al, '1'\nprint_reg_do:\n\tmov bx, 0x0006 ; page = 0 (BH), color = gray on black (BL)\n\tmov ah, 0x09 ; write character stored in AL\n\tmov cx, 1\n\tint 0x10\n\tmov ah, 3 ; move cursor one column forward\n\tint 0x10\n\tinc dx\n\tmov ah, 2 ; set cursor\n\tint 0x10\n\tpop cx\n\tshl dx, 1\n\tloop print_reg_loop\n\tjmp $\n%endmacro\n%endif\n\n; ==============================================================================\n; MACROS\n; ==============================================================================\n\n; Sleeps for the given number of microseconds.\n%macro sleep 1\n\tpusha\n\txor cx, cx\n\tmov dx, %1\n\tmov ah, 0x86\n\tint 0x15\n\tpopa\n%endmacro\n\n; Choose a brick at random.\n%macro select_brick 0\n\tmov ah, 2 ; get current time\n\tint 0x1a\n\tmov al, byte [seed_value]\n\txor ax, dx\n\tmov bl, 31\n\tmul bx\n\tinc ax\n\tmov byte [seed_value], al\n\txor dx, dx\n\tmov bx, 7\n\tdiv bx\n\tshl dl, 3\n\txchg ax, dx ; mov al, dl\n%endmacro\n\n; Sets video mode and hides cursor.\n%macro clear_screen 0\n\txor ax, ax ; clear screen (40x25)\n\tint 0x10\n\tmov ah, 1 ; hide cursor\n\tmov cx, 0x2607\n\tint 0x10\n%endmacro\n\nfield_left_col: equ 13\nfield_width: equ 14\ninner_width: equ 12\ninner_first_col: equ 14\nstart_row_col: equ 0x0412\n\n%macro init_screen 0\n\tclear_screen\n\tmov dh, 3 ; row\n\tmov cx, 18 ; number of rows\nia: push cx\n\tinc dh ; increment row\n\tmov dl, field_left_col ; set column\n\tmov cx, field_width ; width of box\n\tmov bx, 0x78 ; color\n\tcall set_and_write\n\tcmp dh, 21 ; don't remove last line\n\tje ib ; if last line jump\n\tinc dx ; increase column\n\tmov cx, inner_width ; width of box\n\txor bx, bx ; color\n\tcall set_and_write\nib: pop cx\n\tloop ia\n%endmacro\n\n; ==============================================================================\n\ndelay: equ 0x7f00\nseed_value: equ 0x7f02\n\nsection .text\n\nstart_tetris:\n\txor ax, ax\n\tmov ds, ax\n\tinit_screen\nnew_brick:\n\tmov byte [delay], 100 ; 3 * 100 = 300ms\n\tselect_brick ; returns the selected brick in AL\n\tmov dx, start_row_col ; start at row 4 and col 38\nlp:\n\tcall check_collision\n\tjne $ ; collision -> game over\n\tcall print_brick\n\nwait_or_keyboard:\n\txor cx, cx\n\tmov cl, byte [delay]\nwait_a:\n\tpush cx\n\tsleep 3000 ; wait 3ms\n\n\tpush ax\n\tmov ah, 1 ; check for keystroke; AX modified\n\tint 0x16 ; http://www.ctyme.com/intr/rb-1755.htm\n\tmov cx, ax\n\tpop ax\n\tjz no_key ; no keystroke\n\tcall clear_brick\n ; 4b left, 48 up, 4d right, 50 down\n\tcmp ch, 0x4b ; left arrow\n\tje left_arrow ; http://stackoverflow.com/questions/16939449/how-to-detect-arrow-keys-in-assembly\n\tcmp ch, 0x48 ; up arrow\n\tje up_arrow\n\tcmp ch, 0x4d\n\tje right_arrow\n\n\tmov byte [delay], 10 ; every other key is fast down\n\tjmp clear_keys\nleft_arrow:\n\tdec dx\n\tcall check_collision\n\tje clear_keys ; no collision\n\tinc dx\n\tjmp clear_keys\nright_arrow:\n\tinc dx\n\tcall check_collision\n\tje clear_keys ; no collision\n\tdec dx\n\tjmp clear_keys\nup_arrow:\n\tmov bl, al\n\tinc ax\n\tinc ax\n\ttest al, 00000111b ; check for overflow\n\tjnz nf ; no overflow\n\tsub al, 8\nnf: call check_collision\n\tje clear_keys ; no collision\n\tmov al, bl\nclear_keys:\n\tcall print_brick\n\tpush ax\n\txor ah, ah ; remove key from buffer\n\tint 0x16\n\tpop ax\nno_key:\n\tpop cx\n\tloop wait_a\n\n\tcall clear_brick\n\tinc dh ; increase row\n\tcall check_collision\n\tje lp ; no collision\n\tdec dh\n\tcall print_brick\n\tcall check_filled\n\tjmp new_brick\n\n; ------------------------------------------------------------------------------\n\nset_and_write:\n\tmov ah, 2 ; set cursor\n\tint 0x10\n\tmov ax, 0x0920 ; write boxes\n\tint 0x10\n\tret\n\nset_and_read:\n\tmov ah, 2 ; set cursor position\n\tint 0x10\n\tmov ah, 8 ; read character and attribute, BH = 0\n\tint 0x10 ; result in AX\n\tret\n\n; ------------------------------------------------------------------------------\n\n; DH = current row\n%macro replace_current_row 0\n\tpusha ; replace current row with row above\n \tmov dl, inner_first_col\n \tmov cx, inner_width\ncf_aa:\n\tpush cx\n\tdec dh ; decrement row\n\tcall set_and_read\n\tinc dh ; increment row\n\tmov bl, ah ; color from AH to BL\n\tmov cl, 1\n\tcall set_and_write\n\tinc dx ; next column\n\tpop cx\n\tloop cf_aa\n\tpopa\n%endmacro\n\ncheck_filled:\n\tpusha\n\tmov dh, 21 ; start at row 21\nnext_row:\n\tdec dh ; decrement row\n\tjz cf_done ; at row 0 we are done\n\txor bx, bx\n\tmov cx, inner_width\n\tmov dl, inner_first_col ; start at first inner column\ncf_loop:\n\tcall set_and_read\n\tshr ah, 4 ; rotate to get background color in AH\n\tjz cf_is_zero ; jmp if background color is 0\n\tinc bx ; increment counter\n\tinc dx ; go to next column\ncf_is_zero:\n\tloop cf_loop\n\tcmp bl, inner_width ; if counter is 12 full we found a full row\n\tjne next_row\nreplace_next_row: ; replace current row with rows above\n\treplace_current_row\n\tdec dh ; replace row above ... and so on\n\tjnz replace_next_row\n\tcall check_filled ; check for other full rows\ncf_done:\n\tpopa\n\tret\n\nclear_brick:\n\txor bx, bx\n\tjmp print_brick_no_color\nprint_brick: ; al = 0AAAARR0\n\tmov bl, al ; select the right color\n\tshr bl, 3\n\tinc bx\n\tshl bl, 4\nprint_brick_no_color:\n\tinc bx ; set least significant bit\n\tmov di, bx\n\tjmp check_collision_main\n\t; BL = color of brick\n\t; DX = position (DH = row), AL = brick offset\n\t; return: flag\ncheck_collision:\n\tmov di, 0\ncheck_collision_main: ; DI = 1 -> check, 0 -> print\n\tpusha\n\txor bx, bx ; load the brick into AX\n\tmov bl, al\n\tmov ax, word [bricks + bx]\n\n\txor bx, bx ; BH = page number, BL = collision counter\n\tmov cx, 4\ncc:\n\tpush cx\n\tmov cl, 4\nzz:\n\ttest ah, 10000000b\n\tjz is_zero\n\n\tpush ax\n\tor di, di\n\tjz ee ; we just want to check for collisions\n\tpusha ; print space with color stored in DI\n\tmov bx, di ; at position in DX\n\txor al, al\n\tmov cx, 1\n\tcall set_and_write\n\tpopa\n\tjmp is_zero_a\nee:\n\tcall set_and_read\n\tshr ah, 4 ; rotate to get background color in AH\n\tjz is_zero_a ; jmp if background color is 0\n\tinc bx\nis_zero_a:\n\tpop ax\n\nis_zero:\n\tshl ax, 1 ; move to next bit in brick mask\n\tinc dx ; move to next column\n\tloop zz\n\tsub dl, 4 ; reset column\n\tinc dh ; move to next row\n\tpop cx\n\tloop cc\n\tor bl, bl ; bl != 0 -> collision\n\tpopa\n\tret\n\n; ==============================================================================\n\nbricks:\n\t; in AL in AH\n\t; 3rd + 4th 1st + 2nd row\n\tdb 01000100b, 01000100b, 00000000b, 11110000b\n\tdb 01000100b, 01000100b, 00000000b, 11110000b\n\tdb 01100000b, 00100010b, 00000000b, 11100010b\n\tdb 01000000b, 01100100b, 00000000b, 10001110b\n\tdb 01100000b, 01000100b, 00000000b, 00101110b\n\tdb 00100000b, 01100010b, 00000000b, 11101000b\n\tdb 00000000b, 01100110b, 00000000b, 01100110b\n\tdb 00000000b, 01100110b, 00000000b, 01100110b\n\tdb 00000000b, 11000110b, 01000000b, 00100110b\n\tdb 00000000b, 11000110b, 01000000b, 00100110b\n\tdb 00000000b, 01001110b, 01000000b, 01001100b\n\tdb 00000000b, 11100100b, 10000000b, 10001100b\n\tdb 00000000b, 01101100b, 01000000b, 10001100b\n\tdb 00000000b, 01101100b, 01000000b, 10001100b\n\n%ifndef DEBUG\n; It seems that I need a dummy partition table entry for my notebook.\ntimes 446-($-$$) db 0\n\tdb 0x80 ; bootable\n db 0x00, 0x01, 0x00 ; start CHS address\n db 0x17 ; partition type\n db 0x00, 0x02, 0x00 ; end CHS address\n db 0x00, 0x00, 0x00, 0x00 ; LBA\n db 0x02, 0x00, 0x00, 0x00 ; number of sectors\n\n; At the end we need the boot sector signature.\ntimes 510-($-$$) db 0\n\tdb 0x55\n\tdb 0xaa\n%endif\n", | |
"help": "" | |
}, | |
"tetranglix": { | |
"link": "https://github.com/shikhin/tetranglix", | |
"data": "6gV8AAAx247TvAB8jtuOw/y/BAW5tgExwPOqsAPNELUmsAP+xM0QuAC4jsAx/7nQB7gAD/OrvioFZrjb29vbZolE/YlEAYPGEIH+ugZ28DDSviQFv7h9+4sebASDwwI5HmwEdfqE0nU3/sJgDzEx0DHSAwZsBLkHAPfxidPQ44uf6H2/BAW+2wC5EAAwwNHjD0LGiAVH4vRhxwQGAOmlALQBzRZ0WTDkzRaLHID8S3UG/gz/13JGgPxNdQb+BP/XcjuA/Eh1ODHJ/sFgBh4HvgQFuQQAvxMFAc+yBKSDxwP+ynX44u++FAW/BAWxCPOlB2Hi1//Xcwe5AwDrzokc/kQB/9dzP/5MATDSYAYeB7qZfeiHADHJvioFshAw26yEwA9E2v7KdfaE23UL/WCJ94PuEPOkYfyDwRCB+ZABctoHYenx/mC/MAC+KgW5EACsqkeqR+L5g8dggf+gD3LtYWCKRAGxUPbhD7Y80eeDxxgBx9HnsRC+BAW0D4TJdBb+yayEwCYPRAWrq/bBA3XsgceQAOvmYem//ggFw2DoNQCxEITJdBD+yaz/0kf2wQN18YPHDOvsYcNg+LrCfejc/2HDPNt1DoH/ugZzBDoFdQSDxBL5ww+2RAHB4AQPthyNeAYBx74EBcMADyAO4AJgBmADQA4wBlNoTm9YZ1NvVao=", | |
"description": "Includes score, but not full color. A writeup of this game was found in issue 3 of PoC||GTFO as Tetranglix: This Tetris is a Boot Sector", | |
"asm": "; 16 bits, starting at 0x7C00.\nBITS 16\nORG 0x7C00\n\nBSS EQU 0x504 ; The byte at 0x500 is also used, so align on next dword bound.\nBSS_SIZE EQU 438\n\nCUR_TETRAMINO EQU BSS ; 16 bytes.\nROT_TETRAMINO EQU BSS + 16 ; 16 bytes.\nOFFSET EQU BSS + 32 ; 2 bytes.\nSTACK EQU BSS + 38 ; 4 bytes reserved in beginning, 400 bytes.\n\nLEFT_SCANCODE EQU 75\nRIGHT_SCANCODE EQU 77\n\nUP_SCANCODE EQU 72\nDOWN_SCANCODE EQU 80\n\nSCORE_DIGITS EQU 5\n\nCPU 686\n\n; Entry point.\n; cs:ip -> linear address (usually 0x7C00, but irrelevant because we are position independent).\nstart:\n ; Set up segments.\n xor ax, ax\n\n ; Stack.\n mov ss, ax\n mov sp, 0xB800 ;why not\n\n mov ds, ax\n mov es, ax\n\n ; Clear direction flag.\n cld\n\n ; Clear BSS\n mov di, BSS\n mov cx, di ;at least BSS_SIZE\n rep stosb\n\n ; Set to mode 0x03, or 80x25 text mode (ah is zero from above).\n mov al, 0x03\n int 0x10\n\n ; Hide the hardware cursor. \n mov ch, 0x26\n mov ax, 0x103 ; Some BIOS crash without the 03.\n int 0x10\n\n mov es, sp\n mov fs, sp\n\n ; White spaces on black background.\n xor di, di\n mov ax, 0x0F00\n mov cx, ax ; At least 80x25x2.\n rep stosw\n call pop_check\n\n; Detects if CUR_TETRAMINO at OFFSET is colliding with any thing.\n; si -> OFFSET.\n; Output:\n; Carry set if colliding.\ntetramino_collision_check:\n\n lea bx, [bp + check_collision - tetramino_collision_check]\n\n; Processes the current tetramino, calling bx per \"tetramino pixel\".\n; bx -> where to call to; al contains tetramino pixel, di the address into stack.\ntetramino_process:\n pusha\n\n; Gets the offset into stack (i.e., address) into di.\n; si -> points at OFFSET.\n; Output:\n; si -> points at CUR_TETRAMINO.\n; di -> address into stack.\n; Trashes ax.\n\n ; Calculate first index into screen.\n lodsw\n aad 0x10\n cmp byte [si-1], 0x10\n sbb ah, ah\n xchg bx, ax\n lea di, [si + (STACK - OFFSET) + 0xFE + bx]\n xchg bx, ax\n\n mov si, CUR_TETRAMINO\n\n mov cl, 0x10\n\n .loop:\n test cl, 0x13;0b1011\n jnz .load_loop\n\n ; Go to next line in stack.\n add di, 16 - 4\n\n .load_loop:\n lodsb\n\n ; Call wherever the caller wants us to go.\n call bx\n\n inc di\n loop .loop\n\n popa\n ret\n\ncheck_collision:\n cmp al, 0xDB\n jnz .clear_carry\n\n cmp di, STACK + 400\n jae .colliding\n\n cmp al, [di]\n\n .clear_carry:\n clc\n\n jne .next_iter\n\n ; Colliding!\n .colliding:\n\n stc\n mov cl, 1\n .next_iter:\n ret\n\n; Used by the stack joining part.\nmerge:\n or [di], al\n ret\n\n; All tetraminos in bitmap format.\ntetraminos:\n db 0xF0;0b11110000 ; I\n db 0xE2;0b11100010 ; J\n db 0x2E;0b00101110 ; L\n db 0x66;0b01100110 ; O\n db 0x36;0b00110110 ; S\n db 0xE4;0b11100100 ; T\n db 0x63;0b01100011 ; Z\n\npop_check:\n pop bp ; Save some bytes.\n\n .borders:\n mov si, STACK - 3\n mov ax, 0xDBDB\n\n .borders_init:\n mov [si], ax\n mov [si + 2], ax\n mov [si + 4], ax\n\n add si, 16\n cmp si, STACK + 400 - 3\n jbe .borders_init\n\n ; Cleared dl implies \"load new tetramino\".\n xor dl, dl\n\n .event_loop:\n mov si, OFFSET\n\n mov bx, [0x046C]\n inc bx\n inc bx ; Wait for 2 PIT ticks.\n\n .busy_loop:\n cmp [0x046C], bx\n jne .busy_loop\n\n ; If we don't need to load a new tetramino, yayy!\n test dl, dl\n jnz .input\n\n ; Load a tetramino to CUR_TETRAMINO, from the compressed bitmap format.\n\n .choose_tetramino:\n rdtsc\n\n ; Only 7 tetraminos, index as 1-7.\n and ax, 7\n je .choose_tetramino\n\n ; Get the address of the tetramino (in bitmap format).\n cwd\n xchg di, ax\n\n ; Load tetramino bitmap in dl.\n mov dl, [cs:bp + di + (tetraminos - tetramino_collision_check) - 1]\n shl dx, 4\n\n ; Convert from bitmap to array.\n mov di, CUR_TETRAMINO\n mov cl, 0x10\n\n .loop_bitmap:\n\n shl dx, 1\n\n ; If the bit we just shifted off was set, store 0xDB.\n sbb al, al\n and al, 0xDB\n mov [di], al\n inc di\n\n loop .loop_bitmap\n\n ; Loaded.\n mov dl, 6\n\n mov word [si], dx\n jmp .link_next_iter\n\n ; Check for input.\n .input:\n ; Check for keystroke.\n mov ah, 0x01\n int 0x16\n\n ; If no keystroke, increment vertical offset.\n jz .vertical_increment\n\n ; Clear the keyboard buffer.\n xor ah, ah\n int 0x16\n\n ; Go left.\n .left:\n cmp ah, LEFT_SCANCODE\n jne .right\n\n dec byte [si]\n jmp .call_bp\n\n ; Go right.\n .right:\n cmp ah, RIGHT_SCANCODE\n jne .rotate\n\n inc byte [si]\n\n .call_bp:\n xor ah, LEFT_SCANCODE ^ RIGHT_SCANCODE\n call bp\n jc .left\n\n ; Rotate it.\n .rotate:\n cmp ah, UP_SCANCODE\n jne .vertical_increment\n\n inc cx\n\n .rotate_loop:\n ; Rotates CUR_TETRAMINO 90 degrees clock-wise.\n ; Output:\n ; CUR_TETRAMINO -> rotated tetramino.\n pusha\n push es\n\n ; Reset ES.\n push ds \n pop es\n\n mov si, CUR_TETRAMINO\n mov di, ROT_TETRAMINO + 3\n push si\n mov cl, 4\n\n .loop:\n mov ch, 4\n\n .tetramino_line:\n movsb\n scasw\n inc di\n dec ch\n jnz .tetramino_line\n\n sub di, 4*4+1\n loop .loop\n\n pop di\n mov cl, 4*4/2 ; CH would be zero, from above.\n rep movsw\n\n pop es\n popa\n\n loop .rotate_loop\n\n call bp\n ; To restore, just rotate 3 more times.\n mov cl, 3\n jc .rotate_loop\n\n .vertical_increment:\n mov cl, 1\n call upd_score\n\n ; Check if we can go below one byte, successfully.\n inc byte [si + 1]\n call bp\n .link_next_iter:\n jnc .next_iter\n\n ; If we can't, we need a new tetramino.\n dec byte [si + 1]\n je $ ; Game Over\n cwd\n\n ; Joins the current tetramino to the stack, and any complete lines together.\n ; si -> OFFSET.\n push es\n\n push ds\n pop es\n\n lea bx, [bp + merge - tetramino_collision_check]\n call tetramino_process\n\n mov si, STACK + 15\n std\n\n .loop_lines:\n push si\n mov cl, 16\n\n .line:\n lodsb\n test al, al\n loopnz .line ; If it was a blank, exit loop to indicate failure.\n\n jz .next_line\n\n lea cx, [si - (STACK - 1)]\n lea di, [si + 16]\n rep movsb\n mov cl, 64\n call upd_score\n\n .next_line:\n pop si\n add si, 16\n cmp si, STACK + 15 + 400\n jb .loop_lines\n\n cld\n pop es\n\n jmp .borders\n\n .next_iter: \n ; Display the stack.\n push si\n\n ; Add 24 characters padding in the front.\n mov ah, 0x0F\n mov di, 48\n mov si, STACK\n\n .loop_stack_lines:\n ; Copy 32 characters.\n mov cl, 16\n\n .stack_line:\n lodsb\n\n ; Store one character as two -- to make stack \"squarish\" on 80x25 display.\n stosw\n stosw\n\n loop .stack_line\n\n ; Handle remaining 24 characters in row, and starting 24 in next row.\n add di, 96\n cmp di, (25 * 160) ; If we go beyond the last row, we're over.\n jb .loop_stack_lines\n\n pop si\n\n ; Displays CUR_TETRAMINO at current OFFSET.\n ; si -> OFFSET.\n\n ; Calculate first index into screen.\n mov bx, [si]\n mov al, 40\n mul bh\n mov cl, 12\n add cl, bl\n add ax, cx\n\n ; One character takes 2 bytes in video memory.\n shl ax, 2\n xchg di, ax\n\n ; Loops for 16 input characters.\n mov cl, 0x10\n mov si, CUR_TETRAMINO\n\n mov ah, 0x0F\n\n .loop_tetramino:\n test cl, 0x13;0b1011\n jnz .load_tetramino\n\n ; Since each tetramino input is 4x4, we must go to next line\n ; at every multiple of 4.\n ; Since we output 2 characters for one input char, cover offset of 8.\n add di, (80 - 8) * 2\n\n .load_tetramino:\n lodsb\n test al, al\n\n ; Output two characters for \"squarish\" output.\n cmovz ax, [es:di]\n stosw\n stosw\n\n loop .loop_tetramino\n\n jmp .event_loop\n\nupd_score:\n mov bx, SCORE_DIGITS * 2\n\n .chk_score:\n dec bx\n dec bx\n js $\n mov al, '0'\n xchg [fs:bx], al\n or al, 0x30\n cmp al, '9'\n je .chk_score\n inc ax\n mov [fs:bx], al\n\n loop upd_score\n ret\n\n; IT'S A SECRET TO EVERYBODY.\ndb \"ShNoXgSo\"\n\n; Padding.\ntimes 510 - ($ - $$) db 0\n\nBIOS_signature:\n dw 0xAA55\n\n; Pad to floppy disk.\ntimes (1440 * 1024) - ($ - $$) db 0\n", | |
"help": "" | |
}, | |
"snake": { | |
"link": "https://github.com/JulianSlzr/project512", | |
"data": "ZjHAjtiOwI7QvAB8vwB+Zqtmq7jQB6sEBKsEBKuw/+Zg5GEk/OZhuAC4jsC4AwDNEP7EuQAgzRAx/7nQB7ggDmDzq78KAr66fehiAb7ufb8+AuhZAb7Dfb9KDehQAbj+ArkmAL+oAvOruRAAq2C5KQAw5POrtAKrYYHHngDi7bkmAL+qDPOrYYnPvvV96B4BvQYA6NEAZjHAzRqJyGbB4BCJ0GaJw2YrBgB+ZoP4A3zlZokeAH7kYDyxD4RC/yR/PBF0EzwedB88H3QTPCB1xrA+g8cE6xWwXoHvoADrDbB2gcegAOsFsDyD7wQmgD1vD5TEdAYmgD0gdVqqT2AGHgeJ6UG+CH4B7on3R0f986T8B2FXiT4Ifos+Cn6wKqqA/AF0CYu+CH6wIKrrJEVFv1ICgwYEfgShBH6zCvbzhsQEMKpPT0+I4DDkCMB17ugWAF/pQf+/PAy+4n3oUADkYDyxdfrpmf5gZjHAMNvNGoD7BX/0idC5SwD34WYPt9JmuQEAAQBm9/GJ0GbB6hBmuYAMAAD38YDi/P7DgfogA3zOidcmgD0gdcawb6phw2CsCMB0BKpH6/dhw1NuYWtlNTEyAFdBU0QgLSBEaXJlY3Rpb24sIE4gLSBOZXcgR2FtZQBZb3UgbG9zdCA9KABTY29yZToAPCAqICoAAAAAVao=", | |
"description": "A basic snake/nibbles clone with score", | |
"help": "", | |
"asm": "; Snake512\r\n; Copyright (C) 2009 Julian Salazar (user Zenith)\r\n; Entry for the Second 512-byte OS Contest at osdev.org\r\n;\r\n; You're free to use, redistribute, modify, and edit this program\r\n; in any way as long you attribute the original to me.\r\n\r\norg 0x7C00\r\nuse16\r\n\r\nstart:\r\n\r\nsetup:\r\n\t; Setup the segment values\r\n\txor eax, eax\r\n\tmov ds, ax\r\n\tmov es, ax\r\n\tmov ss, ax\r\n\tmov sp, 0x7C00\r\n\t\r\ninit:\r\n\t; Set initial values\r\n\tmov di, clockticks ; Zero out clockticks and score\r\n\tstosd\r\n\tstosd\r\n\t; Initialize the snake pointer array\r\n\tmov ax, (160*25)/2 ; The first entry is the head of the snake\r\n\tstosw\r\n\tadd al, 4\r\n\tstosw\r\n\tadd al, 4\r\n\tstosw\r\n\t; Reset the keyboard\r\n\tmov al, 0xFF\r\n\tout 0x60, al\r\n\t; Turn off the PC speaker and get rid of those annoying beeps\r\n\tin al, 0x61\r\n\tand al, 0xFC\r\n\tout 0x61, al\r\n\t\r\n\t; Setup the video mode, clearing the screen\r\nscreen:\r\n\t; Set the value of ES\r\n\tmov ax, 0xB800\r\n\tmov es, ax\r\n\t; Set video-mode\r\n\tmov ax, 3\r\n\tint 0x10\r\n\t; Hide cursor\r\n\tinc ah\r\n\tmov cx, 0x2000 ; Not sure if cx is zeroed - could use mov ch, 0x20 instead\r\n\tint 0x10\r\n\t; Set screen color (yellow on black)\r\n\txor di, di\r\n\tmov cx, (160*25)/2\r\n\tmov ax, 0x0E20\r\n\tpusha ; pusha takes less bytes than just saving one register\r\n\trep stosw\r\n\t\r\n.messages:\r\n\tmov di, (160*3)+42\r\n\tmov si, msg_name ; Print message (using di as the position)\r\n\tcall print\r\n\tmov si, msg_score\r\n\tmov di, (160*3)+94\r\n\tcall print\r\n\tmov si, msg_controls\r\n\tmov di, (160*21)+42\r\n\tcall print\r\n\t\r\n.rect:\r\n\t; Draw rectangle around playable area\r\n\tmov ax, 0x02FE\r\n\tmov cx, 38\r\n\tmov di, (160*4)+40\r\n\trep stosw\r\n\tmov cx, 16\r\n.rect_loop:\r\n\tstosw\r\n\tpusha\r\n\tmov cx, 41\r\n\txor ah, ah\r\n\trep stosw\r\n\tmov ah, 2\r\n\tstosw\r\n\tpopa\r\n\tadd di, 158\r\n\tloop .rect_loop\r\n\tmov cx, 38\r\n\tmov di, (160*20)+42\r\n\trep stosw\r\n\r\n; New Game!\r\ngame:\r\n\r\n.setup:\r\n\tpopa ; Restore values\r\n\t\r\n\t; Draw initial snake at the center of the map (the snake buffer is pre-initialized)\r\n\tmov di, cx\r\n\tmov si, init_snake\r\n\tcall print\r\n\tmov bp, 6 ; This is the length of the snake * 2\r\n\tcall place_food\r\n\t\r\n.delay:\r\n\txor eax, eax\r\n\tint 0x1A\r\n\tmov ax, cx\r\n\tshl eax, 16\r\n\tmov ax, dx\r\n\t\r\n\tmov ebx, eax\r\n\tsub eax, [clockticks]\r\n\t\r\n\tcmp eax, 3 ; Delay of 3 clock ticks (18.2 ticks per second)\r\n\tjl .delay\r\n\r\n\tmov [clockticks], ebx\r\n\t\r\n\t; get the last scan code (will return previous if no new key)\r\n\tin al, 0x60\r\n\t\r\n\t; scan codes\r\n\t; up = 83, left = 79, down = 84, right = 89 (not working)\r\n\t; w = 17, a = 30, s = 31, d = 32\r\n.direction:\r\n\tcmp al, 177 ; 'n' - new game, only when 'n' is released\r\n\tje start\r\n\tand al, 0x7F ; Recognize both press and release codes\r\n\tcmp al, 17 ; 'w' - up\r\n\tje .up\r\n\tcmp al, 30 ; 'a' - left\r\n\tje .left\r\n\tcmp al, 31 ; 's' - down\r\n\tje .down\r\n\tcmp al, 32 ; 'd' - right\r\n\tjne .delay\r\n\r\n.right:\r\n\tmov al, '>'\r\n\tadd di, 4\r\n\tjmp .move\r\n\t\r\n.up:\r\n\tmov al, '^'\r\n\tsub di, 160\r\n\tjmp .move\r\n\r\n.down:\r\n\tmov al, 'v'\r\n\tadd di, 160\r\n\tjmp .move\r\n\t\r\n.left:\r\n\tmov al, '<'\r\n\tsub di, 4\r\n\t\r\n.move:\t\r\n\t; Check if eating\r\n\tcmp byte [es:di], 'o'\r\n\tsete ah\r\n\tje .nofail\r\n\tcmp byte [es:di], ' ' ; If it hits the wall or itself\r\n\tjne .fail\r\n\t\r\n.nofail:\r\n\t; Move the head\r\n\tstosb\r\n\tdec di\r\n\t\r\n\tpusha ; Save di\r\n\t\r\n\t; Copy snake buffer backward once (move the pointers over)\r\n\tpush es ; Save current value of es\r\n\tpush ds ; Set es to 0\r\n\tpop es\r\n\tmov cx, bp\r\n\tinc cx\r\n\tmov si, snake\r\n\tadd si, bp ; si points to last array entry\r\n\tmov di, si\r\n\tinc di\r\n\tinc di ; di points to next cell\r\n\tstd ; set direction flag (the 'good' kind of std)\r\n\trep movsb\r\n\tcld\r\n\tpop es\r\n\t\r\n\tpopa ; Restore/save di again\r\n\tpush di\r\n\tmov [snake], di ; Save pointer to the new head of the snake\r\n\tmov di, [snake+2]\r\n\tmov al, '*'\r\n\tstosb ; Replace old head with body\r\n\t\r\n\t; Did the snake eat the food?\r\n\tcmp ah, 1\r\n\tje .food\r\n\t\r\n\t; Food wasn't eaten, clear the end of the snake\t\r\n\tmov di, [snake+bp]\r\n\tmov al, ' '\r\n\tstosb\r\n\t\r\n\tjmp .done\r\n\t\r\n.food:\r\n\tinc bp\r\n\tinc bp ; Increase snake length in bp\r\n\t\r\n\tmov di, (160*3)+114\r\n\tadd word [score], 4\r\n\tmov ax, [score]\r\n\tmov bl, 10\r\n.printscore_loop:\r\n\tdiv bl\r\n\txchg al, ah\r\n\tadd al, '0'\r\n\tstosb\r\n\tdec di ; Apparently, this is shorter than sub di, 3\r\n\tdec di\r\n\tdec di\r\n\tmov al, ah\r\n\txor ah, ah\r\n\tor al, al\r\n\tjnz .printscore_loop\r\n\tcall place_food\r\n.done:\r\n\tpop di ; Restore di\r\n\tjmp .delay\r\n\r\n.fail:\r\n\tmov di, (160*19)+92\r\n\tmov si, msg_fail\r\n\tcall print\r\n.fail_wait: ; Wait for N to be released\r\n\tin al, 0x60\r\n\tcmp al, 177\r\n\tjne .fail_wait\r\n\tjmp start\r\n\r\nplace_food:\r\n\tpusha\r\n\t; Place the next piece of food\r\n\t; Uses a ParkMiller random number generator with n=65537 and g=75\r\n\t; The seed will be the lower word of the RTC (dx)\r\n.seed:\r\n\txor eax, eax\r\n\txor bl, bl\r\n\tint 0x1A\r\n.random:\r\n\tcmp bl, 5 ; If it's looped 5 times, current seed is probably hopeless, get new one\r\n\tjg .seed\r\n\t\r\n\t; Algorithm: Xn+1 = (Xn * 75) mod 65537 (these values were used in the ZX Spectrum)\r\n\tmov ax, dx\r\n\tmov cx, 75\r\n\tmul cx ; dx:ax = current_num * g\r\n\tmovzx edx, dx\r\n\tmov ecx, 65537\r\n\tdiv ecx ; edx = edx:eax mod ecx = (seed * g) mod n = next number in sequence\r\n\tmov ax, dx\r\n\tshr edx, 16\r\n\tmov ecx, (160*20)\r\n\tdiv cx ; dx = dx:ax mod cx = offset to place next food at (puts number within range)\r\n\tand dl, 0xFC ; make value divisible by 4\r\n\t\r\n\t; Test if value is usable\r\n\tinc bl\r\n\tcmp dx, (160*5) ; Food can't be in the upper row, or else the snake won't reach it ;)\r\n\tjl .random\r\n\tmov di, dx\r\n\tcmp byte [es:di], 0x20 ; Check if the area is empty space\r\n\tjne .random\r\n\t\r\n\tmov al, 'o'\r\n\tstosb ; Draw the food\r\n\tpopa\r\n\tret\r\n\t\r\n; color byte is already set\r\n; di is the offset in video memory to write to\r\nprint:\r\n\tpusha\r\n.loop:\r\n\tlodsb\r\n\tor al, al\r\n\tjz .done\r\n\tstosb\r\n\tinc di\r\n\tjmp .loop\r\n.done:\r\n\tpopa\r\n\tret\r\n\r\nmsg_name: db 'Snake512',0\r\nmsg_controls: db 'WASD - Direction, N - New Game',0\r\nmsg_fail: db 'You lost =(',0\r\nmsg_score: db 'Score:',0\r\ninit_snake: db '< * *',0\r\n\r\ntimes 510-($-$$) db 0\r\ndw 0xAA55 ; Bootloader signature\r\n\r\nsection .bss\r\nclockticks: resd 1\r\nscore: resd 1 ; Only the word is used, aligns snake to 4-bytes\r\nsnake: ; This is the base of an array of pointers\r\n" | |
}, | |
"rickroll": { | |
"link": "https://github.com/JulianSlzr/project512", | |
"data": "McCO2I7AjtC8AHy/AH5mq7gAuI7AuAMAzRD+xLkAIM0QsLbmQ8cGBH4Nfb4mfWYx/+t8Zg+2+CQPgefwANDgwe8DZlcPtuiLnhZ9iNjmQoj45kLkYQwD5mEx/7nQB7ggD/OrieizCvbjiceJ6MHgC4DEILAguRkAYLkKAPOrYYHHoADi82C+9ny/ugfoWQCLNgR+v1oJrDDkAcdWvgl96EYAXuhCAL4Jfeg8AGFmX2YxwM0aichmweAQidBmicNmKwYAfmY5+HzmZokeAH6sCMAPhV7/iTYEfqwIwHQF6AcA6+u+NX3r5qwIwHQIqiaADQ9H6/PDWW91IEdvdCBSaWNrcm9sbCdkACAOIABEKEludHJvKQDjC5cKbwnpCPAHEgetBvEFg4RghIUXFhUUg4RgNTeFADJXZSdyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQAxMjMzNGJh8AA+Li4uKFZlcnNlKS4uLgAxMTKTMGc39DExMjMxM2QyMfAxMTIzMWA0NDQ19OM0NTM0NDQ1ZPAxMjNhNDWUADROZXZlciBnb25uYSBnaXZlIHlvdSB1cAAQERMRRUWUAD4uLi4oQ2hvcnVzKS4uLgAQERMRRESTEBETEWM0YmAwZMMQERMRZTWUEBETEWcykxARExFjNMIxMGTzAAAAAAAAAAAAVao=", | |
"description": "Snake music?", | |
"asm": "; Music Demo\r\n; Copyright (C) 2009 Julian Salazar (user Zenith)\r\n; Entry for the Third 512-byte OS Contest at osdev.org\r\n;\r\n; You're free to use, redistribute, modify, and edit this program\r\n; in any way as long you attribute the original to me.\r\n\r\norg 0x7C00\r\nuse16\r\n\r\n%define PITFREQ 1193180\r\n%define G4 PITFREQ/392\r\n%define A4 PITFREQ/440\r\n%define B4 PITFREQ/494\r\n%define C5 PITFREQ/523\r\n%define D5 PITFREQ/587\r\n%define E5 PITFREQ/659\r\n%define F5 PITFREQ/698\r\n%define G5 PITFREQ/784\r\n\r\nstart:\r\n\r\nsetup:\r\n\t; Setup the segment values\r\n\txor ax, ax\r\n\tmov ds, ax\r\n\tmov es, ax\r\n\tmov ss, ax\r\n\tmov sp, 0x7C00\r\n\t\r\ninit:\r\n\t; Set initial values\r\n\tmov di, clockticks ; Zero out clockticks\r\n\tstosd\r\n\t\r\n\t; Setup the video mode, clearing the screen\r\nscreen:\r\n\t; Set the value of ES\r\n\tmov ax, 0xB800\r\n\tmov es, ax\r\n\t; Set video-mode\r\n\tmov ax, 3\r\n\tint 0x10\r\n\t; Hide cursor\r\n\tinc ah\r\n\tmov cx, 0x2000 ; Not sure if cx is zeroed - could use mov ch, 0x20 instead\r\n\tint 0x10\r\n\t\r\n\t; Set up speaker to connect to timer 2\r\n\tmov al, 0xB6\r\n\tout 0x43, al\r\n\t\r\n\tmov word [msg], intromsg\r\n\t\r\nplay:\r\n\tmov si, intro\r\n\txor edi, edi ; Initialize clock ticks\r\n\tjmp delay\r\n\t\r\n; BX = Note frequency\r\n; BP = Duration of note\r\nplaynote:\r\n; AL = Note\r\n; Bottom 4 bits = note\r\n; If 4th bit set, do color flash\r\n; Top 4 bits = duration\r\n\tmovzx edi, al\r\n\tand al, 0x0F\t; AL = Note\r\n\tand di, 0xF0\t; DI = Duration\r\n\t\r\n\tshl al, 1\t\t; multiply AL by 2\r\n\tshr di, 3\t\t; Shift DI right by 3 (Number of ticks * 2)\r\n\r\n\tpush edi\r\n\tmovzx bp, al\r\n\tmov bx, [notes + bp]\r\n\r\n\tmov al, bl\r\n\tout 0x42, al\r\n\tmov al, bh\r\n\tout 0x42, al\r\n\t\r\n\tin al, 0x61\r\n\tor al, 3\r\n\tout 0x61, al\r\n\t\r\ncolorcolumn:\r\n\t; Clear the entire screen\r\n\txor di, di\r\n\tmov cx, (160*25)/2\r\n\tmov ax, 0x0F20\r\n\trep stosw\r\n\t\r\n\t; There are eight \"color columns\", 10 characters wide each\r\n\t; BP contains original note * 2\r\n\tmov ax, bp\r\n\tmov bl, 10\r\n\tmul bl\r\n\tmov di, ax\r\n\tmov ax, bp\r\n\tshl ax, 11\t; Move to AH, divide by 2\r\n\tadd ah, 0x20\r\n\tmov al, 0x20\r\n\tmov cx, 25\r\n.loop:\r\n\tpusha\r\n\tmov cx, 10\r\n\trep stosw\r\n\tpopa\r\n\tadd di, 160\r\n\tloop .loop\r\n\t\r\n\tpusha\r\n\t\r\n\tmov si, message\r\n\tmov di, (160*12)+29*2\r\n\tcall print ; Reprint the message\r\n\t\r\n\tmov word si, [msg]\r\n\tmov di, (160*15)-6\r\n\tlodsb\r\n\txor ah, ah\r\n\tadd di, ax\r\n\t\r\n\tpush si\r\n\tmov si, notechar\r\n\tcall print\r\n\tpop si\r\n\tcall print\r\n\tmov si, notechar\r\n\tcall print\r\n\t\r\n\tpopa\r\n\t\r\n\tpop edi\r\n\t\r\ndelay:\r\n\txor eax, eax\r\n\tint 0x1A\r\n\tmov ax, cx\r\n\tshl eax, 16\r\n\tmov ax, dx\r\n\t\r\n\tmov ebx, eax\r\n\tsub eax, [clockticks]\r\n\t\r\n\tcmp eax, edi ; Delay of EDI clock ticks (18.2 ticks per second)\r\n\tjl delay\r\n\t\r\n\tmov [clockticks], ebx\r\n\t\r\nloadnote:\r\n\tlodsb\r\n\tor al, al\r\n\tjnz playnote\r\n\t\r\n\tmov [msg], si\r\n\tlodsb\r\n\tor al, al\r\n\tjz .done\r\n\tcall print\r\n\tjmp loadnote\r\n\t\r\n.done:\r\n\tmov si, main\r\n\tjmp loadnote\r\n\r\n; color byte is already set\r\n; di is the offset in video memory to write to\r\nprint:\r\n\t; Print the message in the center of the screen\r\n.loop:\r\n\tlodsb\r\n\tor al, al\r\n\tjz .done\r\n\tstosb\r\n\tor byte [es:di], 0x0F\r\n\tinc di\r\n\tjmp .loop\r\n.done:\r\n\tret\r\n\t\r\n; G shall be the base note (0)\r\n\t\r\nmessage:\r\n\tdb \"You Got Rickroll'd\",0\r\nnotechar:\r\n\tdb \" \",14,\" \",0\r\nintromsg:\r\n\tdb 34*2,\"(Intro)\",0\r\n\r\nnotes:\r\n\tdw G4, A4, B4, C5, D5, E5, F5, G5\r\n\r\nintro:\r\n\tdb 0x83, 0x84, 0x60, 0x84, 0x85, 0x17, 0x16, 0x15, 0x14, 0x83, 0x84, 0x60, 0x35, 0x37, 0x85\r\nmain:\r\n\t; We're no strangers to love\r\n\tdb 0,25*2,\"We're no strangers to love\",0\r\n\tdb 0x31, 0x32, 0x33, 0x33, 0x34, 0x62, 0x61, 0xF0\r\n\tdb 0,31*2,\"...(Verse)...\",0\r\n\t; You know the rules, and so do I\r\n\tdb 0x31, 0x31, 0x32, 0x93, 0x30, 0x67, 0x37, 0xF4\r\n\t; A full commitment's what I'm thinking of\r\n\tdb 0x31, 0x31, 0x32, 0x33, 0x31, 0x33, 0x64, 0x32, 0x31, 0xF0\r\n\t; You wouldn't get this from any another guy\r\n\tdb 0x31, 0x31, 0x32, 0x33, 0x31, 0x60, 0x34, 0x34, 0x34, 0x35, 0xF4\r\n\t; I just want to tell you how I'm feeling\r\n\tdb 0xE3, 0x34, 0x35, 0x33, 0x34, 0x34, 0x34, 0x35, 0x64, 0xF0\r\n\t; Gotta make you understand\r\n\tdb 0x31, 0x32, 0x33, 0x61, 0x34, 0x35, 0x94\r\n\tdb 0,26*2,\"Never gonna give you up\",0\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x45, 0x45, 0x94\r\n\tdb 0,31*2,\"...(Chorus)...\",0\r\n\t;db 0,29*2,\"Never gonna let you down\",0\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x44, 0x44, 0x93\r\n\t;db 0,29*2,\"Never gonna run around and desert you\",0\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x63, 0x34, 0x62, 0x60, 0x30, 0x64, 0xC3\r\n\t;db 0,29*2,\"Never gonna make you cry\",0 ; (same as never gonna give you up)\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x65, 0x35, 0x94\r\n\t;db 0,29*2,\"Never gonna say goodbye\",0\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x67, 0x32, 0x93\r\n\t;db 0,29*2,\"Never gonna tell a lie and hurt you\",0\r\n\tdb 0x10, 0x11, 0x13, 0x11, 0x63, 0x34, 0xC2, 0x31, 0x30, 0x64, 0xF3\r\n\tdb 0,0\r\n\t\r\ntimes 510-($-$$) db 0\r\ndw 0xAA55 ; Bootloader signature\r\n\r\n;times 1474048 db 0\r\n\r\nsection .bss\r\nclockticks: resd 1\r\nmsg: resd 1", | |
"help": "" | |
}, | |
"petty bird": { | |
"link": "https://github.com/XanClic/512-petty-bird", | |
"data": "uBMAzRC4AKCO2I7A5nDkcC6iAH4x7UW5yAC/AQCBxz8Bg/0UdxOJyC6KFgB+g+J/KdB4CYP4I3MEMMDrArAvquLbg/04ciCD7TguoAB+iMLA4gcw0IjCwOoFMNCIwsDiAzDQLqIAfrQBzRYuobl8dQwu/wa7fC4DBrt86xBQMOTNFlguxwa7fPr/g8D6PcgAcyQuo7l8uUAB9+GDwCqJx4A9AHURxgUsvgEAMf+5APrzpPTpYP/06/1kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"description": "Good physics, but not as good graphics as FBird", | |
"asm": "'use16\n\norg 0x7c00\n\n\nDISTANCE equ 56\nPOLE_WIDTH equ 20\nGAP_HEIGHT equ 35\n\n\nmov ax,0x13\nint 0x10\n\n\nmov ax,0xa000\nmov ds,ax\nmov es,ax\n\nout 0x70,al\nin al,0x70\nmov [cs:random],al\n\nxor bp,bp\n\nmain_loop:\ninc bp\nmov cx,200\nmov di,1\ndraw_pole_loop:\nadd di,319\ncmp bp,POLE_WIDTH\nja no_pole\nmov ax,cx\nmov dl,[cs:random]\nand dx,0x7f\nsub ax,dx\njs is_pole\ncmp ax,GAP_HEIGHT\njae is_pole\nno_pole:\nxor al,al\njmp draw_bg\nis_pole:\nmov al,47\ndraw_bg:\nstosb\nloop draw_pole_loop\ncmp bp,DISTANCE\njb not_next\nsub bp,DISTANCE\nmov al,[cs:random]\nmov dl,al\nshl dl,7\nxor al,dl\nmov dl,al\nshr dl,5\nxor al,dl\nmov dl,al\nshl dl,3\nxor al,dl\nmov [cs:random],al\nnot_next:\n\nmov ah,1\nint 0x16\nmov ax,[cs:y_pos]\n\njnz flap\ninc word [cs:y_speed]\nadd ax,[cs:y_speed]\njmp draw_bird\n\nflap:\npush ax\nxor ah,ah\nint 0x16\npop ax\nmov word [cs:y_speed],0xfffa\nadd ax,0xfffa\n\ndraw_bird:\ncmp ax,200\njae crash\n\nmov [cs:y_pos],ax\nmov cx,320\nmul cx\n\nadd ax,42\nmov di,ax\ncmp byte [di],0\njne crash\nmov byte [di],44\n\nmov si,1\nxor di,di\nmov cx,320*200\nrep movsb\n\nhlt\njmp main_loop\n\ncrash:\nhlt\njmp crash\n\n\ny_pos dw 100\ny_speed dw 0\n\ntimes 510-($-$$) db 0\ndw 0xaa55\n\nrandom:\n'", | |
"help": "" | |
}, | |
"tronsolitare": { | |
"link": "https://github.com/XlogicX/tronsolitare", | |
"data": "McCO2I7QvACctLiOwLADzRC0AbUmzRC50Ae4IB8x//Orv54AmK+vsU7zq4H//g519L0AAb/RB7AvqlDouQCJ6MHoDvfYg8AEix5sBAHDOR5sBHL6tAHNFlh0BDDkzRZQgPxLdBWA/E10GYD8SHQQgPxQdceBx54A6wmD7wTrBIHvogDoCwC4IC+rV+hDAF/rqiaLBT0gL3QgPSAfdBuB/wAPcxU9N890EIbEJfAPPKB0CTzAsCl0CsPNGYAG03wDsAGiyXz+xDDAAcVzA70AAcODxQGB/QD2d1+J6L8CD1CGxOgBAFjUEOgAAIbEPAocaS+qsAeqww8xJA88B3U5Vw8xMdAx0gMGbAS50Af38dHigP4Pc+qJ1yaLBT0gL3TgPSAfdNsPMSQHBDGRDzGA5CCAxK+IyKtfw7oAAUKLHmwEg8MCOR5sBHX6idAx/7nQB/Orv8QHsA++Y32xDqSq4vzr2QIgWU9VIFdJTiEhISACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"description": "A little bit like 1-player tron, but with power-ups and traps with snake/nibbles like strategies. Complex and progressive scoring system and winnable.", | |
"asm": ";Tron Solitare\n; *This is a PoC boot sector ( <512 bytes) game\n; *Controls to move are just up/down/left/right\n; *Avoid touching yourself, blue border, and the\n; unlucky red 7\n\n[ORG 0x7c00] ;add to offsets\nLEFT EQU 75\nRIGHT EQU 77\nUP EQU 72\nDOWN EQU 80\n\n;Init the environment\n; init data segment\n; init stack segment allocate area of mem\n; init E/video segment and allocate area of mem\n; Set to 0x03/80x25 text mode\n; Hide the cursor\n xor ax, ax ;make it zero\n mov ds, ax ;DS=0\n\n mov ss, ax ;stack starts at 0\n mov sp, 0x9c00 ;200h past code start\n \n mov ah, 0xb8 ;text video memory\n mov es, ax ;ES=0xB800\n\n mov al, 0x03\n int 0x10\n\n ;Seems that this isn't needed, but leaving in commented out in case it needs to be added back\n ;mov al, 0x03 ;Some BIOS crash without this. \n ;mov ch, 0x26\n ;inc ah\n ;int 0x10 \n\n ;Use this instead:\n mov ah, 1\n mov ch, 0x26\n int 0x10 \n\n;Draw Border\n ;Fill in all blue\n mov cx, 0x07d0 ;whole screens worth\n mov ax, 0x1f20 ;empty blue background\n xor di, di\n rep stosw ;push it to video memory\n\n ;fill in all black except for remaining blue edges\n mov di, 158 ;Almost 2nd row 2nd column (need to add 4)\n cbw ;space char on black on black\n fillin:\n scasw\n scasw ;Adjust for next line and column\n mov cl, 78 ;inner 78 columns (exclude side borders)\n rep stosw ;push to video memory\n cmp di, 0x0efe ;Is it the last col of last line we want?\n jne fillin ;If not, loop to next line\n\n ;init the score\n mov bp, 0x0100 ;#CHEAT (You can set the initial score higher than this)\n\n ;Place the game peice in starting position\n mov di, 0x07d1 ;starting position\n mov al, 0x2f ;char to display\n stosb \n push ax ;initial key (nothing) \n\nmainloop:\n call random ;Maybe place an item on screen\n\n ;Wait Loop\n ;Get speed (based on game/score progress)\n mov ax, bp ;read data at coordinate\n shr ax, 14 ;now value 0-3\n neg ax\n add ax, 4 ;#CHEAT, default is 4; make amount higher for overall slower (but still progressive) game\n\n mov bx, [0x046C] ;Get timer state\n add bx, ax ;Wait 1-4 ticks (progressive difficulty)\n ;add bx, 8 ;unprogressively slow cheat #CHEAT (comment above line out and uncomment this line)\n delay:\n cmp [0x046C], bx\n jb delay\n\n ;Get keyboard state\n mov ah, 1\n int 0x16\n pop ax\n jz persisted ;if no keypress, jump to persisting move state\n\n ;Clear Keyboard buffer\n xor ah, ah\n int 0x16\n\n ;Otherwise, move in direction last chosen\n persisted:\n push ax\n ;Check for directional pushes and take action\n cmp ah, LEFT\n je left\n cmp ah, RIGHT\n je right\n cmp ah, UP\n je up\n cmp ah, DOWN\n jne mainloop\n\n down:\n add di, 158\n jmp movement_overhead\n\n left:\n sub di, 4 ;coordinate offset correction\n jmp movement_overhead\n up:\n sub di, 162\n right:\n\n movement_overhead:\n call collision_check\n mov ax, 0x2f20\n stosw \n push di\n call score\n pop di\n jmp mainloop\n\n collision_check:\n mov ax, [es:di] ;grab video buffer + current location\n\n ;Did we Lose?\n ;#CHEAT: comment out all 4 of these checks (8 instructions) to be invincible\n cmp ax, 0x2f20 ;did we land on green (self)?\n je gameover \n cmp ax, 0x1f20 ;did we land on blue (border)?\n je gameover\n cmp di, 0x0f00 ;did we land in score coordinate?\n jnb gameover\n cmp ax, 0xcf37 ;magic red 7\n je gameover\n\n ;Score Changes\n xchg ah, al\n and ax, 0x0ff0 ;mask background\n cmp al, 0xa0 ;add to score\n je bonus\n cmp al, 0xc0 ;subtract from score\n mov al, 0x29 ;make itemstuff: routine use sub opcode \n je penalty\n ret\n\ngameover: \n int 0x19 ; Reboot the system and restart the game.\n\n ;Legacy gameover, doesn't reboot, just ends with red screen\n ;xor di, di\n ;mov cx, 80*25\n ;mov ax, 0x4f20\n ;rep stosw \n ;jmp gameover\n\n bonus:\n add byte [selfmodify + 2], 3\n mov al, 0x01 ;make itemstuff: routine use add opcode\n\n penalty:\n\n do_item:\n mov byte [math], al\n\n itemstuff:\n inc ah ;1-8 instead of 0-7 \n xor al, al\n math:\n add bp, ax ;read data at coordinate and subtract from score\n\n jnb comm_ret ;sanity check for integer underflow\n\n underflow:\n mov bp, 0x0100\n\n comm_ret:\n ret\n\nscore:\n ;for each mov of character, add 'n' to score\n ;this source shows add ax, 1, however, each bonus\n ;item that is picked up increments this value by\n ;3 each time an item is picked up. Yes, this is \n ;self modifying code, which is why the lable\n ;'selfmodify:' is seen above, to be conveniently\n ;used as an address to pivot off of in an\n ;add byte [selfmodify + offset to '1'], 3 instruction\n selfmodify: add bp, 1 ;increment character in coordinate\n ;Why 0xf600 as score ceiling:\n ;if it was something like 0xffff, a score from 0xfffe would\n ;likley integer overflow to a low range (due to the progressive)\n ;scoring. 0xf600 gives a good amount of slack for this. However,\n ;it's still \"technically\" possible to overflow; for example,\n ;hitting a '7' bonus item after already getting more than 171\n ;bonus items (2048 points for bonus, 514 points per move) would\n ;make the score go from 0xf5ff to 0x0001.\n cmp bp, 0xf600 ;is the score high enough to 'win' ;#CHEAT\n ja win\nmov ax, bp\nmov di, 0f02h\npush ax\nxchg ah,al\ncall hex2asc\npop ax\nhex2asc:\naam 16\ncall hex2nib\nhex2nib:\nxchg ah,al\ncmp al,0ah\nsbb al,69h\ndas\nstosb\nmov al,7\nstosb\n ret\n\nrandom:\n ;Decide whether to place bonus/trap\n in al,(0x40)\n and al, 0x0f\n cmp al, 0x07\n jne undo\n\n push di ;store coord\n\n ;Getting random pixel\n redo:\n in al,(0x40) ;random\n xor ax, dx ;xor it up a little\n xor dx, dx ;clear dx\n add ax, [0x046C] ;moar randomness\n mov cx, 0x07d0 ;Amount of pixels on screen\n div cx ;dx now has random val\n shl dx, 1 ;adjust for 'even' pixel values\n ;Are we clobbering other data?\n cmp dh, 0x0f ;Is the pixel the score?\n jnb redo ;Get a different value\n\n mov di, dx\n mov ax, [es:di] ;read data at coordinate\n cmp ax, 0x2f20 ;Are we on the snake?\n je redo\n cmp ax, 0x1f20 ;Are we on the border?\n je redo\n\n ;Display random pixel\n\n ;Decide on item-type and value\n powerup:\n in al,(0x40) ;random\n and al, 0x07 ;get random 8 values\n add al, 0x31 ;baseline\n mov ah, al\n xchg cx, ax ;cx has rand value\n in al,(0x40) ;random\n mov ah, al\n ;background either 'A' or 'C' (light green or red)\n and ah, 0x20 ;keep bit 13\n add ah, 0xaf ;turn bit 14 and 12 on\n mov al, cl ;item-type + value\n\n stosw ;display it\n pop di ;restore coordinate\n\n undo:\n ret\n\nwin:\n mov dx, 100h\nwin1:\n inc dx ;incrememnt fill char/fg/bg (whichever is next)\n\n ;clear screen\n\n mov bx, [0x046C] ;Get timer state\n add bx, 2\n delay2:\n cmp [0x046C], bx\n jne delay2\n\n mov ax, dx\n xor di, di\n mov cx, 0x07d0 ;enough for full screen\n rep stosw ;commit to video memory\n\n mov di, 0x07c4 ;coord to start 'YOU WIN!' message\n mov al, 0x0f ;white text on black background\n mov si, winmessage ;get win message pointer\n mov cl, winmessage_e - winmessage\n winloop: movsb\n stosb ;commit char to video memory\n loop winloop ;is it the last character?\n jmp win1\n \n winmessage: \n db 0x02, 0x20\n dq 0x214e495720554f59 ;YOU WIN!\n db 0x21, 0x21, 0x20, 0x02\n winmessage_e: \n\n ;BIOS sig and padding\n times 510-($-$$) db 0\n dw 0xAA55\n\n ; Pad to floppy disk.\n ;times (1440 * 1024) - ($ - $$) db 0\n", | |
"help": "" | |
}, | |
"dasher": { | |
"link": "https://github.com/QiZD90/dasher512", | |
"data": "McCO2I7QvAB8ieWwA80QuAMBtSXNEOgpAOhPAOjlAGoCagDoBgGLDvF9Ow7zfXUG6DgA6EgBgD73fQB03Oj1AOvXxgb2fQDGBvV9ALsAAIoe8H1r2ySLh8h9o/F9i4fKfaPzfegEAOjLAMO1AID9EHQosQCA+RB0HVHoQwBR6DQAgD73fQB0BWgjAusCaiDoYQD+weve/sXr0/82833oFABoJA7oTAD/NvF96AcAaEAE6D8Aw1taU7sAALQCzRDDW1lTUbsAAIoe8H1r2yTQ5THAiOgBw4D5CHwBQ4qXqH18BEuA6Qi7gADT6yDaiBb3fVnDW1hTUYjjtAm5AQC3AM0QWcOLDvF9Ai72fQIO9X1R6Kz/gD73fQB0DMYG9n0Axgb1fQDrBIkO8X3DW1pZU7SGzRXDtADNFoD8SHQbgPxLdCKA/FB0KYD8TXXoxgb1fQHGBvZ9AOskxgb1fQDGBvZ9/+sYxgb1ff/GBvZ9AOsMxgb1fQDGBvZ9AesAw/4G8H2KHvB9gPsCdQdqAOgl/+sE6K3+wzH2tA6wJM0QagBoAEDohv/r8P//sAOgAaABoAGgAaABoAGgAaAFuAWABaAFsD2AAf//AQECC///kAGBBd/9wEGXcZQRlUOVEZXRlBGX8ZABnrGCAf//CAgOCAAAAAAAAAAAAAAAAAAAVao=", | |
"description": "A simple puzzle game with 2 levels. Slide from wall to wall, trying to solve a puzzle and reach the finish. The game looks like Atomix, but without atoms.", | |
"asm": "org 0x7c00\nbits 16\n\nsizeof_level equ 36 ; size of level in bytes\nlevels_count equ (levels_end-levels)/sizeof_level\n\ngreen equ 2\nred equ 4\nyellow equ 14\n\nstart:\n\t; Setup registers and segments\n\txor ax, ax\n\tmov ds, ax\n\tmov ss, ax\n\n\t; Setup stack\n\tmov sp, 0x7c00\n\tmov bp, sp\n\n\t; Set up videomode (ah is 0x00 due to xor)\n\tmov al, 0x03; 80x25 colored VGA text videomode\n\tint 0x10\n\n\t; Disable a cursor\n\tmov ax, 0x103\n\tmov ch, 0x25\n\tint 0x10\n\n\tcall load_level\n\nloop:\n\tcall draw\n\tcall update\n\n\tpush WORD 2\n\tpush WORD 0 ; pushing 2 words takes lesser bytes than pushing 1 dword\n\tcall sleep\n\n\tmov cx, [hero_pos]\n\tcmp cx, [finish_pos]\n\tjnz short .endif\n\t.on_finish:\n\t\tcall draw\n\t\tcall end_level\n\t.endif:\n\tcmp [buffer], BYTE 0\n\tjz short loop\n\n\tcall input\n\n\tjmp short loop\n\nload_level:\n\tmov [delta_y], BYTE 0\n\tmov [delta_x], BYTE 0\n\n\tmov bx, 0\n\tmov bl, [current_level]\n\timul bx, sizeof_level\n\n\tmov ax, [levels+bx+(sizeof_level-4)]\n\tmov [hero_pos], ax\n\n\tmov ax, [levels+bx+(sizeof_level-2)]\n\tmov [finish_pos], ax\n\n\tcall draw\n\tcall input\n\tret\n\ndraw:\n\tmov ch, 0 ; Row counter\n\t.row_loop:\n\t\tcmp ch, 16\n\t\tjz short .row_end\n\n\t\tmov cl, 0 ; Column counter\n\t\t.column_loop:\n\t\t\tcmp cl, 16\n\t\t\tjz short .column_end\n\n\t\t\tpush cx\n\t\t\tcall get_block_info\n\t\t\tpush cx\n\t\t\tcall move_cursor\n\n\t\t\tcmp [buffer], BYTE 0\n\t\t\tjz short .void\n\n\t\t\t.wall:\n\t\t\t\tpush WORD (green<<8 | '#')\n\t\t\t\tjmp short .endif\n\t\t\t.void:\n\t\t\t\tpush WORD ' '\n\t\t\t.endif:\n\t\t\tcall draw_char\n\n\t\t\tinc cl\n\t\t\tjmp short .column_loop\n\n\t\t.column_end:\n\t\tinc ch\n\t\tjmp short .row_loop\n\t.row_end:\n\n\t; Drawing finish\n\tpush WORD [finish_pos]\n\tcall move_cursor\n\tpush WORD (yellow<<8 | '$')\n\tcall draw_char\n\n\t; Drawing hero\n\tpush WORD [hero_pos]\n\tcall move_cursor\n\tpush WORD (red<<8 | '@')\n\tcall draw_char\n\n\tret\n\n; Moves text cursor to specified coordinates\n; input: 1 word - coordinates (y; x)\nmove_cursor:\n\tpop bx\n\tpop dx\n\tpush bx\n\n\tmov bx, 0\n\tmov ah, 0x02\n\tint 0x10\n\n\tret\n\n; Writes to the buffer 0, if tile on specified coordinates is void, and anything else otherwise\n; input: 1 word - coordinates (y; x)\nget_block_info:\n\tpop bx\n\tpop cx\n\tpush bx\n\n\tpush cx\n\n\t; Compute an offset from the start of the levels data\n\tmov bx, 0\n\tmov bl, [current_level]\n\timul bx, sizeof_level ; now BX stores an offset from the start of a level\n\tshl ch, 1 ; as every row occupies 2 bytes, we should multiply it\n\n\txor ax, ax ; clear AX\n\tmov al, ch ; \"byte to word\"\n\n\tadd bx,ax\n\n\t; Get one row, things are getting tricky here\n\tcmp cl, 8\n\tjnge short .endif ; if CL<8\n\t.if_greater_or_equals:\n\t\tinc bx\n\t.endif:\n\tmov dl, [levels+bx]\n\n\tjnge short .endif_2 ; if CL<8\n\t.if_greater_or_equals_2:\n\t\tdec bx\n\t\tsub cl, 8 ; CL = CL MOD 8\n\t.endif_2:\n\n\tmov bx, 1<<7\n\tshr bx, cl\n\n\tand dl, bl\n\tmov [buffer], dl\n\n\tpop cx\n\n\tret\n\n; Outputs a character at current cursor position\n; Separate subprograms for all characters aren't given due to an overhead\n; input: 1 word - character (high byte is color, and low byte is an ascii code)\ndraw_char:\n\tpop bx\n\tpop ax\n\tpush bx\n\n\tpush cx\n\n\tmov bl, ah\n\tmov ah, 0x09\n\tmov cx, 1\n\tmov bh, 0\n\tint 0x10\n\n\tpop cx\n\n\tret\n\nupdate:\n\tmov cx, [hero_pos]\n\tadd ch, [delta_y]\n\tadd cl, [delta_x]\n\n\tpush cx\n\tcall get_block_info\n\n\tcmp [buffer], BYTE 0\n\tjz short .is_void\n\t.is_block:\n\t\tmov [delta_y], BYTE 0\n\t\tmov [delta_x], BYTE 0\n\n\t\tjmp .endif\n\t.is_void:\n\t\tmov [hero_pos], cx\n\t.endif:\n\n\tret\n\n; Sleeps for specified time\n; input: 2 words - amount of microseconds,\nsleep:\n\tpop bx\n\tpop dx\n\tpop cx\n\tpush bx\n\n\tmov ah, 0x86\n\tint 0x15\n\n\tret\n\n\ninput:\n\t.loop:\n\t; Blocking keyboard input\n\tmov ah, 0\n\tint 0x16\n\n\tcmp ah, 0x48 ; Up arrow\n\tjz short .up_pressed\n\n\tcmp ah, 0x4b ; Left arrow\n\tjz short .left_pressed\n\n\tcmp ah, 0x50 ; Down arrow\n\tjz short .down_pressed\n\n\tcmp ah, 0x4d ; Right arrow\n\tjnz short .loop\n\n\t.right_pressed:\n\t\tmov [delta_x], BYTE 1\n\t\tmov [delta_y], BYTE 0\n\t\tjmp short .endif\n\n\t.up_pressed:\n\t\tmov [delta_x], BYTE 0\n\t\tmov [delta_y], BYTE -1\n\t\tjmp short .endif\n\n\t.left_pressed:\n\t\tmov [delta_x], BYTE -1\n\t\tmov [delta_y], BYTE 0\n\t\tjmp short .endif\n\n\t.down_pressed:\n\t\tmov [delta_x], BYTE 0\n\t\tmov [delta_y], BYTE 1\n\t\tjmp short .endif\n\n\t.endif:\n\tret\n\nend_level:\n\tinc BYTE [current_level]\n\tmov bl, [current_level]\n\n\tcmp bl, levels_count\n\tjnz short .endif\n\n\tpush WORD 0\n\tcall move_cursor\n\tjmp short win\n\n\t.endif:\n\tcall load_level\n\tret\n\nwin:\n\txor si, si\n\t.win_loop:\n\t\tmov ah, 0x0e\n\t\tmov al, '$'\n\t\tint 0x10\n\n\t\tpush WORD 0\n\t\tpush WORD 16384\n\t\tcall sleep\n\n\t\tjmp short .win_loop\n\n; Levels' layouts 8x8 (0 - void, 1 - wall), 2 bytes for start pos, 2 bytes for finish pos\nlevels:\ndb 11111111b, 11111111b\ndb 10110000b, 00000011b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000001b\ndb 10100000b, 00000101b\ndb 10111000b, 00000101b\ndb 10000000b, 00000101b\ndb 10100000b, 00000101b\ndb 10110000b, 00111101b\ndb 10000000b, 00000001b\ndb 11111111b, 11111111b\ndb 1, 1\ndb 2, 11\n\ndb 11111111b, 11111111b\ndb 10010000b, 00000001b\ndb 10000001b, 00000101b\ndb 11011111b, 11111101b\ndb 11000000b, 01000001b\ndb 10010111b, 01110001b\ndb 10010100b, 00010001b\ndb 10010101b, 01000011b\ndb 10010101b, 00010001b\ndb 10010101b, 11010001b\ndb 10010100b, 00010001b\ndb 10010111b, 11110001b\ndb 10010000b, 00000001b\ndb 10011110b, 10110001b\ndb 10000010b, 00000001b\ndb 11111111b, 11111111b\ndb 8, 8\ndb 14, 8\nlevels_end:\n\ncurrent_level db 0\n\nhero_pos dw 0\nfinish_pos dw 0\n\n; Currrent velocity on x- and y-axes\ndelta_x db 0\ndelta_y db 0\n\nbuffer db 0 ; Mainly used for saving an block info\n\n; PADDING AND SIGNATURE\ntimes 510-($-$$) db 0\ndb 0x55, 0xaa\n", | |
"help": "" | |
}, | |
"BootMeCrackMe": { | |
"link": "https://xlogicx.net/BootMe_-_CrackMe.html", | |
"data": "", | |
"description": "A simple puzzle game with 2 levels. Slide from wall to wall, trying to solve a puzzle and reach the finish. The game looks like Atomix, but without atoms.", | |
"help": "", | |
"asm": "" | |
}, | |
"battlesnakes": { | |
"link": "https://github.com/darkvoxels/battlesnakes", | |
"data": "uACQjtC4//+JxPomxwYkAJR9JowOJgD7uACgjsC0ALATzRC0AbUgsQDNELAH6EwBsAO/QX25CgDzqrAGv/V9uQoA86qwAYse+H2LDvZ96AMBsASLHvx9iw76fej2APSwC4se+H2LDvZ96OgAsA6LHvx9iw76fejbAKD1fYjDJA+A4/CLDvZ9ixb4fTwBdQFKPAJ1AUk8BHUBQjwIdQFBv0ABD6/6Ac8miiWA/AN1CWC3A7ML6IkAYYD8DnUFsAHougCA/At0BYD8BnUFsAToqwCJDvZ9iRb4fbAE6IIAiw76fYsW/H2A+xB1AUqA+yB1AUmA+0B1AUKA+4B1AUG/QAEPr/oBzyaKJYD8BnUJYLcGsw7oKgBhgPwLdQWwBOhbAID8DnQFgPwDdQWwAehMAIkO+n2JFvx9sAHoIwDpBP+/AAC5APomOB11AyaIPUc5+XXzw79AAQ+v+wHPJogFw2CD+QB8E4H5QAF/DYP6AHwIgfrIAH8CYcPoAAC5APq/AADzqjwHdAv09PT09PT09PTNGcNg5GCKHvV9PBF1BoDj8IDDATwedQaA4/CAwwI8H3UGgOPwgMMEPCB1BoDj8IDDCDxIdQaA4w+AwxA8S3UGgOMPgMMgPFB1BoDjD4DDQDxNdQaA4w+Aw4CIHvV9sCDmIGHPKEsAZAD1AGQAVao=", | |
"description": "This is basically Tron, it supports 2-players (at once).", | |
"asm": ";Lets NASM Know our origin address so that it can calculate the correct offsets\n[ORG 0x7c00]\n\n;SETUP STACK\nmov ax, 0x9000\nmov ss, ax\nmov ax, 0xFFFF\nmov sp, ax\n\n;Specify a new Interrupt Service Routine for Keyboard in the Interrupt Vector Table,\n;REF\n ;https://en.wikipedia.org/wiki/Interrupt_descriptor_table\n ;http://wiki.osdev.org/Interrupt_Vector_Table\n\n;IVT Offset | INT # | IRQ # | Description\n;-----------+-------+-------+------------------------------\n;0x0024 | 0x09 | 1 | Keyboard\n;NOTE: Found that ES defaulted to 0x00 so removed this..\n;mov ax, 0x0000\n;mov es, ax\ncli\t\t\t\t\t\t\t;Disable Interrupts\nmov [es:4 * 9], word get_keys\t\t\t\t;Update the IVT with the location of our Interrupt Service Routine (get_keys)\nmov [es:4 * 9 + 2], cs\t\t\t\t\t;Specify what segment our ISR is in\nsti\t\t\t\t\t\t\t;Enable Interrupts\n\n;POINT ES TO VIDEO MEMORY\nmov ax, 0xA000\nmov es, ax\n\n;SWITCH TO VIDEO MODE 0x13, GRAPHICS MODE, REF https://en.wikipedia.org/wiki/Mode_13h\n\t;resolution of 320\u00d7200 pixels\nmov ah, 0x00\nmov al, 0x13\nint 0x10\n\n;HIDE TEXT CURSOR\nmov ah, 1\nmov ch, 0x20\nmov cl, 0x00\nint 0x10\n\nstart:\nmov al, 0x07\ncall clearScreen\n\n;PLAYER SETUP\n;Player Start points p1(75,49) p2(245,50)\nmov al, 0x03\nmov di, 320*100+65\nmov cx, 10\nrep stosb\n\nmov al, 0x06\nmov di, 320*100+245\nmov cx, 10\nrep stosb\n\nloop:\n\t;RENDER PLAYER 1\n\tmov al, 0x01\n\tmov bx, [player1y]\n\tmov cx, [player1x]\n\tcall render_player\n\n\t;RENDER PLAYER 2\n\tmov al, 0x04\n\tmov bx, [player2y]\n\tmov cx, [player2x]\n\tcall render_player\t\n\n\thlt ;Add a Delay, this is based off Interrupts\n\n\t;RENDER PLAYER 1 CURRENT POS AS TAIL\n\tmov al, 0x0B\n\tmov bx, [player1y]\n\tmov cx, [player1x]\n\tcall render_player\n\n\t;RENDER PLAYER 2 CURRENT POS AS TAIL\n\tmov al, 0x0E\n\tmov bx, [player2y]\n\tmov cx, [player2x]\n\tcall render_player\n\n\t;PLAYER MOVEMENT\n\tmov al, [playerI]\n\tmov bl, al\n\tand al, 0x0F\n\tand bl, 0xF0\n\n\tmov cx, [player1x]\n\tmov dx, [player1y]\nmove_p1_w:\n\tcmp al, 0x01\n\tjne move_p1_a\n\tdec dx\nmove_p1_a:\n\tcmp al, 0x02\n\tjne move_p1_s\n\tdec cx\nmove_p1_s:\n\tcmp al, 0x04\n\tjne move_p1_d\n\tinc dx\nmove_p1_d:\n\tcmp al, 0x08\n\tjne p1_update\n\tinc cx\np1_update:\n\tmov di, 320\n\timul di, dx\n\tadd di, cx\n\tmov ah, [es:di]\n\tcmp ah, 0x03 ;CHECK FOR TAIL COMPLETE\n\tjne p1_kill\n\tpusha\n\tmov bh, 0x03\n\tmov bl, 0x0b\n\tcall fillscan\n\tpopa\n\np1_kill:\n\tcmp ah, 0x0E\n\tjne p1_tail_die\n\tmov al, 0x01\n\tcall clearScreen\n\np1_tail_die:\n\tcmp ah, 0x0B ;Check for tail suicide\n\tje p1_die\n\np1_wall_die:\n\tcmp ah, 0x06 ;Check for p2 Wall\n\tjne p1_npos\n\np1_die:\n\tmov al, 0x04\n\tcall clearScreen\n\np1_npos:\n\t;SET NEW POS\n\tmov [player1x], cx\n\tmov [player1y], dx\n\n\tmov al, 0x04\n\tcall bounds_check\n\n\tmov cx, [player2x]\n\tmov dx, [player2y]\nmove_p2_u:\n\tcmp bl, 0x10\n\tjne move_p2_l\n\tdec dx\nmove_p2_l:\n\tcmp bl, 0x20\n\tjne move_p2_d\n\tdec cx\nmove_p2_d:\n\tcmp bl, 0x40\n\tjne move_p2_r\n\tinc dx\nmove_p2_r:\n\tcmp bl, 0x80\n\tjne p2_update\n\tinc cx\np2_update:\n\tmov di, 320\n\timul di, dx\n\tadd di, cx\n\tmov ah, [es:di]\n\tcmp ah, 0x06\t;CHECK FOR TAIL COMPLETE\n\tjne p2_kill\n\tpusha\n\tmov bh, 0x06\n\tmov bl, 0x0E\n\tcall fillscan\n\tpopa\n\np2_kill:\n\tcmp ah, 0x0B\n\tjne p2_tail_die\n\tmov al, 0x04\n\tcall clearScreen\n\np2_tail_die:\n\tcmp ah, 0x0E ;Check for tail suicide\n\tje p2_die\n\np2_wall_die:\n\tcmp ah, 0x03 ;Check for p1 Wall\n\tjne p2_npos\n\np2_die:\n\tmov al, 0x01\n\tcall clearScreen\n\np2_npos:\n\tmov [player2x], cx\n\tmov [player2y], dx\n\n\tmov al, 0x01\n\tcall bounds_check\n\nnext:\n\tjmp loop\n\nfillscan:\n\tmov di, 0x00\n\tmov cx, 320 * 200\nscan:\n\tcmp [es:di], bl\n\tjne sloop\n\tmov [es:di], bh\nsloop:\n\tinc di\n\tcmp cx, di\n\tjnz scan\n\tret\n\nrender_player:\t\t\t\t\t\t;al=color, bx=y, cx=x\n\tmov di, 320\n\timul di, bx\n\tadd di, cx\n\tmov [es:di], al\n\tret\n\nbounds_check:\t\t\t\t\t\t;al=color to clear screen, dx=y, cx=x\n\tpusha\n\t;x lower bounds\n\tcmp cx, 0x00\n\tjl p_bounds_die\n\n\t;x upper bounds\n\tcmp cx, 320\n\tjg p_bounds_die\n\n\t;y lower bounds\n\tcmp dx,0x00\n\tjl p_bounds_die\n\n\t;y upper bounds\n\tcmp dx, 200\n\tjg p_bounds_die\n\n\t;Return if no bounds conditions met:\n\tpopa\n\tret\n\n\t;Game Over on player who hit bounds\np_bounds_die:\n\tcall clearScreen\n\nclearScreen:\n\t;AL = Hex Color\n\tmov cx, 320*200\t\t\t\t\t;number of times to inc.. row * columns for whole screen\n\tmov di, 0\t\t\t\t\t; Start...\n\trep stosb\n\tcmp al, 0x07\t\t\t\t\t;CHECK if its the default color\n\tje cs_exit\t\t\t\t\t;if it is exit, if not, Game Over reboot\ncs_gameover:\n\thlt\t\t\t\t\t\t;Long Delay, TODO, do more optimizations and build better timing\n\thlt\n\thlt\n\thlt\n\thlt\n\thlt\n\thlt\n\thlt\n\thlt\n\tint 0x19\t\t\t\t\t;Reboot system to restart the game... \ncs_exit:\n\tret\n\n;CUSTOM KEYBOARD INTTERUPT FOR READING MULTIPLE KEYS AT ONCE..\nget_keys:\n\tpusha\t\t\t\t\t\t;Push all registers to the stack\n\tin al, 0x60\t\t\t\t\t;read keyboard scan code\n\tmov bl, [playerI]\nP1_W:\n\tcmp al, 0x11\n\tjne P1_A\n\tand bl, 0xF0\n\tadd bl, 0x01\nP1_A:\n\tcmp al, 0x1E\n\tjne P1_S\n\tand bl, 0xF0\n\tadd bl, 0x02\nP1_S:\n\tcmp al, 0x1F\n\tjne P1_D\n\tand bl, 0XF0\n\tadd bl, 0x04\nP1_D:\n\tcmp al, 0x20\n\tjne P2_U\n\tand bl, 0xF0\n\tadd bl, 0x08\nP2_U:\n\tcmp al, 0x48\n\tjne P2_L\n\tand bl, 0x0F\n\tadd bl, 0x10\nP2_L:\n\tcmp al, 0x4B\n\tjne P2_D\n\tand bl, 0x0F\n\tadd bl, 0x20\nP2_D:\n\tcmp al, 0x50\n\tjne P2_R\n\tand bl, 0x0F\n\tadd bl, 0x40\nP2_R:\n\tcmp al, 0x4D\n\tjne get_keys_done\n\tand bl, 0x0F\n\tadd bl, 0x80\n\nget_keys_done:\n\tmov [playerI], bl\n\n\t;Send \"END OF INTERRUPT\" to the Programmable Interrupt Controller\n\tmov al, 0x20\n\tout 0x20, al\n\n\tpopa \t\t\t\t;Pop all registers back from the stack\n\tiret ; Return\n\n\n;Player Input's and Cord's\nplayerI db 0x28\nplayer1x dw 0x004b\nplayer1y dw 0x0064\nplayer2x dw 0x00f5\nplayer2y dw 0x0064\n\n;NASM Padding for BootSector Boot Signature\ntimes 510-($-$$) db 0\ndw 0xAA55\n", | |
"help": "1: WASD, 2: Arrows" | |
}, | |
"flappybird": { | |
"link": "https://github.com/nanochess/fbird", | |
"data": "uAIAzRD8uAC4jtiOwL+gDzHAq6ursKCrsGCrv0oAuEYPq7Atq7BCq7BJq7BSq7BEq7lQAFHo/ABZ4vm0Ac0WnDHAzRaddfSgqA8CBqQPoqgPJPi0FPbkg8Agl6CsDyQEdBSKhWD/x4Vg/x4NAgXQ6McFFA3rBooFxwUfDQJFAsdFAhANPEB0J8YFKsZFAiq/yge4Qg+rsE+rsE6rsEursCGruWQAUeg5AVni+ele/+gwAaCsDyQHdQT/BqQPsCCIhWD/iEUCquhqAOhnAIA+oACwdAWAPqIAsHUh/waiD6GiD7+OADHSuwoA9/OBwjAMkv2rxgUg/JIJwHXptAHNFnQvtADNFjwbdQLNIKGoD4PoEIP4CHIDo6gPxgakDwCwtuZDsJDmQrBK5kLkYQwD5mHpE/++ogC/oAC5TwDzpbggDqutgf6iD3XwxwaeD98C5EAkcHQYuwgEiR7+Dr9eDiQgdAaJHYHvoADHBR4J/w6mD4sepg+D+wN3ZnUK5ECD4AcEBKOqD4sOqg8J27KwdAmy24P7A3ICsrG/PgG0CojQq4HHngDi+bDEq4HHvgOw36uBx54AiNCrgceeAIH/AA9y8wnbdRqhoA9Ao6APsQPT6LRQKMSA/BB3ArQQiCamD8O0AM0aUrQAzRpbOdN09v8GrA/kYST85mHDT1RHVao=", | |
"description": "A Flappy Bird clone with reasonably better graphics than the previous petty bird version", | |
"asm": " ;\n ; F-bird text game in a bootsector\n ;\n ; by Oscar Toledo G.\n ; http://nanochess.org/\n ;\n ; Creation date: Jun/04/2017. A messy unoptimized thing.\n ; Revision date: Jun/05/2017. Better usage of graphic charset.\n ; Removed a non-8088 long jump. Added\n ; sound. Solved bug when overwriting\n ; previous score.\n ;\n\n use16\n\n mov ax,0x0002 ; Set 80x25 text mode\n int 0x10 ; Call BIOS\n cld ; Reset direction flag (so stosw increments registers)\n mov ax,0xb800 ; Point to video segment\n mov ds,ax ; Both the source (common access)\n mov es,ax ; and target segments\n ;\n ; Game restart\n ;\nfb21: mov di,pipe ; Init variables in video segment (saves big bytes)\n xor ax,ax\n stosw ; pipe\n stosw ; score\n stosw ; grav\n mov al,0xa0\n stosw ; next\n mov al,0x60\n stosw ; bird\n\n mov di,0x004a ; Game title\n mov ax,0x0f46 ; 'F' in white, good old ASCII\n stosw\n mov al,0x2d ; '-'\n stosw\n mov al,0x42 ; 'B'\n stosw\n mov al,0x49 ; 'I'\n stosw\n mov al,0x52 ; 'R'\n stosw\n mov al,0x44 ; 'D'\n stosw\n mov cx,80 ; Introduce 80 columns of scenery\nfb1: push cx\n call scroll_scenery\n pop cx\n loop fb1\n\nfb23: mov ah,0x01 ; Check if key pressed\n int 0x16\n pushf\n xor ax,ax ; Wait for a key\n int 0x16\n popf\n jnz fb23 ; Jump if key was accumulated, if not already waited for key ;)\n\n ;\n ; Main loop\n ;\nfb12: mov al,[bird] ; Bird falls... \n add al,[grav] ; ...because of gravity...\n mov [bird],al ; ...into new position.\n and al,0xf8 ; Row is a 5.3 fraction, nullify fraction\n mov ah,0x14 ; Given integer is x8, multiply by 20 to get 160 per line\n mul ah ; Row into screen\n add ax,$0020 ; Fixed column\n xchg ax,di ; Pass to DI (AX cannot be used as pointer)\n mov al,[frame]\n and al,4 ; Wing movement each 4 frames\n jz fb15\n mov al,[di-160] ; Get character below\n mov word [di-160],$0d1e ; Draw upper wing\n add al,[di] ; Add another character below\n shr al,1 ; Normalize\n mov word [di],$0d14 ; Draw body\n jmp short fb16\n\nfb15: mov al,[di] ; Get character below\n mov word [di],$0d1f ; Draw body\nfb16: add al,[di+2] ; Get character below head\n mov word [di+2],$0d10 ; Draw head\n cmp al,0x40 ; Collision with scenery?\n jz fb19\n ;\n ; Stars and game over\n ;\n mov byte [di],$2a ; '*' Asterisks to indicate crashing\n mov byte [di+2],$2a\n mov di,0x07CA \n mov ax,0x0f42 ; 'B' in white, good old ASCII\n stosw\n mov al,0x4F ; 'O'\n stosw\n mov al,0x4E ; 'N'\n stosw\n mov al,0x4B ; 'K'\n stosw\n mov al,0x21 ; '!'\n stosw\n mov cx,100 ; Wait 100 frames\nfb20: push cx\n call wait_frame \n pop cx\n loop fb20\n jmp fb21 ; Restart\n\nfb19: call wait_frame ; Wait for frame\n mov al,[frame]\n and al,7 ; 8 frames have passed?\n jnz fb17 ; No, jump\n inc word [grav] ; Increase gravity\nfb17:\n mov al,$20\n mov [di-160],al ; Delete bird from screen\n mov [di+2],al\n stosb\n call scroll_scenery ; Scroll scenery\n call scroll_scenery ; Scroll scenery\n cmp byte [0x00a0],0xb0 ; Passed a column?\n jz fb27\n cmp byte [0x00a2],0xb0 ; Passed a column?\nfb27: jnz fb24\n inc word [score] ; Increase score\n mov ax,[score]\n mov di,0x008e ; Show current score\nfb25: xor dx,dx ; Extend AX to 32 bits\n mov bx,10 ; Divisor is 10\n div bx ; Divide\n add dx,0x0c30 ; Convert remaining 0-9 to ASCII, also put color\n xchg ax,dx\n std\n stosw\n mov byte [di],0x20 ; Clean at least one character of prev. score\n cld\n xchg ax,dx\n or ax,ax ; Score digits still remain?\n jnz fb25 ; Yes, jump\nfb24: mov ah,0x01 ; Any key pressed?\n int 0x16\n jz fb26 ; No, go to main loop\n mov ah,0x00\n int 0x16 ; Get key\n cmp al,0x1b ; Escape key?\n jne fb4 ; No, jump\n int 0x20 ; Exit to DOS or to oblivion (boot sector)\nfb4: mov ax,[bird]\n sub ax,0x10 ; Move bird two rows upward\n cmp ax,0x08 ; Make sure the bird doesn't fly free outside screen\n jb fb18\n mov [bird],ax\nfb18: mov byte [grav],0 ; Reset gravity\n mov al,0xb6 ; Flap sound\n out (0x43),al\n mov al,0x90\n out (0x42),al\n mov al,0x4a\n out (0x42),al\n in al,(0x61)\n or al,0x03 ; Turn on sound\n out (0x61),al\nfb26: jmp fb12\n\n ;\n ; Scroll scenery one column at a time\n ;\nscroll_scenery:\n ;\n ; Move whole screen\n ;\n mov si,0x00a2 ; Point to row 1, column 1 in SI\n mov di,0x00a0 ; Point to row 1, column 0 in DI\nfb2: mov cx,79 ; 79 columns\n repz ; Scroll!!!\n movsw\n mov ax,0x0e20 ; Clean last character\n stosw\n lodsw ; Advance source to keep pair source/target\n cmp si,0x0fa2 ; All scrolled?\n jnz fb2 ; No, jump\n ;\n ; Insert houses\n ;\n mov word [0x0f9e],0x02df ; Terrain\n in al,(0x40) ; Get \"random\" number\n and al,0x70\n jz fb5\n mov bx,0x0408 ; House of one floor\n mov [0x0efe],bx\n mov di,0x0e5e\n and al,0x20 ; Check \"random\" number\n jz fb3\n mov [di],bx ; House of two floors\n sub di,0x00a0\nfb3: mov word [di],0x091e ; Add roof\n ;\n ; Check if it's time to insert a column\n ;\nfb5: dec word [next] ; Decrease time (column really) for next pipe\n mov bx,[next]\n cmp bx,0x03 ; bx = 3,2,1,0 for the four columns making the pipe\n ja fb6\n jne fb8\n in al,(0x40) ; Get \"random\" number\n and ax,0x0007 ; Between 0 and 7\n add al,0x04 ; Between 4 and 11\n mov [tall],ax ; This will tell how tall the pipe is\nfb8: mov cx,[tall]\n or bx,bx ; Rightmost?\n mov dl,0xb0\n jz fb7 ; Yes, jump\n mov dl,0xdb\n cmp bx,0x03 ; Leftmost?\n jb fb7 ; No, jump\n mov dl,0xb1\nfb7: mov di,0x013e ; Start from top of screen\n mov ah,0x0a\n mov al,dl\nfb9: stosw\n add di,0x009e\n loop fb9\n mov al,0xc4\n stosw\n add di,0x009e*6+10\n mov al,0xdf \n stosw\n add di,0x009e\nfb10: mov al,dl\n stosw\n add di,0x009e\n cmp di,0x0f00\n jb fb10\n or bx,bx\n jnz fb6\n mov ax,[pipe]\n inc ax ; Increase total pipes shown\n mov [pipe],ax\n mov cl,3\n shr ax,cl\n mov ah,0x50 ; Decrease distance between pipes\n sub ah,al\n cmp ah,0x10\n ja fb11\n mov ah,0x10\nfb11: mov [next],ah ; Time for next pipe\nfb6: ret\n\n ;\n ; Wait for a frame\n ;\nwait_frame:\n mov ah,0x00 ; Use base clock tick\n int 0x1a\nfb14: push dx\n mov ah,0x00 ; Read again base clock tick\n int 0x1a\n pop bx\n cmp bx,dx ; Wait for change\n jz fb14\n inc word [frame] ; Increase frame count\n in al,(0x61)\n and al,0xfc ; Turn off sound\n out (0x61),al\n ret\n\n db \"OTG\" ; 3 unused bytes\n\n db 0x55,0xaa ; Bootable signature\n\npipe: equ 0x0fa0\nscore: equ 0x0fa2\ngrav: equ 0x0fa4\nnext: equ 0x0fa6\nbird: equ 0x0fa8\ntall: equ 0x0faa\nframe: equ 0x0fac\n", | |
"help": "" | |
}, | |
"bootman": { | |
"link": "https://github.com/guyhill/Boot-Man", | |
"data": "+jHAjtiIFnR8jtC8KABQaI59UGhgfInEQM0QuQAgtAHNEPy4ALiOwL+IB77Lfbr6Bbk8AK3R4FC42wFyC7j5DznXdQS2ALAEq1cBz6tfWIPpBHnhg+9wedj7sCDmIOv6YL6pff5MBHQUYc8eB7gBArkBALqAALsAfM0T64PGRAQDikQDixTo4gB0A4hEAopEAosU6NUAdBG4IA8mgD0EdQOIRAWHFOjgALsbAIh8VL3//4oggHwFIHQDgPQIsM6LUAE7FHUDiERUUjjgdDHomgB0LLkQDDh8BXUFiwwDSAVQKNEo9Q++wQ+vwA++zQ+vyQHBWDnpcweJzYgAiVABWiwEPMJzw4tAA+h9AIPrB3mei1AIOxR1A4hEVOhVACaLBYlACoPDB4P7G3XluOwvsRA4fAV1Fzh8VHQZixS4Dw7oRgAAXATGBmh8Auta/kwFtA+xAItQAegvAADMg+sHefO4Ag6LFOggAOs8om99/sKA4h9SD7b+a/8otgAB14PHBNHnWiaAPdvD6Of/q8Ng5GAsIXMSJAPA4AL22ATOOgarfXQDoqx9Yc8PD8rKEADCAQH5DwAAzgEX+Q8ABMoeAfkP/ADOHhf5DwQA//8AgP2/gYC/+gCC/b4BgL/+gAC//j+Av66/rr+Av/6AAP3+gYC/vgCA/b79vgGA//8AVao=", | |
"description": "This is a PacMan clone with individual Ghost AI and working power pills. This game is in text-mode", | |
"asm": "; Boot-Man\n;\n; (c) 2019 Guido van den Heuvel\n;\n; Boot-Man is a Pac-Man clone that fits (snugly) inside the Master Boot Record of a USB stick.\n; A USB stick with Boot-Man on it boots into the game (hence the name). Unfortunately, however,\n; Boot-Man leaves no room in the MBR for a partition table, which means that a USB stick with Boot-Man\n; in its MBR cannot be used to store data. In fact, Windows does not recognize a USB stick with \n; Boot-Man in its MBR as a valid storage medium.\n;\n; Controls of the game: you control Boot-Man using the WASD keys. No other user input is necessary. Some other\n; keys can also be used to control Boot-Man, this is a side effect of coding my own keyboard handler in \n; just a few bytes. There simply wasn't room for checking the validity of every key press.\n;\n; The game starts automatically, and when Boot-Man dies, a new game starts automatically within a couple of seconds.\n;\n;\n; I've had to take a couple of liberties with the original Pac-Man game to fit Boot-Man inside the 510\n; bytes available in the MBR:\n;\n; * The ghosts start in the four corners of the maze, they do not emerge from a central cage like in the original\n;\n; * There's just a single level. If you finish the level, the game keeps running with an empty maze. While \n; it is rather difficult to finish the game (which is intentional, because of the single level), it is possible. \n;\n; * Boot-Man only has 1 life. If Boot-Man dies, another game is started automatically (by re-reading the MBR\n; from disk, there simply isn't enough room to re-initialize the game in any other way)\n;\n; * Power pills function differently from the original. When Boot-Man eats a power pill, all ghosts become\n; ethereal (represented in game by just their eyes being visible) and cease to chase Boot-Man. While ethereal,\n; Boot-Man can walk through ghosts with no ill effects. While I would really like to include the \"ghost hunting\"\n; from the original, which I consider to be an iconic part of the game, this simply isn't possible in the little\n; space available.\n;\n; * There's no score, and no fruit to get bonus points from.\n; \n; * All ghosts, as well as Boot-Man itself, have the same, constant movement speed. In the original, the ghosts\n; run at higher speeds than Pac-Man, while Pac-Man gets delayed slightly when eating and ghosts get delayed when moving\n; through the tunnel connecting both sides of the maze. This leads to very interesting dynamics and strategies\n; in the original that Boot-Man, by necessity, lacks.\n;\n;\n; Boot-Man runs in text mode. It uses some of the graphical characters found in IBM codepage 437 for its objects:\n; - Boot-Man itself is represented by the smiley face (\u263b), which is character 0x02 in the IBM charset\n; - The Ghosts are represented by the infinity symbol (\u221e), which is character 0xec. These represent\n; a ghost's eyes, with the ghost's body being represented simply by putting the character on a \n; coloured background\n; - The dots that represent Boot-Man's food are represented by the bullet character (\u2022), \n; which is character 0xf9\n; - The power pills with which Boot-Man gains extra powers are represented by the diamond (\u2666),\n; which is character 0x04\n; - The walls of the maze are represented by the full block character (\u2588), which is character 0xdb\n;\n; Boot-Man runs off int 8, which is connected to the timer interrupt. It should therefore run at the same speed\n; on all PCs. It includes its own int 9 (keyboard) handler. The code is quite heavily optimized for size, so\n; code quality is questionable at best, and downright atrocious at worst.\n\n\norg 0x7c00 ; The MBR is loaded at 0x0000:0x7c00 by the BIOS\nbits 16 ; Boot-Man runs in Real Mode. I am assuming that the BIOS leaves the CPU is Real Mode.\n ; This is true for the vast majority of PC systems. If your system's BIOS\n ; switches to Protected Mode or Long Mode during the boot process, Boot-Man\n ; won't run on your machine.\n\nstart:\n cli ; Disable interrupts, as we are going to set up interrupt handlers and a stack\n xor ax, ax \n mov ds, ax ; Set up a data segment that includes the Interrupt Vector Table and the Boot-Man code\n mov [bootdrive], dl ; save the current drive number, which has been stored in dl by the BIOS\n mov ss, ax \n mov sp, 0x28 ; Set up a temporary stack, within the interrupt vector table\n push ax ; This saves some bytes when setting up interrupt vectors\n push word int9handler ; Set up my own int 9 (keyboard) handler\n push ax\n push word int8handler ; Set up my own int 8 (timer interrupr) handler\n \n mov sp, ax ; Set up the real stack.\n\n inc ax ; int 0x10 / ah = 0: Switch video mode. Switch mode to 40x25 characters (al = 1). \n int 0x10 ; In this mode, characters are approximately square, which means that horizontal \n ; and vertical movement speeds are almost the same.\n\n mov cx, 0x2000 ; int 0x10 / ah = 1: Determine shape of hardware cursor. \n mov ah, 0x01 ; With cx = 0x2000, this removes the hardware cursor from the screen altogether\n int 0x10\n\n\n cld ; Clear the direction flag. We use string instructions a lot as they have one-byte codes\n mov ax, 0xb800\n mov es, ax ; Set up the es segment to point to video RAM\n\n\n;-----------------------------------------------------------------------------------------------------\n; buildmaze: builds the maze. The maze is stored in memory as a bit array, with 1 representing a wall\n; and 0 representing a food dot. Since the maze is left-right symmetrical, only half of the\n; maze is stored in memory. The positions of the power pills is hard-coded in the code.\n; Adding the power pills to the bit array would have necessitated 2 bits for every \n; character, increasing its size drastically.\n;\n; Both sides of the maze are drawn simultaneously. The left part is drawn left to right,\n; while the right part is drawn right to left. For efficiency reasons, the entire maze \n; is built from the bottom up. Therefore, the maze is stored upside down in memory\n;-----------------------------------------------------------------------------------------------------\nbuildmaze:\n mov di, 0x0788 ; Lower left corner of maze in video ram\n mov si, maze ; The first byte of the bit array containing the maze\n mov dx, 0x05fa ; Address in video ram of the lower left powerpill\n.maze_outerloop:\n mov cx, 0x003c ; The distance between a character in the maze and its \n ; symmetric counterpart. Also functions as loop counter\n lodsw ; Read 16 bits from the bit array, which represents one\n ; 32 character-wide row of the maze\n.maze_innerloop:\n shl ax, 1 ; shift out a single bit to determine whether a wall or dot must be shown\n push ax\n mov ax, 0x01db ; Assume it is a wall character (0x01: blue; 0xdb: full solid block)\n jc .draw ; Draw the character if a 1 was shifted out\n mov ax, 0x0ff9 ; otherwise, assume a food character (0x0f: white; x0f9: bullet)\n cmp di, dx ; See if instead of food we need to draw a power pill\n jnz .draw \n mov dh, 0x00 ; Update powerpill address to draw remaining powerpills\n mov al, 0x04 ; powerpill character (0x04: diamond - no need to set up colour again)\n.draw:\n stosw ; Store character + colour in video ram\n push di\n add di, cx ; Go to its symmetric counterpart\n stosw ; and store it as well\n pop di\n pop ax\n sub cx, 4 ; Update the distance between the two sides of the maze\n jns .maze_innerloop ; As long as the distance between the two halves is positive, we continue\n\n sub di, 0x70 ; Go to the previous line on the screen in video RAM. \n jns .maze_outerloop ; Keep going as long as this line is on screen.\n\n\n sti ; Initialization is complete, hence we can enable interrupts\n\n.end: ; Idle loop, as everything else is done in the interrupt handlers.\n ; Unfortunately there's no room for a hlt here so the CPU keeps running 100%.\n mov al, 0x20 ; Continuously write \"end of interrupt\". This saves a few bytes in the \n out 0x20, al ; interrupt handlers themselves, as we have to do it only once, here.\n jmp .end ; Overall, not a good way to implement an idle loop, its only saving grace \n ; being that it is so short.\n\n\n;-----------------------------------------------------------------------------------------------------\n; int8handler: The main loop of the game. Tied to int 8 (the timer interrupt, which fires 18x per \n; second by default), to ensure that the game runs at the same speed on all machines\n;\n; The code first updates Boot-Man's position according to its movement direction\n; and keyboard input. Then the ghost AI is run, to determine the ghosts' movement\n; direction and update their position. Finally, Boot-Man and the ghosts are drawn\n; in their new positions. Collisions between Boot-Man and the ghosts are checked\n; before and after ghost movement. We need to detect for collisions twice, because\n; if you only check once, Boot-Man can change position with a ghost without colliding\n; (in the original, collisions are checked only once, and as a consequence, it is \n; possible in some circumstances to actually move Pac-Man through a ghost). \n;-----------------------------------------------------------------------------------------------------\nint8handler:\n pusha\n mov si, bootman_data ; Use si as a pointer to the game data. This reduces byte count of the code:\n ; mov reg, [address] is a 4 byte instruction, while mov reg, [si] only has 2.\n dec byte [si + pace_offset] ; Decrease the pace counter. The pace counter determines the overall\n ; speed of the game. We found that moving at a speed of 6 per second\n ; gives good speed and control, so we use a counter to only move\n ; once for every three times that the interrupt fires.\n ; We also use the pace counter to include longer delays at game start\n ; and after Boot-Man dies, by intitalizing the counter with higher values.\njump_offset: equ $ + 1 \n jz .move_all ; If the pace counter is not 0, we immediately finish.\n popa ; The offset of this jump gets overwritten when boot-man dies\n iret\n\n push ds ; The new offset points here.\n pop es ; This code reloads the MBR and jumps back to the start\n mov ax, 0x0201 ; al = number of sectors to read\n mov cx, 0x0001 ; cx / dh : CHS of sector to read. In this case we read sector 1, the MBR.\nbootdrive: equ $ + 1\n mov dx, 0x0080 ; dl = disk number to read from. This code gets updated with the actual \n ; number of the boot disk at program start.\n mov bx, 0x7c00\n int 0x13 ; int 0x13 / ah = 2: read one or more sectors from storage medium\n jmp start ; Go back to the top once read is complete. This re-inits all \n ; modified code and data\n\n.move_all:\n mov byte [si + pace_offset], 0x3; Reset the pace counter.\n;-----------------------------------------------------------------------------------------------------\n; Move Boot-Man\n;-----------------------------------------------------------------------------------------------------\n mov al, [si + 3] ; al = new movement direction, as determined by keyboard input\n mov dx, [si] ; dx = current position of Boot-Man\n call newpos ; Update dx to move 1 square in the direction indicated by al\n ; newpos also checks for collisions with walls (in which case ZF is set)\n jz .nodirchange ; ZF indicates that new position collides with wall. We therefore try to keep\n ; moving in the current direction instead.\n mov [si + 2], al ; If there's no collision, update the current movement direction\n.nodirchange:\n mov al, [si + 2] ; al = current movement direction\n mov dx, [si] ; dx = current position of Boot-Man\n call newpos ; Update dx to move 1 square in direction al\n jz .endbootman ; If there's a wall there, do nothing\n.move:\n mov ax, 0x0f20 ; Prepare to remove Boot-Man from screen, before drawing it in the new position\n ; 0x0f = black background, white foreground; 0x20 = space character\n cmp byte [es:di], 0x04 ; Detect power pill\n jnz .nopowerpill \n mov byte [si + timer_offset], al; If Boot-Man just ate a power pill, set up the ghost timer to 0x20. We use al here\n ; as it accidentally contains 0x20, and this is one byte shorter than having an \n ; explicit constant.\n.nopowerpill:\n xchg dx, [si] ; Retrieve current position and store new position\n call paint ; Actually remove Boot-Man from the screen\n.endbootman:\n;-----------------------------------------------------------------------------------------------------\n; ghost AI and movement\n; \n; Determine the new movement direction for each ghost. Ghost movement direction is determined by\n; the following rule:\n; (1) Every ghost must keep moving\n; (2) It is forbidden for ghosts to suddenly start moving backwards. Unless Boot-Man just consumed \n; a powerpill, in which case ghosts are forbidden from continuing in the direction they were going\n; (3) Whenever a ghost has multiple movement options (i.e., it is at a crossroads), try moving 1 space\n; in each direction that is allowed, and calculate the distance to the target location after \n; that move. Choose the direction for which this distance is lowest as the new movement direction\n;\n; During normal movement, ghosts target a position that is related to the position of Boot-Man, as follows:\n; \n; number | ghost colour | target\n; -------+--------------+-------------------\n; 1 | purple | bootman's position\n; 2 | red | 4 squares below Boot-Man\n; 3 | cyan | 4 squares to the left of Boot-Man\n; 4 | green | 4 squares to the right of Boot-Man\n;\n; There's two different reasons for having slightly different AI for each ghost:\n; (1) If all ghosts have the same AI they tend to bunch together and stay there. With the current AI, \n; ghosts will sometimes bunch together, but they will split apart eventually\n; (2) With this setup, the ghosts tend to surround Boot-Man, making it harder for the player\n; \n; When Boot-Man picks up a power pill, a timer starts running, and ghosts become ethereal.\n; As long as the ghosts are ethereal, the \n; ghosts will not chase Boot-Man. Instead they will use the center of the big rectangular block\n; in the middle of the maze as their target. They cannot reach it, obviously, so the result is \n; that they will keep circling this block for as long as the timer runs.\n;\n; This AI is related to, but not the same as, the AI actually used in Pac-Man. The red Pac-Man ghost\n; uses Pac-Man itself as target, same as my purple ghost, while the pink Pac-Man ghost will \n; target 4 squares ahead of Pac-Man, in the direction Pac-Man is currently moving. The other ghosts' \n; movement is a bit more complex than that. I had to simplify the AI because of the limited code size.\n;-----------------------------------------------------------------------------------------------------\n mov bx, 3 * gh_length + bm_length ; Set up offset to ghost data. With this, si + bx is a \n ; pointer to the data from the last ghost. Also used as \n ; loop counter to loop through all the ghosts \n mov byte [si + collision_offset], bh ; Reset collision detection. BH happens to be 0 at this point\n.ghost_ai_outer:\n mov bp, 0xffff ; bp = minimum distance; start out at maxint\n mov ah, [bx + si] ; ah will become the forbidden movement direction. We start\n ; with the current direction, which is forbidden if Boot-Man\n ; just ate a power pill\n cmp byte [si + timer_offset], 0x20 ; If timer_offset == 0x20, Boot-Man just picked up a power pill\n jz .reverse ; so in that case we do not flip the direction.\n xor ah, 8 ; Flip the current direction to obtain the forbidden direction in ah\n.reverse:\n mov al, 0xce ; al = current directions being tried. Doubles as loop counter\n ; over all directions.\n ; Values are the same as those used by the newpos routine\n mov dx, [bx + si + gh_offset_pos] ; dx = current ghost position\n cmp dx, [si] ; compare dx with Boot-Man position\n jne .ghost_ai_loop ; If they are equal,\n mov [si + collision_offset], al ; We store a non-zero value in the collision_detect flag\n ; We use al here as we know it to be non-zero, and this reduces\n ; code size compared to using a literal constant.\n.ghost_ai_loop:\n push dx\n cmp al, ah ; If the current direction is the forbidden direction\n jz .next ; we continue with the next direction\n call newpos ; Update ghost position and check if it collides with a wall\n jz .next ; if so, we continue with the next direction\n mov cx, 0x0c10 ; Target position if ghosts are ethereal. Position 0x0c10 \n ; (x = 0x10, y = 0x0c) is in the center of the maze.\n cmp byte [si + timer_offset], bh ; See if ghost timer runs. We compare with bh, which is known to be 0.\n jnz .skip_target ; If ghost timer runs, we use the aforementioned target position\n mov cx, [si] ; Otherwise we use Boot-Man's current position,\n add cx, [bx + si + gh_offset_focus] ; Updated with an offset that is different for each ghost\n.skip_target:\n;-----------------------------------------------------------------------------------------------------\n; get_distance: Calculate distance between positions in cx (target position) and dx (ghost position)\n; This used to be a function, but I inlined it to save some space.\n; The square of the distance between the positions in cx and dx is calculated,\n; according to Pythagoras' theorem.\n;-----------------------------------------------------------------------------------------------------\n push ax\n sub cl, dl ; after this, cl contains the horizontal difference\n sub ch, dh ; and ch the vertical difference\n\n movsx ax, cl\n imul ax, ax ; ax = square of horizontal difference\n movsx cx, ch \n imul cx, cx ; cx = square of vertical difference\n\n add cx, ax ; cx = distance squared between positions in cx and dx\n pop ax\n\n cmp cx, bp ; Compare this distance to the current minimum\n jnc .next ; and if it is,\n mov bp, cx ; update the minimum distance\n mov [bx + si], al ; set the movement direction to the current direction\n mov [bx + si + gh_offset_pos], dx ; Store the new ghost position\n.next:\n pop dx ; Restore the current ghost position \n sub al, 4 ; Update the current direction / loop counter\n cmp al, 0xc2 \n jnc .ghost_ai_loop\n\n mov ax, [bx + si + gh_offset_terrain] ; Remove the ghost in the old position from the screen \n call paint ; by painting the terrain underneath that ghost that was \n ; determined in the previous movement phase.\n sub bx, gh_length ; Go to the next ghost,\n jns .ghost_ai_outer ; and stop after the final ghost\n\n\n.ghostterrain_loop: ; Second loop through all the ghosts, to determine terrain\n ; underneath each one. This is used in the next movement phase\n ; to restore the terrain underneath the ghosts.\n ; Note that this \"terrain storing\" approach can trigger a bug\n ; if Boot-Man and a ghost share a position. In that case\n ; an extra Boot-Man character may be drawn on screen.\n mov dx, [bx + si + gh_offset_pos + gh_length] ; dx = updated ghost position\n cmp dx, [si] ; compare dx with Boot-Man's position\n jne .skip_collision ; and if they coincide,\n mov [si + collision_offset], al ; set the collision detect flag to a non-zero value.\n.skip_collision:\n call get_screenpos ; find the address in video ram of the updated ghost position,\n mov ax, [es:di] ; store its content in ax\n mov [bx + si + gh_offset_terrain + gh_length], ax ; and copy it to ghostterrain\n add bx, gh_length ; go to next ghost\n cmp bx, 3 * gh_length + bm_length ; and determine if it is the final ghost\n jnz .ghostterrain_loop\n\n ; Test if ghosts are invisible\n mov ax, 0x2fec ; Assume ghost is visible: 0x2f = purple background, white text\n ; 0xec = infinity symbol = ghost eyes \n mov cl, 0x10 ; cl = difference in colour between successive ghosts\n ; ch is set to zero as that leads to smaller code\n cmp byte [si + timer_offset], bh ; See if ghost timer is running (note bh is still zero at this point)\n jnz .ghosts_invisible ; If it is, ghosts are ethereal\n\n cmp byte [si + collision_offset], bh ; Ghosts are visible, so test for collisions\n jz .no_collision\n\n ; Ghosts are visible and collide with boot-man, therefore boot-man is dead\n mov dx, [si] ; dx = current Boot-Man position\n mov ax, 0x0e0f ; Dead boot-man: 0x0e = black background, yellow foreground\n ; 0x0f = 8 pointed star\n call paint\n add byte [si + pace_offset], bl ; Update pace counter: this introduces a small period of mourning \n ; after Boot-Man's death. \n ; It was 3, and bl = 0x1b at this point, so it becomes 0x1e\n mov byte [jump_offset], 2 ; Modify the pace code at the start of this handler, to jump to the\n ; code that re-loads the MBR and re-starts the game\n jmp intxhandler_end\n\n ; Ghosts are invisible\n.ghosts_invisible:\n dec byte [si + timer_offset] ; Update ghost_timer to limit the period of the ghosts being ethereal\n mov ah, 0x0f ; Update ghost colour to black background, white eyes\n mov cl, 0x0 ; Update difference between colours of successive ghosts. Value of 0x0\n ; means all ghosts are the same colour when they are ethereal.\n\n.no_collision:\n.ghostdraw: ; Draw the ghosts on the screen\n mov dx, [bx + si + gh_offset_pos] ; dx = new ghost position\n call paint ; show ghost in video ram\n add ah, cl ; Update ghost colour.\n sub bx, gh_length ; Loop over all ghosts\n jns .ghostdraw ; until the final one.\n\n \n mov ax, word 0x0e02 ; Draw boot-man on the screen. 0x0e = black background, yellow foreground\n ; 0x02 = smiley face\n mov dx, [si] ; dx = new Boot-Man position\n call paint ; show Boot-Man\n\n.end:\n jmp intxhandler_end\n\n\n;-----------------------------------------------------------------------------------------------------\n; newpos: calculates a new position, starting from a position in dx and movement direction in al.\n; dl contains the x coordinate, while dh contains the y coordinate. The movement directions\n; in al are as follows:\n; 0xc2: move right\n; 0xc6: move down\n; 0xca: move left\n; 0xce: move up\n;\n; The reason for these fairly strange values is that they form the 2nd byte (the ModR/M byte)\n; of the instruction updating the position: \n; inc dl (0xfe, 0xc2), inc dh (0xfe), dec dl (0xfe, 0xca), dec dh (0xfe, 0xce)\n; The code first modifies itself to the correct instruction, then executes this instruction. The\n; reason for doing it in this way is that this is a lot shorter than the traditional way of \n; doing an if / elif / elif / else chain.\n;\n; Immediately after calculating the new position we also determine the address in video RAM \n; corresponding to this position. All lines of the screen are stored one after the other in RAM,\n; starting at 0xb800:0x0000. Since each line has 40 characters, and every character takes up\n; two bytes (one for colour, one for the character code), the equation to calculate video RAM\n; offset from x, y coordinates is as follows:\n; \n; offset = 2 * (40 * y + x + 4),\n;\n; with the +4 due to the fact that the maze is in the center of the screen, with a 4 character wide\n; border to the left.\n;\n; newpos and get_screenpos used to be two separate functions but since they were almost\n; always called one after the other, combining them saved some bytes of code.\n;-----------------------------------------------------------------------------------------------------\nnewpos:\n mov [.modified_instruction + 1], al ; Here the instruction to be executed is modified\n.modified_instruction:\n db 0xfe, 0xc2 ; inc dl in machine code\n and dl, 0x1f ; Deal with tunnels\nget_screenpos:\n push dx\n movzx di, dh ; di = y coordinate\n imul di, di, 0x28 ; multiply di by 0x28 = 40 decimal, the screen width\n mov dh, 0 ; After this, dx contains the x coordinate\n add di, dx ; di = y * 40 + x\n add di, 4 ; Skip the left border by adding 4 to di\n shl di, 1 ; Multiply di by 2\n pop dx\n cmp byte [es:di], 0xdb ; Check to see if the new position collides with a wall\n ; 0xdb = full block character that makes up the wall\n ret\n\n;-----------------------------------------------------------------------------------------------------\n; paint: paints a character on screen at given x, y coordinates in dx\n; simple convenience function that gets called enough to be actually worth it, in terms\n; of code length.\n;-----------------------------------------------------------------------------------------------------\npaint:\n call get_screenpos ; Convert x, y coordinates in dx to video memory address\n stosw ; stosw = shorter code for mov [es:di], ax\n ; stosw also adds 2 to di, but that effect is ignored here\n ret\n\n\n;-----------------------------------------------------------------------------------------------------\n; int9handler: the keyboard handler. It converts the scancodes for WASD into valid movement \n; directions, as described in the comment block above the newpos function.\n; We perform some bit wrangling to convert one into the other. This used to be\n; a simple if / elif / elif / else construction, but this bit-wrangling code is\n; much shorter, albeit much more obscure.\n; In the hope of documenting how this works, the comments show how these scancodes\n; are modified step by step into valid movement directions.\n;\n; The reason that this works at all is that, coincidentally, WASD are in order of scancode,\n; (which is not a coincidence, as these scancodes reflect the geometry of the original\n; IBM PC keyboards), and that the lowest 2 bits of these are different for all 4.\n; Unfortunately, they are ordered in the wrong way: scancodes ascend while movement\n; directions descend. Therefore an extra minus sign is needed somewhere, which is\n; provided by the neg instruction.\n;-----------------------------------------------------------------------------------------------------\nint9handler:\n pusha\n in al, 0x60 ; We use the legacy I/O port for the keyboard. This code \n ; would also work in an IBM PC from almost 40 years ago\n\n ; This code converts al from scancode to movement direction.\n ; Input: 0x11 (W), 0x1e (A), 0x1f (S), 0x20 (D)\n ; Output: 0xce (up), 0xca (right), 0xc6 (down), 0xc2 (left) \n ;\n ; Other scancodes below 0x21 are also mapped onto a movement direction\n ; Starting input: 0x11 0x1e 0x1f 0x20\n sub al, 0x21 ; 0xf0 0xfd 0xfe 0xff\n jnc intxhandler_end ; if al >= 0x21, ignore scancode;\n ; this includes key release events\n and al, 3 ; 0x00 0x01 0x02 0x03\n shl al, 2 ; 0x00 0x04 0x08 0x0c\n neg al ; 0x00 0xfc 0xf8 0xf4\n add al, 0xce ; 0xce 0xca 0xc6 0xc2\n cmp al, [bootman_data + 2] ; If the new direction is the same as the current direction, ignore it\n jz intxhandler_end\n mov [bootman_data + 3], al ; Set new direction to the direction derived from the keyboard input\nintxhandler_end:\n popa\n iret\n\nbootman_data:\n db 0x0f, 0x0f ; Boot-Man's x and y position\n db 0xca ; Boot-Man's direction\n db 0xca ; Boot-Man's future direction\n\npace_counter: db 0x10\nghost_timer: db 0x0 ; if > 0 ghosts are invisible, and is counted backwards to 0\n\nghostdata:\n db 0xc2 ; 1st ghost, direction\nghostpos:\n db 0x01, 0x01 ; x and y position\nghostterrain:\n dw 0x0ff9 ; terrain underneath\nghostfocus:\n db 0x0, 0x0 ; focus point for movement\nsecondghost:\n db 0xce ; 2nd ghost, direction\n db 0x01, 0x17 ; x and y position\n dw 0x0ff9 ; terrain underneath\n db 0x0, 0x4 ; focus point for movement\n db 0xca ; 3rd ghost, direction\n db 0x1e, 0x01 ; x and y position\n dw 0x0ff9 ; terrain underneath\n db 0xfc, 0x0 ; focus point for movement\n db 0xce ; 4th ghost, direction\n db 0x1e, 0x17 ; x and y position\n dw 0x0ff9 ; terrain underneath\n db 0x4, 0x0 ; focus point for movement\nlastghost:\n\nbm_length equ ghostdata - bootman_data\ngh_length equ secondghost - ghostdata\ngh_offset_pos equ ghostpos - ghostdata\ngh_offset_terrain equ ghostterrain - ghostdata\ngh_offset_focus equ ghostfocus - ghostdata\npace_offset equ pace_counter - bootman_data\ntimer_offset equ ghost_timer - bootman_data\n\n; The maze, as a bit array. Ones denote walls, zeroes denote food dots / corridors\n; The maze is stored upside down to save one cmp instruction in buildmaze\nmaze: dw 0xffff, 0x8000, 0xbffd, 0x8081, 0xfabf, 0x8200, 0xbefd, 0x8001\n dw 0xfebf, 0x0080, 0xfebf, 0x803f, 0xaebf, 0xaebf, 0x80bf, 0xfebf\n dw 0x0080, 0xfefd, 0x8081, 0xbebf, 0x8000, 0xbefd, 0xbefd, 0x8001\n dw 0xffff\nmaze_length: equ $ - maze\n\n; Collision detection flag. It is initialized by the code\ncollision_detect:\n\ncollision_offset equ collision_detect - bootman_data\n\ntimes 510 - ($ - $$) db 0\ndb 0x55\ndb 0xaa\n", | |
"help": "wasd" | |
}, | |
"pillman": { | |
"description": "Another PacMan clone and even programmed around the same time as Boot-Man. Though there are no power pills in this version, it's the smooth graphics-mode that sets this version apart.", | |
"data": "uBMAzRD8uACgjtiOwL7Bfb8gFC6tkbvwANHhuBg3cwO4OALoIgAB34PrEHJP6BgAKd+D7wjr4q2XrYTkk7AwUMZE/wLorgBYUFNRV1C7eX0u15O5CACI+NDjcgIxwID/EHIHgD0OdJgyBari6YHHOAFYQKgHddVfWVtYw4HHiAmB/ut9dY2xBbgCAC6lq+L7tADNGjsWBPp09okWBPq0Ac0WtAB0As0WiOA8AXUCzSAsSHIMPAlzCLv1fS7Xov75viDmrZetkzHA6CsAgDb/+YC4KA54B6Ai5rED0uDoaP+9N3y3If/Vty7/1bco/9W3NP/V65t0A+hO/4n4MdK5QAH38YjUCMSA5Ad1WrU3OG3/EOQ4rQAKEOQ4bQgQ5DitwP4Q5IT/dCv2wwV0DDsWAPqwAnIMsAjrCDwAsARyArABhMR1I4jYhMR1HdDodfiwCOv0iRYA+i6iOn2g/vmExHUGINx0GojYiET+qAW7gP11A7sCAKgMdAL32wHfiXz8wwBC5+f//348PH788PD8fjz//////////zx+///n50IAPH7/////fjw8ftvb////pQAAABgYAAAAPH4/Dw8/fjwAAP5/AkICQv9/QEJ+fgICfwLAA0ACfwJAAv5/AkL/e0AKfn4CQP9/AACYqpBQmGSgPKhQAQAACAACAAAEVao=", | |
"link": "https://github.com/nanochess/Pillman", | |
"asm": " ;\n ; Pillman\n ;\n ; by Oscar Toledo G.\n ; http://nanochess.org/\n ;\n ; (c) Copyright 2019 Oscar Toledo G.\n ;\n ; Creation date: Jun/11/2019.\n ; Revision date: Jun/12/2019. Draws level.\n ; Revision date: Jun/13/2019. Pillman can move.\n ; Revision date: Jun/14/2019. Now ghosts don't get stuck. Ghost are\n ; transparent. Pillman doesn't leave\n ; trash.\n ; Revision date: Jun/15/2019. Ghosts can catch pillman. Optimized.\n ; 509 bytes.\n ; Revision date: Jul/09/2019. Self-modifying code, move subroutine,\n ; cache routine address (Peter Ferrie).\n ; 504 bytes.\n ; Revision date: Jul/22/2019. Added Esc key to exit.\n ;\n\n cpu 8086\n\n %ifndef com_file ; If not defined create a boot sector\ncom_file: equ 0\n %endif\n\nbase: equ 0xf9fe ; Memory base (same segment as video)\nintended_dir: equ base+0x00 ; Next direction for player\nframe: equ base+0x01 ; Current video frame\nx_player: equ base+0x02 ; Saved X-coordinate of player\ny_player: equ ms6+0x01 ; Saved Y-coordinate of player\nold_time: equ base+0x06 ; Old time\n\n ;\n ; Maze should start at x,y coordinate multiple of 8\n ;\nBASE_MAZE: equ 16*X_OFFSET+32 \npos1: equ BASE_MAZE+21*8*X_OFFSET\n\nX_OFFSET: equ 0x0140\n\nMAZE_COLOR: equ 0x37 ; No color should be higher or equal value\nPILL_COLOR: equ 0x02 ; Color for pill\nPLAYER_COLOR: equ 0x0e ; Should be unique\n\n ;\n ; XOR combination of these plus PILL_COLOR shouldn't\n ; result in PLAYER_COLOR\n ;\nGHOST1_COLOR: equ 0x21 ; Ghost 1 color\nGHOST2_COLOR: equ 0x2e ; Ghost 2 color\nGHOST3_COLOR: equ 0x28 ; Ghost 3 color\nGHOST4_COLOR: equ 0x34 ; Ghost 4 color\n\n %if com_file\n org 0x0100 ; Start address for COM file\n %else\n org 0x7c00 ; Start address for boot sector\n %endif\nrestart:\n mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA)\n int 0x10 ; Call BIOS\n cld\n mov ax,0xa000 ; Video segment\n mov ds,ax ; Use as source data segment\n mov es,ax ; Use as target data segment\n ;\n ; Draw the maze\n ;\n mov si,maze ; SI = Address of maze data\n mov di,BASE_MAZE ; DI = Address for drawing maze\ndraw_maze_row:\n cs lodsw ; Load one word of data from Code Segment\n xchg ax,cx ; Put into AX\n mov bx,30*8 ; Offset of mirror position\ndraw_maze_col:\n shl cx,1 ; Extract one tile of maze\n mov ax,MAZE_COLOR*0x0100+0x18 ; Carry = 0 = Wall\n jnc dm1 ; If bit was zero, jump to dm1\n mov ax,PILL_COLOR*0x0100+0x38 ; Carry = 1 = Pill\ndm1: call draw_sprite ; Draw tile\n add di,bx ; Go to mirror position\n sub bx,16 ; Mirror finished?\n jc dm2 ; Yes, jump\n call draw_sprite ; Draw tile\n sub di,bx ; Restore position\n sub di,8 ; Advance tile\n jmp draw_maze_col ; Repeat until finished\n\n ;\n ; Move ghost\n ; bh = color\n ;\nmove_ghost:\n lodsw ; Load screen position\n xchg ax,di\n lodsw ; Load direction\n test ah,ah\n xchg ax,bx ; Color now in ah\n mov al,0x30\n push ax\n mov byte [si-1],0x02 ; Remove first time setup flag\n call move_sprite3\n pop ax\n ;\n ; Draw the sprite/tile\n ;\n ; ah = sprite color\n ; al = sprite (x8)\n ; di = Target address\ndraw_sprite:\n push ax\n push bx\n push cx\n push di\nds0: push ax\n mov bx,bitmaps-8\n cs xlat ; Extract one byte from bitmap\n xchg ax,bx\n mov cx,8 \nds1: mov al,bh\n shl bl,1 ; Extract one bit \n jc ds2\n xor ax,ax ; Background color\nds2:\n cmp bh,0x10 ; Color < 0x10\n jc ds4 ; Yes, jump\n cmp byte [di],PLAYER_COLOR ; \"Eats\" player?\n je restart ; Yes, it should crash after several hundred games\nds3:\n xor al,[di] ; XOR ghost again pixel\nds4:\n stosb\n loop ds1\n add di,X_OFFSET-8 ; Go to next video line\n pop ax\n inc ax ; Next bitmap byte\n test al,7 ; Sprite complete?\n jne ds0 ; No, jump\n pop di\n pop cx\n pop bx\n pop ax\n ret\n\ndm2: \n add di,X_OFFSET*8-15*8 ; Go to next row\n cmp si,setup_data ; Maze completed?\n jne draw_maze_row ; No, jump\n\n ;\n ; Setup characters\n ;\n ; CX is zero at this point\n ; DI is equal to pos1 at this point\n ;mov di,pos1\n mov cl,5 ; 5 elements (player + ghosts)\n mov ax,2 ; Going to right\ndm3:\n cs movsw ; Copy position from Code Segment\n stosw ; Store desired direction\n loop dm3 ; Loop\n\n ;\n ; Main game loop\n ;\ngame_loop:\n mov ah,0x00\n int 0x1a ; BIOS clock read\n cmp dx,[old_time] ; Wait for time change\n je game_loop\n mov [old_time],dx ; Save new time\n\n mov ah,0x01 ; BIOS Key available\n int 0x16\n mov ah,0x00 ; BIOS Read Key\n je no_key\n int 0x16\nno_key:\n mov al,ah\n cmp al,0x01 ; Esc key\n jne no_esc\n int 0x20\nno_esc:\n sub al,0x48 ; Code for arrow up?\n jc no_key2 ; Out of range, jump.\n cmp al,0x09 ; Farther than arrow down?\n jnc no_key2 ; Out of range, jump.\n mov bx,dirs\n cs xlat ; Translate direction to internal code\n mov [intended_dir],al ; Save as desired direction\nno_key2:\n mov si,pos1 ; SI points to data for player\n lodsw ; Load screen position\n xchg ax,di\n lodsw ; Load direction/type\n xchg ax,bx\n xor ax,ax ; Delete pillman\n call move_sprite2 ; Move\n xor byte [frame],0x80 ; Alternate frame\n mov ax,0x0e28 ; Closed mouth\n js close_mouth ; Jump if sign set.\n mov al,[pos1+2] ; Using current direction\n mov cl,3 ; Multiply by 8\n shl al,cl ; Show open mouth\nclose_mouth:\n call draw_sprite ; Draw\n ;\n ; Move ghosts\n ;\n mov bp, move_ghost\n mov bh,GHOST1_COLOR\n call bp\n mov bh,GHOST2_COLOR\n call bp\n mov bh,GHOST3_COLOR\n call bp\n mov bh,GHOST4_COLOR\n call bp\n jmp game_loop \n\n ;\n ; DI = address on the screen\n ; BL = wanted direction\n ;\nmove_sprite3: \n je move_sprite ; If zero, won't remove first time\nmove_sprite2:\n call draw_sprite ; Remove ghost\nmove_sprite:\n mov ax,di ; Prepare to extract pixel row/column\n xor dx,dx\n mov cx,X_OFFSET\n div cx\n ; Now AX = Row, DX = Column\n mov ah,dl\n or ah,al\n and ah,7 ; Both aligned at 8 pixels?\n jne ms0 ; No, jump because cannot change direction.\n ; AH is zero already\n ;mov ah,0\n ;\n ; Get available directions\n ;\n mov ch,MAZE_COLOR\n cmp [di-0x0001],ch ; Left\n adc ah,ah ; AH = 0000 000L\n cmp [di+X_OFFSET*8],ch ; Down\n adc ah,ah ; AH = 0000 00LD\n cmp [di+0x0008],ch ; Right\n adc ah,ah ; AH = 0000 0LDR\n cmp [di-X_OFFSET],ch ; Up\n adc ah,ah ; AH = 0000 LDRU\n\n test bh,bh ; Is it pillman?\n je ms4 ; Yes, jump\n\n ;\n ; Ghost\n ;\n test bl,0x05 ; Test BL for .... .D.U\n je ms6 ; No, jump\n ; Current direction is up/down\n cmp dx,[x_player] ; Compare X coordinate with player\n mov al,0x02 ; Go right\n jc ms8 ; Jump if X ghost < X player\n mov al,0x08 ; Go left\n jmp ms8\n\n ; Current direction is left/right\nms6: cmp al,0x00 ; (SMC) Compare Y coordinate with player\n mov al,0x04 ; Go down\n jc ms8 ; Jump if Y ghost < Y player\n mov al,0x01 ; Go up\nms8:\n test ah,al ; Can it go in intended direction?\n jne ms1 ; Yes, go in direction\n\n mov al,bl\nms9: test ah,al ; Can it go in current direction?\n jne ms1 ; Yes, jump\n shr al,1 ; Try another direction\n jne ms9\n mov al,0x08 ; Cycle direction\n jmp ms9\n\n ;\n ; Pillman\n ;\nms4:\n mov [x_player],dx ; Save current X coordinate\n cs mov [y_player],al ; Save current Y coordinate\n\n mov al,[intended_dir]\n test ah,al ; Can it go in intended direction?\n jne ms1 ; Yes, go in that direction\n\nms5: and ah,bl ; Can it go in current direction?\n je ms2 ; No, stops\n\nms0: mov al,bl\n\nms1: mov [si-2],al ; Save new direction\n test al,5 ; If going up/down...\n mov bx,-X_OFFSET*2 ; ...bx = vertical movement\n jne ms3\n mov bx,1*2 ; ...bx = horizontal movement\nms3:\n test al,12\n je ms7\n neg bx ; Reverse direction\nms7:\n add di,bx ; Do move\n mov [si-4],di ; Save the new screen position\nms2:\n ret\n\n ;\n ; Game bitmaps\n ;\nbitmaps:\n db 0x00,0x42,0xe7,0xe7,0xff,0xff,0x7e,0x3c ; dir = 1\n db 0x3c,0x7e,0xfc,0xf0,0xf0,0xfc,0x7e,0x3c ; dir = 2\n db 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ; Maze\n db 0x3c,0x7e,0xff,0xff,0xe7,0xe7,0x42,0x00 ; dir = 4\n db 0x3c,0x7e,0xff,0xff,0xff,0xff,0x7e,0x3c ; Closed mouth\n db 0x3c,0x7e,0xdb,0xdb,0xff,0xff,0xff,0xa5 ; Ghost\n db 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00 ; Pill\n db 0x3c,0x7e,0x3f,0x0f,0x0f,0x3f,0x7e,0x3c ; dir = 8\n\n ;\n ; Maze shape\n ;\nmaze:\n dw 0b0000_0000_0000_0000\n dw 0b0111_1111_1111_1110\n dw 0b0100_0010_0000_0010\n dw 0b0100_0010_0000_0010\n dw 0b0111_1111_1111_1111\n dw 0b0100_0010_0100_0000\n dw 0b0111_1110_0111_1110\n dw 0b0000_0010_0000_0010\n dw 0b0000_0010_0111_1111\n dw 0b0000_0011_1100_0000\n dw 0b0000_0010_0100_0000\n dw 0b0000_0010_0111_1111\n dw 0b0000_0010_0100_0000\n dw 0b0111_1111_1111_1110\n dw 0b0100_0010_0000_0010\n dw 0b0111_1011_1111_1111\n dw 0b0000_1010_0100_0000\n dw 0b0111_1110_0111_1110\n dw 0b0100_0000_0000_0010\n dw 0b0111_1111_1111_1111\n dw 0b0000_0000_0000_0000\n\n ;\n ; Starting positions\n ; \nsetup_data:\n dw BASE_MAZE+0x78*X_OFFSET+0x78\n dw BASE_MAZE+0x30*X_OFFSET+0x70\n dw BASE_MAZE+0x40*X_OFFSET+0x78\n dw BASE_MAZE+0x20*X_OFFSET+0x80\n dw BASE_MAZE+0x30*X_OFFSET+0x88\n\n ;\n ; Convert arrow codes to internal directions\n ;\ndirs:\n db 0x01 ; 0x48 = Up arrow\n db 0x00\n db 0x00\n db 0x08 ; 0x4b = Left arrow\n db 0x00\n db 0x02 ; 0x4d = Right arrow\n db 0x00\n db 0x00\n db 0x04 ; 0x50 = Down arrow\n\n %if com_file\n %else\n times 510-($-$$) db 0x4f\n db 0x55,0xaa ; Make it a bootable sector\n %endif\n\n", | |
"help": "" | |
}, | |
"invaders": { | |
"description": "A graphics-mode clone of Invaders, very playable and difficult.", | |
"data": "uBMAzRD8uACgjtiOwLQEo5D8McC5SH4x//OriwVAQKuIxJK4AByruJjmq7goFLsQJbELq4PAFpOr/sST4vUFjhWA/1x167+g1LEFuAQL6IUBg8c84vW+lvyAfAIgci3+xYD9N3SsrZetPCh0CzwgdQTGRP4o6F4Bgf5y/XXbiPAsAnLSNAGIwojG68qAdAIIRYPlB1JWVXUOtADNGjsWjPx09okWjPy+gPy5BACtPUABl3JD6BcBge9CAYoFLCByKVZXtAT25AHGrZfGBCC4CA7oAgFfXusfrQnAdC09APCXcxTo6ACBxz4BuAwMiXz+gL1AAQt1BTHAiUT+gD0cdQbHBpL8OAroxgDizL6S/K0IwHQMBAh1CLQc/g6R/HhKiUT+izzorgB1LrQCzRaoBHQCT0+oCHQCR0eoA3QNgz6A/AB1Bo1FCKOA/Jc9/uV0Bz0y53QCiQRdXlqLBID6AXYKBYACPYDUchvNIEhIcgODwARQ0eixoPbx/syA/JRYcgOAzhaJBAUGD5O5AwDkQDz8cg2/gvyDPQB0A6/i+Ikd6cL+GBg8JDx+/yQAgEIYEEiCAQC9234kPGbDADxa/6U8ZmYAAAAAAAAAAHICMcCJhUABq8NRV5xQu6R9LteTuQoA+Ij4iPzo4f/Q4+L1gcdsAlhAqAd14J1fWcNPVao=", | |
"link": "https://github.com/nanochess/Invaders", | |
"asm": " ;\n ; Invaders in 512 bytes\n ;\n ; by Oscar Toledo G.\n ;\n ; (c) Copyright 2015-2019 Oscar Toledo G.\n ;\n ; Creation: Oct/27/2015.\n ; Revision: Nov/06/2015. Adjusted bullet collision. Invaders\n ; accelerate.\n ; Revision: Apr/03/2019. Invaders now can shoot. Spaceship does\n ; explosion.\n ; Revision: May/28/2019. Invaders goes down at 11px instead 12px.\n ; Now starts another invaders wave more\n ; difficult.\n ; Revision: Jun/01/2019. Redesigned for 320x200x256 mode.\n ; Revision: Jun/02/2019. Now in color. Color carries information\n ; about thing being hit.\n ; Revision: Jun/03/2019. Optimized, 601 bytes as COM!!!\n ; Revision: Jun/04/2019. At last 512 bytes!\n ; Revision: Jun/05/2019. By popular demand added pure8088 option. Now\n ; the 8088 version also is bootable! so now\n ; 8088 is the default.\n ; Revision: Jun/06/2019. jtsiomb made the point that the COM file\n ; doesn't need to be 512 bytes, so Esc for\n ; exiting and also returns to text mode.\n ; Revision: Jun/29/2019. Now spaceship moves to left pressing Ctrl,\n ; to right pressing Alt, and shoots pressing\n ; Shift. Spaceship stops when you depress the\n ; direction key. To exit you press Scroll\n ; Lock. Used the extra bytes to implement\n ; barriers that stop the invaders' bullets.\n ; (suggested in Reddit by nils-m-holm).\n ;\n\n ;\n ; Using PUSHA and POPA the code can be smaller, not enabled by\n ; default.\n ;\n\n %ifndef pure8088 ; Define as 0 to create a 80186/80286 binary\npure8088: equ 1 ; Enabled by default for pure 8088 assembler\n\n cpu 8086\n %endif\n\n %ifndef com_file ; If not defined create a boot sector\ncom_file: equ 0\n %endif\n\nbase: equ 0xfc80 ; Memory base (same segment as video)\n\nshots: equ base+0x00 ; Space to contain 4 shots (2 bytes each one)\n ; Plus space for a ignored shot (full table)\n ; Notice (sprites + SPRITE_SIZE) - (shots + 2)\n ; must be divisible by SPRITE_SIZE.\nold_time: equ base+0x0c ; Old time\nlevel: equ base+0x10 ; Current level number\nlives: equ base+0x11 ; Current lives\nsprites: equ base+0x12 ; Space to contain sprite table\n\nSHIP_ROW: equ 0x5c*OFFSET_X ; Row of spaceship\nX_WIDTH: equ 0x0140 ; X-width of video\nOFFSET_X: equ X_WIDTH*2 ; X-offset between screen rows (2 pixels)\nSPRITE_SIZE: equ 4 ; Size of each sprite in bytes\n\n ;\n ; All colors different (important to distinguish things)\n ;\nSPACESHIP_COLOR: equ 0x1c ; Must be below 0x20\nBARRIER_COLOR: equ 0x0b\nSHIP_EXPLOSION_COLOR: equ 0x0a\nINVADER_EXPLOSION_COLOR: equ 0x0e\nBULLET_COLOR: equ 0x0c\nSTART_COLOR: equ ((sprites+SPRITE_SIZE-(shots+2))/SPRITE_SIZE+0x20) \n\n %if com_file\n org 0x0100 ; Start position for COM files\n %else\n org 0x7c00 ; Start position for boot sector\n %endif\n mov ax,0x0013 ; Set mode 0x13 (320x200x256 VGA)\n int 0x10 ; Call BIOS\n cld\n mov ax,0xa000 ; Point to screen memory\n mov ds,ax ; Both DS...\n mov es,ax ; ...and ES\n mov ah,0x04\n mov [level],ax ; Level = 0, Lives = 4\nrestart_game:\n xor ax,ax\n mov cx,level/2 ; Clear screen and variables (except level/lives)\n xor di,di\n rep\n stosw ; ch is zero from here\n\n ;\n ; Setup descend state\n ;\n mov ax,[di] ; al now contains level, ah contains lives\n inc ax ; Increase by 2 (so invaders descend correctly)\n inc ax\n stosw ; Advance level\n mov ah,al\n xchg ax,dx ; Shouldn't damage DX starting here\n\n ;\n ; Setup the spaceship\n ;\n mov ax,SPACESHIP_COLOR*0x0100+0x00\n stosw\n mov ax,SHIP_ROW+0x4c*2\n stosw\n ;\n ; Setup the invaders\n ;\n mov ax,0x08*OFFSET_X+0x28\n mov bx,START_COLOR*0x0100+0x10\nin1: mov cl,0x0b ; Eleven invaders per row\nin5: stosw ; Set invader position\n add ax,0x0b*2 ; Go to next column\n xchg ax,bx\n stosw ; Set invader color and shape\n inc ah ; Go to next color\n xchg ax,bx\n loop in5 ; Loop and also make sure ch is zero\n add ax,0x09*OFFSET_X-0x000b*0x000b*2 ; Go to next row\n cmp bh,START_COLOR+55 ; Whole board finished?\n jne in1 ; No, jump\n\n ;\n ; Draw the barriers\n ;\n mov di,0x55*0x280+0x10*2\n mov cl,5\nin48:\n mov ax,BARRIER_COLOR*0x0100+0x04\n call draw_sprite\n add di,0x1e*2\n loop in48\n\n ; CH is zero\n\nin14:\n mov si,sprites+SPRITE_SIZE\n\n ;\n ; Game loop\n ;\n ; Globals:\n ; SI = Next invader to animate\n ; DL = state (0=left, 1=right, >=2 down)\n ; DH = nstate (next state)\n ; CH = dead invaders\n ; BP = frame counter\n ;\nin46:\n cmp byte [si+2],0x20 ; Current invader is cosmic debris?\n jc in2 ; No, jump\n inc ch ; Count another dead invader\n cmp ch,55 ; All invaders defeated?\n je restart_game ; Yes, jump.\n ;\n ; Yes, invaders speed up\n ;\nin6:\n lodsw ; Load position in AX\n xchg ax,di ; Move to DI\n lodsw ; Get type of sprite\n cmp al,0x28 ; Destroyed?\n je in27 ; Yes, jump\n cmp al,0x20 ; Explosion?\n jne in29 ; No, jump\n mov byte [si-2],0x28 ; Don't draw again\nin29: call draw_sprite ; Draw invader on screen\nin27: cmp si,sprites+56*SPRITE_SIZE ; Whole board revised?\n jne in46 ; No, jump\n mov al,dh\n sub al,2 ; Going down?\n jc in14 ; No, preserve left/right direction\n xor al,1 ; Switch direction\n mov dl,al\n mov dh,al\n jmp in14\n\nin2:\n xor byte [si+2],8 ; Invader animation (before possible explosion)\n ;\n ; Synchronize game to 18.20648 hz. of BIOS\n ;\n inc bp\n and bp,7 ; Each 8 invaders\n %if pure8088\n push dx\n push si\n push bp\n %else\n pusha\n %endif\n jne in12\nin22:\n mov ah,0x00 \n int 0x1a ; BIOS clock read\n cmp dx,[old_time] ; Wait for change\n je in22\n mov [old_time],dx ; Save new current time\nin12:\n %if 1\n ;\n ; Handle player bullet\n ;\n mov si,shots ; Point to shots list\n mov cx,4 ; 4 shots at most\n lodsw ; Read position (player)\n cmp ax,X_WIDTH ; Is it at top of screen?\n xchg ax,di\n jc in31 ; Erase bullet\n ; Doesn't mind doing it all time\n call zero ; Remove bullet \n sub di,X_WIDTH+2\n mov al,[di] ; Read pixel\n sub al,0x20 ; Hits invader?\n jc in30 ; No, jump\n %if pure8088\n push si\n push di\n %else\n pusha\n %endif\n mov ah,SPRITE_SIZE ; The pixel indicates the...\n mul ah ; ...invader hit.\n add si,ax\n lodsw\n xchg ax,di\n mov byte [si],0x20 ; Erase next time\n mov ax,INVADER_EXPLOSION_COLOR*0x0100+0x08 ; But explosion now\n call draw_sprite ; Draw sprite\n %if pure8088\n pop di\n pop si\n %else\n popa\n %endif\n jmp in31\n\n ;\n ; Handle invader bullets\n ;\nin24:\n lodsw ; Read current coordinate\n or ax,ax ; Is it falling?\n je in23 ; No, jump\n cmp ax,0x60*OFFSET_X ; Pixel lower than spaceship?\n xchg ax,di\n jnc in31 ; Yes, remove bullet\n call zero ; Remove bullet \n add di,X_WIDTH-2 ; Bullet falls down\n\n ; Draw bullet\nin30:\n mov ax,BULLET_COLOR*0x0100+BULLET_COLOR\n mov [si-2],di ; Update position of bullet\n cmp byte [di+X_WIDTH],BARRIER_COLOR ; Barrier in path?\n jne in7 ; Yes, erase bullet and barrier pixel\n\n ; Remove bullet\nin31: xor ax,ax ; AX contains zero (DI unaffected)\n mov [si-2],ax ; Delete bullet from table\n\nin7: cmp byte [di],SPACESHIP_COLOR ; Check collision with player\n jne in41 ; No, jump\n mov word [sprites],SHIP_EXPLOSION_COLOR*0x0100+0x38 ; Player explosion\nin41:\n call big_pixel ; Draw/erase bullet\nin23: loop in24\n %endif\n\n ;\n ; Spaceship handling\n ;\n mov si,sprites ; Point to spaceship\n lodsw ; Load sprite frame / color\n or al,al ; Explosion?\n je in42 ; No, jump\n add al,0x08 ; Keep explosion\n jne in42 ; Finished? No, jump\n mov ah,SPACESHIP_COLOR ; Restore color (sprite already)\n dec byte [lives] ; Remove one life\n js in10 ; Exit if all used\nin42: mov [si-2],ax ; Save new frame / color\n mov di,[si] ; Load position\n call draw_sprite ; Draw sprite (spaceship)\n jne in43 ; Jump if still explosion\n\n mov ah,0x02 ; BIOS Get Keyboard Flags \n int 0x16\n %if com_file\n test al,0x10 ; Test for Scroll Lock and exit\n jnz in10\n %endif\n\n test al,0x04 ; Ctrl key?\n jz in17 ; No, jump\n dec di ; Move 2 pixels to left\n dec di\n\nin17: test al,0x08 ; Alt key?\n jz in18 ; No, jump\n inc di ; Move 2 pixels to right\n inc di\nin18:\n test al,0x03 ; Shift keys?\n jz in35 ; No, jump\n cmp word [shots],0 ; Bullet available?\n jne in35 ; No, jump\n lea ax,[di+(0x04*2)] ; Offset from spaceship\n mov [shots],ax ; Start bullet\nin35:\n xchg ax,di\n cmp ax,SHIP_ROW-2 ; Update if not touching border\n je in43\n cmp ax,SHIP_ROW+0x0132\n je in43\nin19: mov [si],ax ; Update position\nin43:\n %if pure8088\n pop bp\n pop si\n pop dx\n %else\n popa\n %endif\n\n mov ax,[si] ; Get position of current invader\n cmp dl,1 ; Going down (state 2)?\n jbe in9 ; No, jump\n add ax,0x0280 ; Go down by 2 pixels\n cmp ax,0x55*0x280 ; Reaches Earth?\n jc in8 ; No, jump\nin10:\n %if com_file\n mov ax,0x0003 ; Restore text mode\n int 0x10\n %endif\n int 0x20 ; Exit to DOS\n\nin9: dec ax ; Moving to left\n dec ax\n jc in20\n add ax,4 ; Moving to right\nin20: push ax\n shr ax,1 ; Divide position by 2...\n mov cl,0xa0 ; ...means we can get column dividing by 0xa0\n div cl ; ...instead of 0x0140 (longer code)\n dec ah ; Convert 0x00 to 0xff\n cmp ah,0x94 ; Border touched? (>= 0x94)\n pop ax\n jb in8 ; No, jump\n or dh,22 ; Goes down by 11 pixels (11 * 2) must be odd\nin8: mov [si],ax\n add ax,0x06*0x280+0x03*2 ; Offset for bullet\n xchg ax,bx\n\n mov cx,3 ; ch = 0 - invader alive\n in al,(0x40) ; Read timer\n cmp al,0xfc ; Random event happening?\n jc in4 ; No, jump\n ;\n ; Doesn't work in my computer:\n ;\n ; mov di,shots+2\n ; xor ax,ax\n ; repne scasw\n ; mov [di-2],bx\n ;\n mov di,shots+2\nin45: cmp word [di],0 ; Search for free slot\n je in44 ; It's free, jump!\n scasw ; Advance DI\n loop in45 ; Until 3 slots searched\nin44:\n mov [di],bx ; Start invader shot (or put in ignored slot)\nin4:\n jmp in6\n\n ;\n ; Bitmaps for sprites\n ;\nbitmaps:\n db 0x18,0x18,0x3c,0x24,0x3c,0x7e,0xFf,0x24 ; Spaceship\n db 0x00,0x80,0x42,0x18,0x10,0x48,0x82,0x01 ; Explosion\n db 0x00,0xbd,0xdb,0x7e,0x24,0x3c,0x66,0xc3 ; Alien (frame 1)\n db 0x00,0x3c,0x5a,0xff,0xa5,0x3c,0x66,0x66 ; Alien (frame 2)\n db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; Erase\n\n ;\n ; Draw pixel per Carry (use AX if Carry=1 or zero if Carry=0)\n ;\nbit: jc big_pixel\nzero: xor ax,ax\n ; Draw a big pixel (2x2 pixels)\nbig_pixel:\n mov [di+X_WIDTH],ax\n stosw\n ret\n\n ; ah = sprite color\n ; al = sprite (x8)\n ; di = Target address\ndraw_sprite:\n %if pure8088\n push cx\n push di\n pushf\n %else\n pusha\n %endif\nin3: push ax\n mov bx,bitmaps\n cs xlat ; Extract one byte from bitmap\n xchg ax,bx ; bl contains byte, bh contains color\n mov cx,10 ; Two extra zero pixels at left and right\n clc ; Left pixel as zero (clean)\nin0: mov al,bh ; Duplicate color in AX\n mov ah,bh\n call bit ; Draw pixel\n shl bl,1\n loop in0\n add di,OFFSET_X-20 ; Go to next video line\n pop ax\n inc ax ; Next bitmap byte\n test al,7 ; Sprite complete?\n jne in3 ; No, jump\n %if pure8088\n popf\n pop di\n pop cx\n %else\n popa\n %endif\n ret\n\n %if com_file\n %else\n times 510-($-$$) db 0x4f\n db 0x55,0xaa ; Make it a bootable sector\n %endif\n", | |
"help": "" | |
}, | |
"BootSlide": { | |
"link": "https://github.com/XlogicX/BootSlide", | |
"asm": ";15 Number Slider Game\n; * Slide the numbers around until they are in order\n; * This is boot sector ( <512 bytes) game\n; * Controls to move are up/down/left/right\n; * Any other key rescrambles numbers\n; Some Fuckery:\n; Polymorphic\n; Abuse of \"Call\"/Ret\n; * There are routines to move the tiles in the up/down/left/right direction,\n; They are controlled by the user AND the scrambling routines. There is a\n; 'valid' way to conditionally call and then return, but this requires a\n; bit more space with all of the control/flow logic. I would rather just\n; manually set the corresponding return points once, and then just do a \n; bunch of conditional jumps, sort of evil, but less code.\n\n[ORG 0x7c00]\nLEFT EQU 75\nRIGHT EQU 77\nUP EQU 72\nDOWN EQU 80\n\n;Init the environment\nxor ax, ax ; make it zero\nmov ds, ax ; DS=0\nmov ss, ax ; stack starts at 0\nmov sp, 0x9c00 ; 200h past code start\nmov ah, 0xb8 ; text video memory\nmov es, ax ; ES=0xB800\nmov ah, 1\nmov ch, 0x26 \nint 0x10\n;Fill in all black\nmov cx, 0x07d0 ; whole screens worth\ncbw ; clear ax (black on black with null char)\nxor di, di ; first coordinate of video mem\nrep stosw ; push it to video memory\n\nmov bp, 0x9 ; blanklocation\n\n; Evil?\nin al,(0x40) ; Get random\ncmp al, 0x10 ; 1 in 16 chance it will be evil\nja draw_border ; If above, then not evil\n; Evil (Descending solvability)\nmov ax, [boarddata] ; Get first and second board number\nxchg ah,al ; And then swap them\nmov [boarddata], ax ; (now evil, can't solve in ascending order)\nmov byte [border + 1], 0xcc ; Change border color to light red\nmov byte [tile1 + 2], 0x48 ; Change tile border to shaded red\nmov byte [tile2 + 1], 0x44 ; Change tile to red\nmov byte [number_color + 1], 0x40 ; Change number to black on red\n\n;draw border\ndraw_border:\nmov di, 1 * 160 + 44 ; corner to start on\nborder: mov ah, 0xff ; white\nmov bx, 22 ; rows\nmov byte [rectrow + 1], 30 ; columns\nmov byte [nextline + 2], 0x64 ; value to get to next line\ncall drawrect\n\n; Scramble\nmov cl, 0xff ; Init to 255 rounds of movement\nscramble:\n dec cx\n je gameloop ; Once done, go to main game loop\n in al,(0x40) ; Get 'random' value\n and al, 3 ; Only preserve last 2 bits (for 4 possible up/down/left/right moves)\n push word scramble ; point of return instead of using call for the below jumps\n ; Do a random tile move based on random results\n; cmp al, 0 ; and al,3 already did this comparison\n je up\n cmp al, 1\n je down\n cmp al, 2\n je left\n cmp al, 3\n je right\n\n; The Main Game Loop\ngameloop:\n call drawboard ; Draw the blank tiles of the board\n waitkey:\n call displaytiles ; Put the actual (hex) numbers into the tiles\n mov ah, 1 ; Is there a key\n int 0x16 ; \"\"\n jz waitkey ; If not wait for a key\n cbw ; clear ax (Get the key)\n int 0x16 ; \"\"\n push word gameloop ; point of return instead of using call\n ; Get the Keys\n cmp ah, UP\n je up\n cmp ah, DOWN\n je down\n cmp ah, LEFT\n je left\n cmp ah, RIGHT\n je right\n cmp ah, 0x01\n je exit\n int 0x19 ; A non-directional key was pressed (reboot/rescramble)\n exit:\n mov ax,0x0002 ; Clear screen\n int 0x10\n int 0x20 ; Return to bootOS\n up:\n mov bx, bp ; get blank tile location\n cmp bx, 0xb ; Out of bounds?\n ja keysdone ; Then return (don't move anything)\n add bx, 4 ; get location above it\n mov al, [boarddata + bx] ; get value above it\n mov byte [boarddata + bx], 0 ; make it the new blank\n mov bp, bx ; update blank location\n sub bx, 4 ; revert to old blank location\n mov [boarddata + bx], al ; put new value in\n ret\n down:\n mov bx, bp ; get blank tile location\n cmp bx, 4 ; Out of bounds?\n jb keysdone ; Then return (don't move anything)\n sub bx, 4 ; get location above it\n mov al, [boarddata + bx] ; get value above it\n mov byte [boarddata + bx], 0 ; make it the new blank\n mov bp, bx ; update blank location\n add bx, 4 ; revert to old blank location\n mov [boarddata + bx], al ; put new value in\n ret\n left:\n mov bx, bp ; get blank tile location\n cmp bl, 3 ; All the way to the left?\n je keysdone ; return without moving anything\n cmp bl, 7 ; \"\"\n je keysdone ; \"\"\n cmp bl, 11 ; \"\"\n je keysdone ; \"\"\n cmp bl, 15 ; \"\"\n je keysdone ; \"\"\n add bl, 1 ; get location above it\n mov al, [boarddata + bx] ; get value above it\n mov byte [boarddata + bx], 0 ; make it the new blank\n mov bp, bx ; update blank location\n sub bl, 1 ; revert to old blank location\n mov [boarddata + bx], al ; put new value in\n ret \n right:\n mov bx, bp ; get blank tile location\n ; Test Right edge, this is aligned with positions 0, 4, 8, and 12.\n ; In binary, these are the only positions where the least significant\n ; bits are always 0, so the below instructions tests for this quality\n test bl, 0x1 ; is the last bit a 1\n jne right_cont ; if not (0), continue with movement\n test bl, 0x2 ; is the next bit a 1\n je keysdone ; if so, don't move anything ... somehow this is the right logic\n right_cont:\n sub bl, 1 ; get location above it\n mov al, [boarddata + bx] ; get value above it\n mov byte [boarddata + bx], 0 ; make it the new blank\n mov bp, bx ; update blank location\n add bl, 1 ; revert to old blank location\n mov [boarddata + bx], al ; put new value in \n ret\n keysdone:\n ret\n\ndisplaytiles:\n; This is a routine for displaying all of the tiles\n xor bx, bx\n mov di, 4 * 160 + 52 ; Position of top-left tile\n displaytilesloop:\n number_color: mov ah, 0x30 ; Black on light-blue background\n mov al, byte [boarddata + bx] ; current number value in boarddata structure\n inc bx ; next tile digit for next iteration\n add al, 0x30 ; 'Convert' to ASCII\n cmp al, 0x3A ; Is it above '9'\n jb number ; If its below (in range of number), skip adjustment\n add al, 7 ; Get it into the ASCII A-F range\n number: \n cmp al, 0x30 ; Check to see if it's 0 (our blank tile)\n jne next_a ; Keep processing if it's not\n call blanktile ; Otherwise, draw a blank (black) tile\n jmp next_b ; Skip over normal printing of number\n\n next_a:\n stosw ; Print the Number\n\n next_b:\n add di, 12 ; Go to the next column\n cmp bx, 4 ; Check if it's the last column\n je nextrow ; If so, do a row adjustment\n cmp bx, 8 ; \"\"\n je nextrow ; \"\"\n cmp bx, 12 ; \"\"\n jne norows ; \"\"\n nextrow:\n add di, 744 ; Coordinate adjustment for going to next row\n norows:\n cmp bx, 0x10 ; check to see if last tile has been printed\n jne displaytilesloop ; otherwise keep getting and printing them\nret\n\nblanktile:\n ; A Blank Tile for the '0' tile\n push bx ; Don't pave over bx, as other routines us it\n sub di, 326 ; Adjust coord from middle to upper-left corner of tile\n cbw ; clear ax (black)\n mov bx, 5 ; rows\n mov byte [rectrow + 1], 7 ; columns\n mov byte [nextline + 2], 0x92 ; Value to get to next line\n call drawrect ; Draw the black rectangle\n sub di, 472 ; Restore coordinate to middle of tile\n pop bx ; Restore bx\nret\n\ndrawboard:\n; This routine graphically draws the tiles (without the numbers)\n mov dx, 2 * 160 + 46 - 758 ; This is much before the start position, helps keep a tighter loop\n mov si, 4 ; 4 rows\n row:\n add dx, 758 ; Advance to next row\n call drawtile\n add dx, 14 ; next column\n call drawtile\n add dx, 14\n call drawtile\n add dx, 14\n call drawtile\n dec si\n jne row\nret\n\ndrawtile:\n ; Color 1\n mov di, dx\n tile1: mov ax, 0x13b1 ; shaded corner border\n mov bx, 5 ; rows\n mov byte [rectrow + 1], 7 ; columns\n mov byte [nextline + 2], 0x92 ; Value to get to next line\n call drawrect\n ; Color 2\n mov di, dx\n tile2: mov ah, 0x33 ; inner light blue main part of tile\n mov bx, 4 ; rows\n mov byte [rectrow + 1], 6 ; columns\n mov byte [nextline + 2], 0x94 ; Value to get to next line\n call drawrect\nret\n\ndrawrect:\n ; Draws rectangle\n rectrow: mov cx, 0 ; columns, self-modified by caller\n rep stosw\n ; add di, (next line value) (ammount self-modified by caller)\n nextline: db 0x81, 0xc7, 0x00, 0x00\n dec bx\n jne rectrow\nret\n\n; Data structure for ordered game\nboarddata:\ndb 0x06, 0x0e, 0x04, 0x08, 0x05, 0x09, 0x0c, 0x0f, 0x02, 0x00, 0x0b, 0x03, 0x0d, 0x0a, 0x07, 0x01\n\n;BIOS sig and padding\ntimes 510-($-$$) db 0\ndw 0xAA55\n", | |
"data": "McCO2I7QvACctLiOwLQBtSbNELnQB5gx//OrvQkA5EA8EHccoeB9huCj4H3GBkR8zMYGrX1IxgbBfUTGBjZ9QL/MALT/uxYAxgbUfR7GBtp9ZOh+AbH/SXQX5EAkA2hXfHRCPAF0XTwCdHY8Aw+EnADoEgHouQC0Ac0WdPeYzRZocXyA/Eh0HYD8UHQ3gPxLdE+A/E10doD8AXQCzRm4AgDNEM0gieuD+wsPh4EAg8MEiofgfcaH4H0Aid2D6wSIh+B9w4nrg/sEcmSD6wSKh+B9xofgfQCJ3YPDBIiH4H3DieuA+wN0R4D7B3RCgPsLdD2A+w90OIDDAYqH4H3Gh+B9AIndgOsBiIfgfcOJ6/bDAXUF9sMCdBaA6wGKh+B9xofgfQCJ3YDDAYiH4H3DwzHbv7QCtDCKh+B9QwQwPDpyAgQHPDB1BegfAOsBq4PHDIP7BHQKg/sIdAWD+wx1BIHH6AKD+xB1zMNTge9GAZi7BQDGBtR9B8YG2n2S6FMAge/YAVvDunj+vgQAgcL2AugWAIPCDugQAIPCDugKAIPCDugEAE515MOJ17ixE7sFAMYG1H0HxgbafZLoFQCJ17QzuwQAxgbUfQbGBtp9lOgBAMO5AADzq4HHAABLdfTDBg4ECAUJDA8CAAsDDQoHAQAAAAAAAAAAAAAAAAAAVao=", | |
"description": "A Boot Sector implementation of the physical 15 number sliding tile puzzle. The object is to get the numbers in order. This game supports an 'evil' mode; there is a small chance the tiles will be red, which is an unsolvable orientation.", | |
"help": "" | |
}, | |
"validation": { | |
"link": "https://github.com/XlogicX/Validation", | |
"description": "This is a game that is social commentary on social media. It is not very fun to play and the mechanics work like a slot machine. A much fuller description can be found on the github page.", | |
"data": "McCO2I7QvACctLiOwLADzRC0AbUmzRC1CLSIMf/zq70AAYkObARXvwwA6HMBX1WLLmwEieg8QnUHXUVV/wZsBFe/AgDoWQFfgf3/JQ+H/gBdtAHNFnTPmM0W6AIA68e/tAWB/+oFdHO+sX3kQCQHswn245gBxoPHErICix5sBAHTOR5sBHX6g8YJge8ABejaAIH+un11A4PGP4PuCejLAIH+un11A4PGP4PuCei8AIPGEoH++X11A766fYH+An51A77DfYHvAAqAwgGA+hJ0koH+8H11pb6xfeugMdsmocsFJjsG3QV1AUMmOwbvBXUBQyah3QUmOwbvBXUBQ5OYswr24wQFPAV0PQHFJoA+ywWZdQOD7UkmgD7dBZl1A4PtSSaAPu8FmXUDg+1JJoA+ywVmdRImgD7dBWZ1CiaAPu8FZnUC6xGD7QWF7XgKw7QiXYH9JgFzArREv6AAuYACUfOrgccABVnzq+v+McCKBMHgBMDoBLsRAFL341qzAbEID7ogB3MChuCr/g6DfeLxxgaDfQf+w4HHkACA+wl138OJ6FCG4OgBAFjUEOgAAIbgPAocaS+qsAeqw4IAaZNjY2TLAPEGChQQMDGxoJ4AezYXdDZvAAQALUDAoRIMAE8AAzVLMwMAAP1BvycbAMEUAG4AGDBgACFBNgAAAAAAVao=", | |
"asm": "; Validation\n[ORG 0x7c00]\n\n;Init the environment\nxor ax, ax ; make it zero\nmov ds, ax ; DS=0\nmov ss, ax ; stack starts at 0\nmov sp, 0x9c00 ; 200h past code start\nmov ah, 0xb8 ; text video memory\nmov es, ax ; ES=0xB800\nmov al, 0x03\nint 0x10\nmov ah, 1\nmov ch, 0x26\nint 0x10\n;Fill in all black\nmov ch, 0x8 ; whole screens worth\n;cbw ; clear ax (black on black with null char)\nmov ah, 0x88 ; Fill screen with dark grey color\nxor di, di ; first coordinate of video mem\nrep stosw ; push it to video memory\nmov bp, 0x100 ; initialize validation to 100\nmov [0x046C], cx ; initilize timer to zero (cx already is 0 at this point)\n\nwaitkey:\n ; Print the Score\n push di ; don't interfere with current video location\n mov di, 12 ; video location to print score\n call score ; Update validation score\n pop di ; restore old video location\n\n ; IRL Bonus Check\n push bp ; keep a backup of the score\n mov bp, [0x046C] ; get current time\n mov ax, bp ; get a copy of the time\n cmp al, 0x42 ; compare the last byte with 0x42 (will occur up to 38 times in gameplay assuming no user interaction)\n jne no_irl ; If it wasn't 0x42, skip the 'IRL' bonus\n pop bp ; Get our score back for a second\n inc bp ; Add a point to it\n push bp ; put score back on the stack (because it's going to get popped in a few instructions)\n inc word [0x046c] ; Also increment the clock by a tic, otherwise the score will climb unrestrained.\n\nno_irl:\n push di ; Keep back up of old video location\n mov di, 2 ; video location of life-timer\n call score ; Display the timer on screen\n pop di ; restore old video location\n\n cmp bp, 0x25FF ; Almost 9 minutes (8:55)...and 2600!\n ja win ; If times up, die\n\n pop bp ; Restore actual score (was being used in conjuction with time before this)\n\n ; Key-Press checking\n mov ah, 1 ; Is there a key\n int 0x16 ; \"\"\n jz waitkey ; If not wait for a key\n cbw ; clear ax (Get the key)\n int 0x16 ; \"\"\n call pull_lever ; If there was a key press, pull the lever\n jmp waitkey ; 'Infinite Loop'\n\n\npull_lever:\nmov di, 160 * 9 + 20 ; Coordinate of first slot peice (upper left)\n; Routine for entire column (slot), this will run 3 times\ncolumn_spin:\n cmp di, 160 * 9 + 74 ; Check to see if it is trying for the 4th time\n je results ; If so, it's now time to see if player won validation\n mov si, retweet - 9 ; Get address of ghost sprite before the first one\n in al,(0x40) ; Get random\n and al, 0x07 ; Mask with 7 possibilities\n mov bl, 9 ; We want to multiply by 9 (for sprite size offset)\n mul bl\n cbw ; clear upper part of ax\n add si, ax ; add random offset to base sprite offset\n add di, 18 ; Go to the next column\n mov dl, 2; Initialize delay (starts fast, gets slower)\n spin:\n ; Delay Loop\n mov bx, [0x046C] ; Get timer\n add bx,dx ; Add deley\n delay:\n cmp [0x046C], bx ; Is it there yet\n jne delay ; If not, check again\n ; First Row\n add si, 9 ; Go to next sprite\n sub di, 1280 ; Go down to next row\n call drawsprite ; Draw the sprite\n\n ; Rotating - Draw the next two sprites.\n ; I hate this algorithm. Make sure rotate effect appears from top to bottom\n ; and that there is out of bounds detection and correction.\n ; Second Row\n cmp si, retweet ; Is the second row the first sprite\n jne next_item_a ; If not, don't worry\n add si, 63 ; If so, correct next row for last sprite\n next_item_a:\n sub si, 9 ; Go back another sprite\n call drawsprite ; Draw the sprite\n ; Third Row\n cmp si, retweet ; Is the third row the first sprite\n jne next_item_b ; If not, don't worry\n add si, 63 ; If so, correct next row for last sprite\n next_item_b:\n sub si, 9 ; Go back another sprite\n call drawsprite ; Draw the sprite\n ; Cleanup\n add si, 18 ; Adjust for next rotation\n cmp si, bus + 9 ; First Bounds check\n jne next_item_c ; Skip if within\n mov si, retweet ; Correct\n next_item_c:\n cmp si, bus + 18 ; Second Bounds check\n jne next_item_d ; Skip if within\n mov si, fbthumb ; Correct\n next_item_d:\n sub di, 2560 ; Vertical Adjust graphic coordinate\n\n add dl, 1 ; Get a little slower of spin (0xb4)\n cmp dl, 18 ; Check if spinning is done\n je column_spin ; If so start to spin the next slot\n cmp si, retweet + 54 ; Normal out of bounds check\n jne spin ; if not out of bounds, then keep rotating\n mov si, retweet - 9 ; if so, reset back to first sprite\n jmp spin\n\n results:\n ; Note that there are some es:addresses being checked, these are a selected pixel\n ; from one of each of the 3 middle row images. The pixel color happens to be unique\n ; for each of the 7 image types. So checking for matches can actually work accurately\n xor bx,bx ; init amount\n mov ax, [es:0x5cb]\n cmp ax, [es:0x5dd] ; compare 1 and 2\n jne next_slot_a\n inc bx ; there was a match\n next_slot_a:\n cmp ax, [es:0x5ef] ; compare 1 and 3\n jne next_slot_b\n inc bx ; there was a match\n next_slot_b:\n mov ax, [es:0x5dd]\n cmp ax, [es:0x5ef] ; compare 2 and 3\n jne last_slot\n inc bx ; there was a match\n last_slot:\n ; At this point, if any two match, bx will have 1. If all 3 match, bx will have 3.\n ; Now, adjust points won\n xchg ax,bx\n cbw\n mov bl, 10 ; multiply score by 10\n mul bl\n add al, 5 ; add 5 to the results\n cmp al, 5 ; (0*10) + 5 = 5, in other words, no matches, so:\n je results_done ; skip to results_done\n ; In other words, if there was a match of any 2, then 10 points are awarded, otherwise, 30 points\n add bp, ax ; add to score\n\n ; Now check for penalties, cyberbullying/bus. Note that these checks only happen after the\n ; checking for matches of two or more\n cmp byte [es:0x5cb], 0x99 ; is cyberbully?\n jne next_accident_a ; if not, check next image\n sub bp, 73 ; if so, subtract 73 points\n next_accident_a: cmp byte [es:0x5dd], 0x99 ; is cyberbully?\n jne next_accident_b ; if not, check next image\n sub bp, 73 ; if so, subtract 73 points\n next_accident_b: cmp byte [es:0x5ef], 0x99 ; is cyberbully?\n jne bus_check ; if not, then start checking for busses\n sub bp, 73 ; if so, subtract 73 points\n bus_check: cmp byte [es:0x5cb], 0x66 ; is it a bus?\n jne results_done ; if not, then not all 3 were busses, results are done\n cmp byte [es:0x5dd], 0x66 ; is it a bus?\n jne results_done ; if not, then not all 3 were busses, results are done\n cmp byte [es:0x5ef], 0x66 ; is it a bus?\n jne results_done ; if not, then not all 3 were busses, results are done\n jmp ded ; all 3 were busses, you ded\n\n results_done:\n ; Subtract a point just for checking your status\n sub bp, 5\n\n ; Check for suicide death\n test bp, bp\n js ded\nret\n\nwin:\nmov ah, 0x22 ; Color for Green\npop bp ; Get score from the stack\ncmp bp, 0x126 ; Compare it to the minimum winning score\njae ded_win ; If it's sufficient, stay green and die\nded: \nmov ah, 0x44 ; Otherwise, dishonorable death\nded_win:\n; Fill in color with everything exept top score line and middle slot row, this way you\n; can see time of death, validation at death, and last slot roll\nmov di, 160 ; start at 2nd row of screen (so timer and score is still visible)\nmov cx, 640 ; fill until 2nd slot row\npush cx\nrep stosw ; push it to video memory\nadd di, 1280 ; start again at 3rd slot row\npop cx\nrep stosw\nhalt: jmp halt\n\ndrawsprite:\n ; Gets and unpacks bits that define changes in color for 2 color sprite\n xor ax, ax ; ax, must be cleared\n mov al, byte [si] ; get the packed colors\n shl ax, 4 ; get upper color into ah\n shr al, 4 ; restore lower color nibble in al\n mov bx, 0x0011 ; have color doubles in each register (xxyy)\n push dx ; mul mangles dx, so save it\n mul bx ;\n pop dx ; restore dx\n\n ; Check color and print it\n mov bl, 1 ; init index to 2nd byte in sprite data structure\n columns:\n mov cl, 8 ; 8 pixels a row\n row:\n pixel_check: bt word [si + bx], 7 ; test bit at row (bx)\n jnc noswitch ; see if we need to switch colors\n xchg ah, al ; switch the colors\n noswitch:\n stosw ; paint the color\n dec byte [pixel_check + 3] ; next pixel\n loop row\n\n ; Next column\n mov byte [pixel_check + 3], 7 ; reset bit test location\n inc bl ; next column (memory)\n add di, 144 ; adjust horizontal screen area\n cmp bl, 9 ; are we done with rows\n jne columns\n\nret\n\nscore:\n ; Most of this routine is some magic from Peter Ferrie\n mov ax, bp\n push ax\n xchg ah,al\n call hex2asc\n pop ax ;<----------;\n hex2asc: ;\n aam 16 ;\n call hex2nib ;\n hex2nib: ;<------; |\n xchg ah,al ; |\n cmp al,0ah ; |\n sbb al,69h ; |\n das ; | Caller\n stosb ; | ^\n mov al,7 ; | |\n stosb ; | |\nret ; -------------1-2-----3\n\n; =========== SPRITE DATA ==========\n; 63 bytes total\n; 9 bytes per sprite\n; Sprite Data structure:\n; 1st byte is color data, first nibble is color 1, and 2nd nibble is color 2\n; Each byte after is a full row of pixels, however, a 1 or 0 does not correspond\n; to a color, it corresponds to if there is a change in color. This makes for a \n; more space optimized drawing routine\nretweet: db 0x82, 0x00, 0x69, 0x93, 0x63, 0x63, 0x64, 0xcb, 0x00\nfbthumb: db 0xf1, 0x06, 0x0a, 0x14, 0x10, 0x30, 0x31, 0xb1, 0xa0\npow: db 0x9e, 0x00, 0x7b, 0x36, 0x17, 0x74, 0x36, 0x6f, 0x00\nheart: db 0x04, 0x00, 0x2d, 0x40, 0xc0, 0xa1, 0x12, 0x0c, 0x00\nplusone: db 0x4f, 0x00, 0x03, 0x35, 0x4b, 0x33, 0x03, 0x00, 0x00\nigheart: db 0xfd, 0x41, 0xbf, 0x27, 0x1b, 0x00, 0xc1, 0x14, 0x00\nbus: db 0x6e, 0x00, 0x18, 0x30, 0x60, 0x00, 0x21, 0x41, 0x36\n\n;BIOS sig and padding\ntimes 510-($-$$) db 0\ndw 0xAA55\n", | |
"help": "" | |
}, | |
"bootrogue": { | |
"link": "https://github.com/nanochess/bootRogue", | |
"description": "A Rogue-like dungeon crawler RPG with HP, enemies, traps, food (increases HP), swords (increases attack), and armor (increases defense). The goal is to go through 26 randomly procedurally generated dungeons and retreive the amulet of yendor, you then must come back up through another 26 dungeons. If it's any boot sector game to try, this is definitely the one to try.", | |
"asm": " ;\n ; bootRogue game in 512 bytes (boot sector)\n ;\n ; by Oscar Toledo G.\n ; http://nanochess.org/\n ;\n ; (c) Copyright 2019 Oscar Toledo G.\n ;\n ; Creation date: Sep/19/2019. Generates room boxes.\n ; Revision date: Sep/20/2019. Connect rooms. Allows to navigate.\n ; Revision date: Sep/21/2019. Added ladders to go down/up. Shows\n ; Amulet of Yendor at level 26. Added\n ; circle of light.\n ; Revision date: Sep/22/2019. Creates monsters and items. Now has\n ; hp/exp. Food, armor, weapon, and traps\n ; works. Added battles. 829 bytes.\n ; Revision date: Sep/23/2019. Lots of optimization. 643 bytes.\n ; Revision date: Sep/24/2019. Again lots of optimization. 596 bytes.\n ; Revision date: Sep/25/2019. Many optimizations. 553 bytes.\n ; Revision date: Sep/26/2019. The final effort. 510 bytes.\n ; Revision date: Sep/27/2019. The COM file exits to DOS instead of halting.\n ;\n\n CPU 8086\n\nROW_WIDTH: EQU 0x00A0 ; Width in bytes of each video row\nBOX_MAX_WIDTH: EQU 23 ; Max width of a room box\nBOX_MAX_HEIGHT: EQU 6 ; Max height of a room box\nBOX_WIDTH: EQU 26 ; Width of box area in screen\nBOX_HEIGHT: EQU 8 ; Height of box area in screen\n\n ; See page 45 of my book\nLIGHT_COLOR: EQU 0x06 ; Light color (brown, dark yellow on emu) \nHERO_COLOR: EQU 0x0e ; Hero color (yellow)\n\n ; See page 179 of my book\nGR_VERT: EQU 0xba ; Vertical line graphic\nGR_TOP_RIGHT: EQU 0xbb ; Top right graphic\nGR_BOT_RIGHT: EQU 0xbc ; Bottom right graphic\nGR_BOT_LEFT: EQU 0xc8 ; Bottom left graphic\nGR_TOP_LEFT: EQU 0xc9 ; Top left graphic\nGR_HORIZ: EQU 0xcd ; Horizontal line graphic\n\nGR_TUNNEL: EQU 0xb1 ; Tunnel graphic (shaded block)\nGR_DOOR: EQU 0xce ; Door graphic (crosshair graphic)\nGR_FLOOR: EQU 0xfa ; Floor graphic (middle point)\n\nGR_HERO: EQU 0x01 ; Hero graphic (smiling face)\n\nGR_LADDER: EQU 0xf0 ; Ladder graphic \nGR_TRAP: EQU 0x04 ; Trap graphic (diamond)\nGR_FOOD: EQU 0x05 ; Food graphic (clover)\nGR_ARMOR: EQU 0x08 ; Armor graphic (square with hole in center)\nGR_YENDOR: EQU 0x0c ; Amulet of Yendor graphic (Female sign)\nGR_GOLD: EQU 0x0f ; Gold graphic (asterisk, like brightness)\nGR_WEAPON: EQU 0x18 ; Weapon graphic (up arrow)\n\nYENDOR_LEVEL: EQU 26 ; Level of appearance for Amulet of Yendor\n\n %ifdef com_file\n org 0x0100\n %else\n org 0x7c00\n %endif\n\n ;\n ; Sorted by order of PUSH instructions\n ;\nrnd: equ 0x0008 ; Random seed (used 4 times)\nstarve: equ 0x0006 ; Starve counter (used once)\nhp: equ 0x0004 ; Current HP (used 2 times)\nlevel: equ 0x0003 ; Current level (starting at 0x01) (used 3 times)\nyendor: equ 0x0002 ; 0x01 = Not found. 0xff = Found. (Used 2 times)\narmor: equ 0x0001 ; Armor level (used 2 times)\nweapon: equ 0x0000 ; Weapon level (used 2 times)\n\n ;\n ; Start of the adventure!\n ;\nstart:\n in ax,0x40 ; Read timer counter\n push ax ; Setup pseudorandom number generator\n mov ax,16\n push ax ; starve\n push ax ; hp\n mov al,1\n push ax ; yendor (low byte) + level (high byte)\n push ax ; weapon (low byte) + armor (high byte)\n inc ax ; ax = 0x0002 (it was 0x0001)\n int 0x10\n mov ax,0xb800 ; Text video segment\n mov ds,ax\n mov es,ax\n\n mov si,random ; SI as a space saver for CALL\n\n mov bp,sp ; Using BP because it implies SS and vars are on stack.\n\ngenerate_dungeon:\n\n ;\n ; Advance to next level (can go deeper or higher)\n ;\n mov bl,[bp+yendor]\n add [bp+level],bl\n %ifdef com_file\n jne .0\n jmp quit ; Stop if level zero is reached\n.0:\n %else\n je $ ; Stop if level zero is reached\n %endif\n\n ;\n ; Select a maze for the dungeon\n ;\n ; There are many combinations of values that generate at least\n ; 16 mazes in order to avoid a table.\n ;\n mov ax,[bp+rnd] \n and ax,0x4182\n or ax,0x1a6d\n xchg ax,dx\n\n ;\n ; Clean the screen to black over black (it hides the maze)\n ;\n xor ax,ax\n xor di,di\n mov ch,0x08\n rep stosw\n\n ;\n ; Draw the nine rooms\n ;\n.7:\n push ax\n call fill_room\n pop ax\n add ax,BOX_WIDTH*2\n cmp al,0x9c ; Finished drawing three rooms?\n jne .6 ; No, jump\n ; Yes, go to following row\n add ax,ROW_WIDTH*BOX_HEIGHT-BOX_WIDTH*3*2\n.6:\n cmp ax,ROW_WIDTH*BOX_HEIGHT*3\n jb .7\n\n ;\n ; Put the ladder at a random corner room\n ;\n shl word [bp+rnd],1\n mov ax,3*ROW_WIDTH+12*2 \n mov bx,19*ROW_WIDTH+12*2 \n jnc .2\n xchg ax,bx\n.2: jns .8\n add ax,BOX_WIDTH*2*2\n.8:\n xchg ax,di\n\n mov byte [di],GR_LADDER\n\n ;\n ; If a deep level has been reached then put the Amulet of Yendor\n ;\n cmp byte [bp+level],YENDOR_LEVEL\n jb .1\n mov byte [bx],GR_YENDOR\n.1:\n ;\n ; Setup hero start\n ;\n mov di,11*ROW_WIDTH+38*2\n ;\n ; Main game loop\n ;\ngame_loop:\n mov ax,game_loop ; Force to repeat, the whole loop...\n push ax ; ...ends with ret.\n\n ;\n ; Circle of light around the player (3x3)\n ;\n mov bx,0x0005 ; BX values\n.1: dec bx\n dec bx ; -1 1 3 -0x00a0\n mov al,LIGHT_COLOR \n mov [bx+di-ROW_WIDTH],al ; -1(1)3 \n mov [bx+di],al \n mov [bx+di+ROW_WIDTH],al ; -1 1 3 +0x00a0\n jns .1\n\n ;\n ; Show our hero\n ;\n push word [di] ; Save character and attribute under \n mov word [di],HERO_COLOR*256+GR_HERO\n add byte [bp+starve],2 ; Cannot use INC because it needs Carry set\n sbb ax,ax ; HP down 1 every 128 steps\n call add_hp ; Update stats\n ; mov ah,0x00 ; Comes here with ah = 0\n int 0x16 ; Read keyboard\n pop word [di] ; Restore character and attribute under \n\n mov al,ah\n %ifdef com_file\n cmp al,0x01\n je quit ; Exit if Esc key is pressed\n %endif\n\n sub al,0x4c\n mov ah,0x02 ; Left/right multiplies by 2\n cmp al,0xff ; Going left (scancode 0x4b)\n je .2\n cmp al,0x01 ; Going right (scancode 0x4d)\n je .2\n cmp al,0xfc ; Going up (scancode 0x48)\n je .3\n cmp al,0x04 ; Going down (scancode 0x50)\n jne move_cancel\n.3:\n mov ah,0x28 ; Up/down multiplies by 40\n.2:\n imul ah ; Signed multiplication\n\n xchg ax,bx ; bx = displacement offset\n mov al,[di+bx] ; Read the target contents\n ;\n ; All the things that can exist on screen start with GR_* (17 things)\n ; So no need to account for all cases.\n ; We won't ever find GR_HERO so there are 16 things to look for.\n ;\n cmp al,GR_LADDER ; GR_LADDER?\n je ladder_found\n ; 15 things to look for (plus zero and monsters)\n ; Anything > GR_TUNNEL and < GR_DOOR is a wall \n cmp al,GR_DOOR ; GR_DOOR?\n jnc .4\n cmp al,GR_TUNNEL ; GR_TUNNEL?\n ja move_cancel\n.4:\n ; 9 things to look for (plus zero and monsters)\n cmp al,GR_TRAP ; GR_TRAP?\n jb move_cancel ; < it must be blank, cancel movement\n ; Move player\n lea di,[di+bx] ; Do move.\n mov bh,0x06 ; Random range for GR_FOOD and GR_TRAP\n je trap_found ; = Yes, went over trap\n ; 8 things to look for (plus monsters)\n cmp al,GR_TUNNEL ; GR_TUNNEL+GR_DOOR+GR_FLOOR ?\n jnc move_cancel ; Yes, jump.\n cmp al,GR_WEAPON ; GR_WEAPON?\n ja battle ; > it's a monster, so go to battle\n ; Only items at this part of code, so clean floor\n mov byte [di],GR_FLOOR ; Delete item from floor\n je weapon_found ; = weapon found\n ; 4 things to look for\n cmp al,GR_ARMOR ; GR_ARMOR?\n je armor_found ; Yes, increase armor\n jb food_found ; < it's GR_FOOD, increase hp\n ; 2 things to look for\n cmp al,GR_GOLD ; GR_GOLD?\n je move_cancel ; Yes, simply take it.\n ; At this point 'al' can only be GR_YENDOR\n ; Amulet of Yendor found!\n neg byte [bp+yendor] ; Now player goes upwards over ladders.\nmove_cancel:\n ret ; Return to main loop.\n\n %ifdef com_file\nquit:\n int 0x20\n %endif\n\n ;\n ; I--\n ; I--\n ; I--\n ;\nladder_found:\n jmp generate_dungeon\n\n ; ______\n ; I I\n ; I #X I\n ; I X# I\n ; \\__/\n ; \narmor_found:\n inc byte [bp+armor] ; Increase armor level\n ret\n\n ;\n ; /| _____________\n ; (|===|oo>_____________>\n ; \\|\n ;\nweapon_found:\n inc byte [bp+weapon] ; Increase weapon level\n ret\n\n ;\n ; Aaaarghhhh!\n ;\ntrap_found:\n call si ; Random 1-6\nsub_hp: neg ax ; Make it negative\n db 0xbb ; MOV BX to jump two bytes\n ;\n ; /--\\\n ; ==== I\n ; \\--/\n ;\nfood_found:\n call si ; Random 1-6\n\nadd_hp: add ax,[bp+hp] ; Add to current HP\n %ifdef com_file\n js quit ; Exit if Esc key is pressed\n %else\n js $ ; Stall if dead\n %endif\n mov [bp+hp],ax ; Update HP.\n ;\n ; Update screen indicator\n ;\n mov bx,0x0f98 ; Point to bottom right corner\n call .1\n %ifdef com_file\n mov al,[bp+weapon]\n call .1\n mov al,[bp+armor]\n call .1\n %endif\n mov al,[bp+level]\n.1:\n xor cx,cx ; CX = Quotient\n.2: inc cx\n sub ax,10 ; Division by subtraction\n jnc .2\n add ax,0x0a3a ; Convert remainder to ASCII digit + color\n call .3 ; Put on screen\n xchg ax,cx\n dec ax ; Quotient is zero?\n jnz .1 ; No, jump to show more digits.\n\n.3: mov [bx],ax\n dec bx\n dec bx\n ret\n\n ;\n ; Let's battle!!!\n ;\nbattle:\n and al,0x1f ; Separate number of monster (1-26) \n shl al,1 ; Make it slightly harder\n mov ah,al ; Use also as its HP\n xchg ax,dx ; Its attack is equivalent to its number\n ; Player's attack\n.2:\n mov bh,[bp+weapon] ; Use current weapon level as dice\n call si\n sub dh,al ; Subtract from monster's HP\n jc .3 ; Killed? yes, jump\n ; Monster's attack\n mov bh,dl ; Use monster number as dice\n call si \n sub al,[bp+armor] ; Subtract armor from attack \n jc .4\n call sub_hp ; Subtract from player's HP\n.4:\n ; mov ah,0x00 ; Comes here with ah = 0\n int 0x16 ; Wait for a key.\n jmp .2 ; Another battle round.\n\n ;\n ; Monster is dead\n ;\n.3:\n mov byte [di],GR_FLOOR ; Remove from screen\n ret\n\n ;\n ; Fill a room\n ;\nfill_room:\n add ax,(BOX_HEIGHT/2-1)*ROW_WIDTH+(BOX_WIDTH/2)*2\n push ax\n xchg ax,di \n shr dx,1 ; Obtain bit of right connection\n mov ax,0x0000+GR_TUNNEL\n mov cx,BOX_WIDTH\n jnc .3\n push di\n rep stosw ; Horizontal tunnel\n pop di\n.3:\n shr dx,1 ; Obtain bit of down connection\n jnc .5\n mov cl,BOX_HEIGHT\n.4:\n stosb ; Vertical tunnel\n add di,ROW_WIDTH-1\n loop .4\n.5: \n mov bh,BOX_MAX_WIDTH-2 \n call si ; Get a random width for room.\n xchg ax,cx\n mov bh,BOX_MAX_HEIGHT-2\n call si ; Get a random height for room.\n mov ch,al\n shr al,1 ;\n inc ax\n mov ah,ROW_WIDTH\n mul ah\n add ax,cx ; Now it has a centering offset\n sub ah,ch ; Better than \"mov bx,cx mov bh,0\"\n and al,0xfe\n add al,0x04\n pop di\n sub di,ax ; Subtract from room center\n mov al,GR_TOP_LEFT ; Draw top row of room\n mov bx,GR_TOP_RIGHT*256+GR_HORIZ\n call fill\n.9:\n mov al,GR_VERT ; Draw intermediate row of room\n mov bx,GR_VERT*256+GR_FLOOR \n call fill\n dec ch\n jns .9\n mov al,GR_BOT_LEFT ; Draw bottom row of room\n mov bx,GR_BOT_RIGHT*256+GR_HORIZ\n\n ;\n ; Fill a row on screen for a room\n ;\nfill: push cx ; Save CX because it needs CL value again\n push di ; Save video position\n call door ; Left border\n.1: mov al,bl ; Filler\n call door\n dec cl\n jns .1\n mov al,bh ; Right border\n call door\n pop di ; Restore video position\n pop cx ; Restore CX \n add di,0x00a0 ; Goes to next row on screen\n ret\n\n ;\n ; Draw a room character on screen\n ;\ndoor:\n cmp al,GR_FLOOR ; Drawing floor?\n jne .3 ; No, jump\n call si ; Get a random number (BH value is GR_VERT)\n cmp al,6 ; Chance of creating a monster\n jnc .11\n add al,[bp+level] ; More difficult monsters as level is deeper\n.9:\n sub al,0x05 \n cmp al,0x17 ; Make sure it fits inside ASCII letters\n jge .9\n add al,0x44 ; Offset into ASCII letters\n jmp short .12\n\n.11:\n cmp al,11 ; Chance of creating an item\n xchg ax,bx\n cs mov bl,[si+bx+(items-random-6)]\n xchg ax,bx\n jb .12\n mov al,GR_FLOOR ; Show only floor.\n.12: \n.3:\n cmp al,GR_HORIZ\n je .1\n cmp al,GR_VERT\n jne .2\n.1: cmp byte [di],GR_TUNNEL\n jne .2\n mov al,GR_DOOR\n.2: stosb\n inc di\n ret\n\nrandom:\n mov al,251\n mul byte [bp+rnd]\n add al,83\n mov [bp+rnd],ax\n \n; rdtsc ; Would make it dependent on Pentium II\n\n; in al,(0x40) ; Only works for slow requirements.\n\n xor ah,ah\n div bh\n mov al,ah\n cbw\n inc ax\n ret\n\n ;\n ; Items\n ;\nitems:\n db GR_FOOD\n db GR_GOLD\n db GR_TRAP\n db GR_WEAPON\n db GR_ARMOR\n\n %ifdef com_file\n %else\n times 510-($-$$) db 0x4f\n db 0x55,0xaa ; Make it a bootable sector\n %endif\n", | |
"data": "5UBQuBAAUFCwAVBQQM0QuAC4jtiOwL7lfYnlil4CAF4DdP6LRgglgkENbRqSMcAx/7UI86tQ6AcBWIPANDycdQMFZAQ9AA9y7NFmCLj4Abv4C3MBk3kDg8Bol8YF8IB+AxpyA8YHDL8sB7hqfFC7BQBLS7AGiIFg/4gBiIGgAHnw/zXHBQEOgEYGAhnA6GQAzRaPBYjgLEy0Ajz/dA48AXQKPPx0BDwEdTe0KPbsk4oBPPB0LTzOcwQ8sXckPARyII05twZ0JjyxcxY8GHdNxgX6dBU8CHQNchg8D3QD9l4Cw+k2//5GAcP+RgDD/9b32Lv/1gNGBHj+iUYEu5gP6AMAikYDMclBg+gKc/oFOgroBACRSHXuiQdLS8MkH9DgiMSSin4A/9YoxnIQiNf/1ipGAXID6Lf/zRbr58YF+sMF+gFQl9HquLEAuRoAcwRX86tf0epzCbEIqoHHnwDi+bcV/9aRtwT/1ojF0OhAtKD25AHIKOwk/gQEXynHsMm7zbvoEQCwurv6uugJAP7NefSwyLvNvFFX6BUAiNjoEAD+yXn3iPjoBwBfWYHHoADDPPp1H//WPAZzDQJGAywFPBd9+gRE6ww8C5MuilgNk3ICsPo8zXQEPLp1B4A9sXUCsM6qR8Ow+/ZmCARTiUYIMOT294jgmEDDBQ8EGAhPVao=", | |
"help": "" | |
}, | |
"Sorry Ass": { | |
"link": "https://www.pouet.net/prod.php?which=67997", | |
"description": "A racing game with a very clever use of code-437 graphics", | |
"asm": "", | |
"data": "", | |
"help": "idk didnt find more" | |
}, | |
"bricks": { | |
"link": "https://github.com/nanochess/bricks", | |
"description": "An Arkanoid/Breakout clone", | |
"asm": "\t;\n\t; Bricks game in one boot sector\n\t;\n\t; by Oscar Toledo G.\n\t;\n\t; Creation date: Nov/02/2019.\n\t;\n\n\tcpu 8086\n\n\t;\n\t; Press Left Shift to start the game\n\t; Press Left Ctrl to move the paddle to the left\n\t; Press Left Alt to move the paddle to the right\n\t;\n\n %ifdef com_file\n org 0x0100\n %else\n org 0x7c00\n %endif\n\nold_time:\tequ 16\t; Old time \nball_x:\t\tequ 14\t; X-coordinate of ball (8.8 fraction)\nball_y:\t\tequ 12\t; Y-coordinate of ball (8.8 fraction)\nball_xs:\tequ 10\t; X-speed of ball (8.8 fraction)\nball_ys:\tequ 8\t; Y-speed of ball (8.8 fraction)\nbeep:\t\tequ 6\t; Frame count to turn off sound\nbricks:\t\tequ 4\t; Remaining bricks\nballs: equ 2\t; Remaining balls\nscore: equ 0\t; Current score\n\n\t;\n\t; Start of the game\n\t;\nstart:\n\tmov ax,0x0002\t\t; Text mode 80x25x16 colors\n\tint 0x10\t\t; Setup\n\tmov ax,0xb800\t\t; Address of video screen\n\tmov ds,ax\t\t; Setup DS\n\tmov es,ax\t\t; Setup ES\n\tsub sp,32\n\txor ax,ax\n\tpush ax\t\t\t; Reset score\n\tmov al,4\t\t\n\tpush ax\t\t\t; Balls remaining\n\tmov bp,sp\t\t; Setup stack frame for globals\n\t;\n\t; Start another level \n\t;\nanother_level:\n\tmov word [bp+bricks],273\t; 273 bricks on screen\n\txor di,di\n\tmov ax,0x01b1\t\t; Draw top border\n\tmov cx,80\n\tcld\n\trep stosw\n\tmov cl,24\t\t; 24 rows\n.1:\n\tstosw\t\t\t; Draw left border\n\tmov ax,0x20\t\t; No bricks on this row\n\tpush cx\n\tcmp cl,23\n\tjnb .2\n\tsub cl,15\n\tjbe .2\n\tmov al,0xdb\t\t; Bricks on this row\n\tmov ah,cl\n.2:\n\tmov cl,39\t\t; 39 bricks per row\n.3:\n\tstosw\n\tstosw\n\tinc ah\t\t\t; Increase attribute color\n\tcmp ah,0x08\n\tjne .4\n\tmov ah,0x01\n.4:\n\tloop .3\n\tpop cx\n\n\tmov ax,0x01b1\t\t; Draw right border\n\tstosw\n\tloop .1\n\n\t;\n\t; Start another ball\n\t;\n\tmov di,0x0f4a\t\t; Position of paddle\nanother_ball:\n\tmov byte [bp+ball_x+1],0x28\t; Center X\n\tmov byte [bp+ball_y+1],0x14\t; Center Y\n\txor ax,ax\n\tmov [bp+ball_xs],ax\t; Static on screen\n\tmov [bp+ball_ys],ax\n\tmov byte [bp+beep],0x01\n\n\tmov si,0x0ffe\t\t; Don't erase ball yet\ngame_loop:\n\tcall wait_frame\t\t; Wait 1/18.2 secs.\n\n\tmov word [si],0x0000\t; Erase ball\n\t\n\tcall update_score\t; Update score\n\t\n\tmov ah,0x02\t\t; Read modifier keys\n\tint 0x16\n\ttest al,0x04\t\t; Left ctrl\n\tje .1\n\tmov byte [di+6],0\t; Erase right side of paddle\n\tmov byte [di+8],0\n\tsub di,byte 4\t\t; Move paddle to left\n\tcmp di,0x0f02\t\t; Limit\n\tja .1\n\tmov di,0x0f02\n.1:\n\ttest al,0x08\t\t; Left alt\n\tje .2\n\txor ax,ax\t\t; Erase left side of paddle\n\tstosw\n\tstosw\t\t\t; DI increased automatically\n\tcmp di,0x0f94\t\t; Limit\n\tjb .2\n\tmov di,0x0f94\t\n.2:\n\ttest al,0x02\t\t; Left shift\n\tje .15\n\tmov ax,[bp+ball_xs]\t; Ball moving?\n\tadd ax,[bp+ball_ys]\n\tjne .15\t\t\t; Yes, jump\n\t\t\t\t; Setup movement of ball\n\tmov word [bp+ball_xs],0xff40\n\tmov word [bp+ball_ys],0xff80\n.15:\n\tmov ax,0x0adf\t\t; Paddle graphic and color\n\tpush di\n\tstosw\t\t\t; Draw paddle\n\tstosw\n\tstosw\n\tstosw\n\tstosw\n\tpop di\n\n\tmov bx,[bp+ball_x]\t\t; Draw ball\n\tmov ax,[bp+ball_y]\n\tcall locate_ball\t; Locate on screen\n\ttest byte [bp+ball_y],0x80\t; Y-coordinate half fraction?\n\tmov ah,0x60\t\t; Interchange colors for smooth mov.\n\tje .12\n\tmov ah,0x06\n.12:\tmov al,0xdc\t\t; Graphic\n\tmov [bx],ax\t\t; Draw\n\tpush bx\n\tpop si\n\n.14:\n\tmov bx,[bp+ball_x]\t\t; Ball position\n\tmov ax,[bp+ball_y]\n\tadd bx,[bp+ball_xs]\t; Add movement speed\n\tadd ax,[bp+ball_ys]\n\tpush ax\n\tpush bx\n\tcall locate_ball\t; Locate on screen\n\tmov al,[bx]\n\tcmp al,0xb1\t\t; Touching borders\n\tjne .3\n\tmov cx,5423\t\t; 1193180 / 220\n\tcall speaker\t\t; Generate sound\n\tpop bx\n\tpop ax\n\tcmp bh,0x4f\n\tje .8\n\ttest bh,bh\n\tjne .7\n.8:\n\tneg word [bp+ball_xs]\t; Negate X-speed if it touches a side\n.7:\t\n\ttest ah,ah\n\tjnz .9\n\tneg word [bp+ball_ys]\t; Negate Y-speed if it touches a side\n.9:\tjmp .14\n\n.3:\n\tcmp al,0xdf\t\t; Touching paddle\n\tjne .4\n\tsub bx,di\t\t; Subtract paddle position\n\tsub bx,byte 4\n\tmov cl,6\t\t; Multiply by 64\n\tshl bx,cl\n\tmov [bp+ball_xs],bx\t; New X speed for ball\n\tmov word [bp+ball_ys],0xff80\t; Update Y speed for ball\n\tmov cx,2711\t\t; 1193180 / 440\n\tcall speaker\t\t; Generate sound\n\tpop bx\n\tpop ax\n\tjmp .14\n\n.4:\n\tcmp al,0xdb\t\t; Touching brick\n\tjne .5\n\tmov cx,1355\t\t; 1193180 / 880\n\tcall speaker\t\t; Generate sound\n\ttest bl,2\t\t; Aligned with brick?\n\tjne .10\t\t\t; Yes, jump\n\tdec bx\t\t\t; Align\n\tdec bx\n.10:\txor ax,ax\t\t; Erase brick\n\tmov [bx],ax\n\tmov [bx+2],ax\n\tinc word [bp+score]\t; Increase score\n\tneg word [bp+ball_ys]\t; Negate Y speed (rebound)\n\tpop bx\n\tpop ax\n\tdec word [bp+bricks]\t; One brick less on screen\n\tjne .14\t\t\t; Fully completed? No, jump.\n\tjmp another_level\t; Start another level\n\n.5:\n\tpop bx\n\tpop ax\n.6:\n\tmov [bp+ball_x],bx\t\t; Update ball position\n\tmov [bp+ball_y],ax\n\tcmp ah,0x19\t\t; Ball exited through bottom?\n\tje ball_lost\t\t; Yes, jump\n\tjmp game_loop\t\t; No, repeat game loop\n\n\t;\n\t; Ball lost\n\t; \nball_lost:\n\tmov cx,10846\t\t; 1193180 / 110\n\tcall speaker\t\t; Generate sound\n\n\tmov word [si],0\t\t; Erase ball\n\tdec byte [bp+balls]\t; One ball less\n\tjs .1\t\t\t; All finished? Yes, jump\n\tjmp another_ball\t; Start another ball\n\n.1:\tcall wait_frame.2\t; Turn off sound\n\tint 0x20\t\t; Exit to DOS / bootOS\n\nwait_frame:\n.0:\n\tmov ah,0x00\t\t; Read ticks\n\tint 0x1a\t\t; Call BIOS\n\tcmp dx,[bp+old_time]\t; Wait for change\n\tje .0\n\tmov [bp+old_time],dx\n\n\tdec byte [bp+beep]\t\t; Decrease time to turn off beep\n\tjne .1\n.2:\n\tin al,0x61\n\tand al,0xfc\t\t; Turn off\n\tout 0x61,al\n.1:\n\n\tret\n\n\t;\n\t; Generate sound on PC speaker\n\t;\nspeaker:\n\tmov al,0xb6\t\t; Setup timer 2\n\tout 0x43,al\n\tmov al,cl\t\t; Low byte of timer count\n\tout 0x42,al\n\tmov al,ch\t\t; High byte of timer count\n\tout 0x42,al\n\tin al,0x61\n\tor al,0x03\t\t; Connect PC speaker to timer 2\n\tout 0x61,al\n\tmov byte [bp+beep],3\t; Duration\n\tret\n\n\t;\n\t; Locate ball on screen\n\t;\nlocate_ball:\n\tmov al,0xa0\n\tmul ah\t\t\t; AH = Y coordinate (row)\n\tmov bl,bh\t\t; BH = X coordinate (column)\n\tmov bh,0\n\tshl bx,1\n\tadd bx,ax\n\tret\n\n\t;\n\t; Update score indicator (from bootRogue)\n\t;\nupdate_score:\n\tmov bx,0x0f98\t\t; Point to bottom right corner\n\tmov ax,[bp+score]\n\tcall .1\n\tmov al,[bp+balls]\n.1:\n\txor cx,cx ; CX = Quotient\n.2:\tinc cx\n\tsub ax,10 ; Division by subtraction\n\tjnc .2\n\tadd ax,0x0a3a ; Convert remainder to ASCII digit + color\n\tcall .3 ; Put on screen\n\txchg ax,cx\n\tdec ax ; Quotient is zero?\n\tjnz .1 ; No, jump to show more digits.\n\n.3:\tmov [bx],ax\n\tdec bx\n\tdec bx\n\tret\n\n %ifdef com_file\n %else\n\ttimes 510-($-$$) db 0x4f\n\tdb 0x55,0xaa ; Make it a bootable sector\n %endif\n", | |
"data": "uAIAzRC4ALiO2I7Ag+wgMcBQsARQieXHRgQRATH/uLEBuVAA/POrsRiruCAAUYD5F3MJgOkPdgSw24jMsSerq/7EgPwIdQK0AeLzWbixAavi179KD8ZGDyjGRg0UMcCJRgqJRgjGRgYBvv4P6A8BxwQAAOhEAbQCzRaoBHQUxkUGAMZFCACD7wSB/wIPdwO/Ag+oCHQNMcCrq4H/lA9yA7+UD6gCdBKLRgoDRgh1CsdGCkD/x0YIgP+43wpXq6urq6tfi14Oi0YM6OEA9kYMgLRgdAK0BrDciQdTXoteDotGDANeCgNGCFBT6MAAigc8sXUduS8V6J0AW1iA/090BIT/dQP3XgqE5HUD914I68w833UbKfuD6wSxBtPjiV4Kx0YIgP+5lwroawBbWOutPNt1JLlLBehdAPbDAnUCS0sxwIkHiUcC/0YA914IW1j/TgR1iOnA/ltYiV4OiUYMgPwZdAPpBf+5XiroKQDHBAAA/k4CeAPp3P7oEwDNILQAzRo7VhB094lWEP5OBnUG5GEk/OZhw7C25kOIyOZCiOjmQuRhDAPmYcZGBgPDsKD25Ij7twDR4wHDw7uYD4tGAOgDAIpGAjHJQYPoCnP6BToK6AQAkUh17okHS0vDT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVao=", | |
"help": "" | |
}, | |
"cubicDoom": { | |
"link": "https://github.com/nanochess/cubicDoom", | |
"description": "A 3d ray-casting shooter. Shoot all of the cubes in the room before they touch and kill you to get to the next level. One of the most impressive boot sector games from a graphical standpoint", | |
"asm": " ;\n ; CubicDoom\n ;\n ; by Oscar Toledo G.\n ;\n ; Creation date: Nov/21/2019.\n ; Revision date: Nov/22/2019. Now working.\n ; Revision date: Nov/23/2019. Optimized.\n ; Revision date: Nov/24/2019. Builds a world. Added evil cubes, and\n ; can shoot them. 517 bytes.\n ; Revision date: Nov/25/2019. Optimized last bytes. 509 bytes.\n ; Revision date: Nov/26/2019. Smaller extract. 508 bytes\n ; (Peter Ferrie).\n ;\n\n ;\n ; Tricks used:\n ; o \"Slow\" ray-casting so doesn't matter if hits horizontal or\n ; vertical wall.\n ;\n\n cpu 8086\n\nEMPTY: equ 0x00 ; Code for empty space\nWALL: equ 0x80 ; Code for wall\nENEMY: equ 0xc0 ; Code for enemy, includes shot count\n\n %ifdef com_file\n org 0x0100\n %else\n org 0x7c00\n %endif\n\ndown: equ 0x000b ; Enemies down\nshot: equ 0x000a ; Shot made\nrnd: equ 0x0008 ; Random number\npx: equ 0x0006 ; Current X position (4.12)\npy: equ 0x0004 ; Current Y position (4.12)\npa: equ 0x0002 ; Current screen angle\noldtim: equ 0x0000 ; Old time\n\nmaze: equ 0xff00 ; Location of maze (16x16)\n\n ;\n ; Start of the game\n ;\nstart:\n mov ax,0x0013 ; Graphics mode 320x200x256 colors\n int 0x10 ; Setup video mode\n mov ax,0xa000 ; Point to video memory.\n mov ds,ax\n mov es,ax\nrestart:\n cld\n xor cx,cx\n push cx ; shot+down\n in ax,0x40\n push ax ; rnd\n mov ah,0x18 ; Start point at maze\n push ax ; px\n push ax ; py\n mov cl,0x04\n push cx ; pa\n push cx ; oldtim\n mov bp,sp ; Setup BP to access variables\n\n mov bx,maze ; Point to maze\n.0: mov al,bl\n add al,0x11 ; Right and bottom borders at zero\n cmp al,0x22 ; Inside any border?\n jb .5 ; Yes, jump\n and al,0x0e ; Inside left/right border?\n mov al,EMPTY\n jne .4 ; No, jump\n.5: mov al,WALL\n.4: mov [bx],al ; Put into maze\n inc bx ; Next square\n jne .0 ; If BX is zero, maze completed\n \n mov cl,12 ; 12 walls and enemies\n mov [bp+down],cl ; Take note of enemies down\n mov di,maze+34 ; Point to center of maze\n mov dl,12 ; Modulo 12 for random number\n.2:\n call random\n mov byte [di+bx],WALL ; Setup a wall\n call random\n mov byte [di+bx],ENEMY ; Setup an enemy\n add di,byte 16 ; Go to next row of maze\n loop .2 ; Repeat until filled\ngame_loop:\n call wait_frame ; Wait a frame\n\n and dl,31 ; 32 frames have passed?\n jnz .16 ; No, jump\n ;\n ; Move cubes\n ;\n call get_dir ; Get player position, also SI=0\n call get_pos ; Convert position to maze address\n mov cx,bx ; Save into CX\n\n mov bl,0 ; BH already ready, start at corner\n\n.17: cmp byte [bx],ENEMY\n jb .18\n cmp bx,cx ; Cube over player?\n jne .25 ; No, jump\n ;\n ; Handle death\n ;\n.22:\n mov byte [si],0x0c ; Blood pixel\n add si,byte 23 ; Advance by prime number\n.23:\n je restart ; Zero = full loop, restart game.\n jnb .22 ; Carry = one fill complete.\n push si\n call wait_frame ; Wait a frame (for fast machines)\n pop si\n jmp .22 ; Continue\n\n.25:\n mov di,bx\n mov al,bl\n mov ah,cl\n mov dx,0x0f0f ; Extract columns\n and dx,ax\n xor ax,dx ; Extract rows\n cmp ah,al ; Same row?\n je .19 ; Yes, jump\n lea di,[bx+0x10] ; Cube moves down\n jnb .19\n lea di,[bx-0x10] ; Cube moves up\n.19: cmp dh,dl ; Same column?\n je .20 ; Yes, jump\n dec di ; Cube goes left\n jb .20\n inc di ; Cube goes right\n inc di\n.20: cmp byte [di],0 ; Can move?\n jne .18 ; No, jump.\n mov al,[bx] ; Take cube\n mov byte [bx],0 ; Erase origin\n stosb ; Put into new place\n.18:\n inc bx ; Continue searching the maze...\n jne .17 ; ...until the end\n\n.16:\n\n ;\n ; Draw 3D view\n ;\n mov di,39 ; Column number is 39\n.2:\n lea ax,[di-20] ; Almost 60 degrees to the left\n add ax,[bp+pa] ; Get vision angle\n call get_dir ; Get position and direction\n.3:\n call read_maze ; Verify wall hit\n jnc .3 ; Continue if it was open space\n\n.4:\n mov cx,0x1204 ; Add grayscale color set...\n ; ...also load CL with 4. (division by 16)\n jz .24 ; Jump if normal wall\n mov ch,32 ; Rainbow\n\n cmp di,byte 20\n jne .24 ; Jump if not at center\n cmp byte [bp+shot],1\n je .24 ; Jump if not shooting\n call get_pos\n inc byte [bx] ; Increase cube hits\n cmp byte [bx],ENEMY+3 ; 3 hits?\n jne .24 ; No, jump\n mov byte [bx],0 ; Yes, remove.\n dec byte [bp+down] ; One cube less\n je .23 ; Zero means to get another level\n.24:\n lea ax,[di+12] ; Get cos(-30) to cos(30)\n call get_sin ; Get cos (8 bit fraction)\n mul si ; Correct wall distance to...\n mov bl,ah ; ...avoid fishbowl effect\n mov bh,dl ; Divide by 256\n inc bx ; Avoid zero value\n\n mov ax,0x0800 ; Constant for projection plane\n cwd\n div bx ; Divide\n cmp ax,198 ; Limit to screen height\n jb .14\n mov ax,198\n.14: mov si,ax ; Height of wall\n\n shr ax,cl ; Divide distance by 16\n add al,ch ; Add palette index\n xchg ax,bx ; Put into BX\n\n push di\n dec cx ; CL=3. Multiply column by 8 pixels\n shl di,cl\n\n mov ax,200 ; Height of screen...\n sub ax,si ; ...minus wall height\n shr ax,1 ; Divide by 2\n\n push ax\n push si\n xchg ax,cx\n mov al,[bp+shot] ; Ceiling color\n call fill_column\n xchg ax,bx ; Wall color\n pop cx\n call fill_column\n mov al,0x03 ; Floor color (a la Wolfenstein)\n pop cx\n call fill_column\n pop di\n dec di ; Decrease column\n jns .2 ; Completed? No, jump.\n\n mov ah,0x02 ; Service 0x02 = Read modifier keys\n int 0x16 ; Call BIOS\n\n mov bx,[bp+pa] ; Get current angle\n test al,0x04 ; Left Ctrl pressed?\n je .8\n dec bx ; Decrease angle\n dec bx\n.8:\n test al,0x08 ; Left Alt pressed?\n je .9\n inc bx ; Increase angle\n inc bx\n.9:\n mov ah,1 ; No shot\n test al,0x01 ; Right shift pressed?\n je .11\n test bh,0x01 ; But not before?\n jne .11\n mov ah,7 ; Indicate shot\n\n.11: mov [bp+shot],ah\n mov bh,al\n mov [bp+pa],bx ; Update angle\n\n test al,0x02 ; Left shift pressed?\n je .10\n xchg ax,bx ; Put angle into AX\n call get_dir ; Get position and direction\n.5: call read_maze ; Move and check for wall hit\n jc .10 ; Hit, jump without updating position.\n cmp si,byte 4 ; Four times (the speed)\n jne .5\n\n mov [bp+px],dx ; Update X position\n mov [bp+py],bx ; Update Y position\n.10:\n jmp game_loop ; Repeat game loop\n\n ;\n ; Get a direction vector\n ;\nget_dir:\n xor si,si ; Wall distance = 0\n mov dx,[bp+px] ; Get X position\n push ax\n call get_sin ; Get sine\n xchg ax,cx ; Onto DX\n pop ax\n add al,32 ; Add 90 degrees to get cosine\n ;\n ; Get sine\n ;\nget_sin:\n test al,64 ; Angle >= 180 degrees?\n pushf\n test al,32 ; Angle 90-179 or 270-359 degrees?\n je .2\n xor al,31 ; Invert bits (reduces table)\n.2:\n and ax,31 ; Only 90 degrees in table\n mov bx,sin_table\n cs xlat ; Get fraction\n popf\n je .1 ; Jump if angle less than 180\n neg ax ; Else negate result\n.1:\n mov bx,[bp+py] ; Get Y position\n ret\n\n ;\n ; Read maze\n ;\nread_maze:\n inc si ; Count distance to wall\n add dx,cx ; Move X\n add bx,ax ; Move Y\n push bx\n push cx\n call get_pos\n mov bl,[bx] ; Read maze byte\n shl bl,1 ; Carry = 1 = wall, Zero = Wall 0 / 1\n pop cx\n pop bx\n ret ; Return\n\n ;\n ; Convert coordinates to position\n ;\nget_pos: \n mov bl,dh ; X-coordinate \n mov cl,0x04 ; Divide by 4096\n shr bl,cl\n and bh,0xf0 ; Y-coordinate / 4096 * 16\n or bl,bh ; Translate to maze array\n mov bh,maze>>8\n ret\n\n ;\n ; Fill a screen column\n ;\nfill_column:\n mov ah,al ; Duplicate pixel value\n.1:\n stosw ; Draw 2 pixels\n stosw ; Draw 2 pixels\n stosw ; Draw 2 pixels\n stosw ; Draw 2 pixels\n add di,0x0138 ; Go to next row\n loop .1 ; Repeat until fully drawn\n ret ; Return\n\n ;\n ; Generate a pseudo-random number (from bootRogue)\n ;\nrandom:\n mov al,251\n mul byte [bp+rnd]\n add al,83\n mov [bp+rnd],al\n mov ah,0\n div dl\n mov bl,ah\n mov bh,0\n ret\n\n ;\n ; Wait a frame (18.2 hz)\n ;\nwait_frame:\n.1:\n mov ah,0x00 ; Get ticks\n int 0x1a ; Call BIOS time service\n cmp dx,[bp+oldtim] ; Same as old time?\n je .1 ; Yes, wait.\n mov [bp+oldtim],dx\n ret\n\n ;\n ; Sine table (0.8 format)\n ;\n ; 32 bytes are 90 degrees.\n ;\nsin_table:\n\tdb 0x00,0x09,0x16,0x24,0x31,0x3e,0x47,0x53\n\tdb 0x60,0x6c,0x78,0x80,0x8b,0x96,0xa1,0xab\n\tdb 0xb5,0xbb,0xc4,0xcc,0xd4,0xdb,0xe0,0xe6\n db 0xec,0xf1,0xf5,0xf7,0xfa,0xfd,0xff,0xff\n\n %ifdef com_file\n %else\n\ttimes 510-($-$$) db 0x4f\n\tdb 0x55,0xaa ; Make it a bootable sector\n %endif\n", | |
"data": "uBMAzRC4AKCO2I7A/DHJUeVAULQYUFCxBFFRieW7AP+I2AQRPCJyBiQOsAB1ArCAiAdDdeuxDIhOC78i/7IM6HoBxgGA6HQBxgHAg8cQ4u/ofAGA4h91VOgOAehDAYnZswCAP8ByQjnLdRHGBAyDxhd0mXP2VuhWAV7r74nfiNiIzLoPDyHCMdA4xHQIjX8QcwONf/A41nQFT3ICR0eAPQB1BooHxgcAqkN1tr8nAI1F7ANGAuixAOjVAHP7uQQSdB+1IIP/FHUYgH4KAXQS6M8A/geAP8N1CMYHAP5OC3SQjUUM6I8A9+aI44jXQ7gACJn38z3GAHIDuMYAicbT6ADok1dJ0+e4yAAp8NHoUFaRikYK6JgAk1nokwCwA1nojQBfT3mJtALNFoteAqgEdAJLS6gIdAJDQ7QBqAF0B/bHAXUCtAeIZgqIx4leAqgCdBST6BMA6DcAcguD/gR19olWBoleBOnn/jH2i1YGUOgEAJFYBCCoQJyoIHQCNB+D4B+73H0u1510AvfYi14Ew0YBygHDU1HoBwCKH9DjWVvDiPOxBNLrgOfwCPu3/8OIxKurq6uBxzgB4vbDsPv2ZggEU4hGCLQA9vKI47cAw7QAzRo7VgB094lWAMMACRYkMT5HU2BseICLlqGrtbvEzNTb4Obs8fX3+v3//09PVao=", | |
"help": "" | |
}, | |
"lightsout": { | |
"link": "https://github.com/XlogicX/lightsout", | |
"description": "A boot sector clone of an old classic 90's puzzle where you have to turn off all of the lights. Selecting a light will toggle that light and the 4 surrounding lights. This game comes with an 'evil' unsolvable mode (displayed with red lights when it happens).", | |
"asm": ";LightsOut\n\n[ORG 0x7c00]\nLEFT EQU 75\nRIGHT EQU 77\nUP EQU 72\nDOWN EQU 80\nSPACE EQU 0x39\n\n; Init the environment\nxor ax, ax ; make it zero\nmov ds, ax ; DS=0\nmov ss, ax ; stack starts at 0\nmov sp, 0x9c00 ; 200h past code start\nmov ah, 0xb8 ; text video memory\nmov es, ax ; ES=0xB800\nmov al, 0x03\nint 0x10\nmov ah, 1\nmov ch, 0x26 \nint 0x10\n; Fill in all black\nmov cx, 0x07d0 ; whole screens worth\ncbw ; clear ax (black on black with null char)\nxor di, di ; first coordinate of video mem\nrep stosw ; push it to video memory\n\n; Draw Grid Background\n; each light is 5x3 'pixels' at 5x5 lights. with border, full grid would be 31x21 'pixels'\nmov bx, 21\t\t\t; 21 rows\nmov ax, 0x78b2\t\t; initialize border color\nmov di, 520\t\t\t; initialize top-left corner\ngrid:\nmov cx, 31\t\t\t; A row of 31\nrep stosw\t\t\t; Draw the row\nadd di, 98\t\t\t; offset to get to next row\ndec bx\t\t\t\t; next iteration\njne grid\t\t\t; repeat if not done\n\n; Draw all lights out\n; bx already 0 from above Routine\nmov cl, 25\nclearboard:\nmov byte [board + bx], 0\ninc bx\nloop clearboard\n; Initialize 4 lights turned on that are still in a winnable state\nmov word [board + 20], 0x0101\nmov word [board + 23], 0x0101\n\n; Evil?\nin al,(0x40) ; Get random\ncmp al, 0x10 ; 1 in 16 chance it will be evil\nja evildone \t; If above, then not evil\n; Otherwise: Evil (start at unsolvable position)\nmov byte [board], 1\t\t\t\t; Unsolvable modification\nmov byte [lighton + 2], 0x44\t; Change lights from yellow to red\nevildone:\n\n; Scramble (game plays itself randomely for 255 moves)\nmov bp, 0xff ; Init to 255 rounds of movement\nscramble:\n dec bp\n je gameloop ; Once done, go to main game loop\n in al,(0x40) ; Get 'random' value\n and al, 3 ; Only preserve last 2 bits (for 4 possible up/down/left/right moves)\n push word scrambletoggle ; point of return instead of using call for the below jumps\n ; Do a random tile move based on random results\n; cmp al, 0 ; and al,3 already did this comparison\n je up\n cmp al, 1\n je down\n cmp al, 2\n je left\n cmp al, 3\n je right\n scrambletoggle:\n push word scramble\n jmp toggle\n\ngameloop:\n; Keyboard handling\nmov ah, 1 ; Is there a key\nint 0x16 ; \"\"\njz gameloop ; If not wait for a key\ncbw ; clear ax (Get the key)\nint 0x16 ; \"\"\npush word gameloop ; point of return instead of using call\n; Get the Keys\ncmp ah, UP\nje up\ncmp ah, DOWN\nje down\ncmp ah, LEFT\nje left\ncmp ah, RIGHT\nje right\ncmp ah, SPACE\nje toggle\ncmp ah, 0x01\nje exit\nret\nexit:\n mov ax,0x0002 ; Clear screen\n int 0x10\n int 0x20 ; Return to bootOS\nup:\n\tsub si, 0x100\t\t; move coordinate\n\tcmp si, -1\t\t\t; bounds check\n\tjg done\t\t\t\t; if good, no mods needed\n\tadd si, 0x100 \t\t; Otherwise, correct bounds\n\tjmp done\ndown:\n\tadd si, 0x100\n\tcmp si, 0x405\n\tjl done\n\tsub si, 0x100\n\tjmp done\nleft:\n\tsub si, 1\n\tmov ax, si\n\tcmp al, -1\n\tjg done\n\tadd si, 1\n\tjmp done\nright:\n\tadd si, 1\n\tmov ax, si\n\tcmp al, 5\n\tjl done\n\tsub si, 1\n\tjmp done\ntoggle:\n\tcall flip\t\t;flip cursored light\n\n\tsub si, 0x100\t\t\t; move up a light\n\tcmp si, -1\t\t\t\t; see if out of bounds\n\tjg upflip\t\t\t\t; if in bounds\n\tjmp updone\t\t\t\t; otherwise don't flip and correct bounds\n\tupflip: call flip\n\tupdone: add si, 0x100\n\n\tadd si, 0x100\n\tcmp si, 0x405\n\tjl downflip\n\tjmp downdone\n\tdownflip: call flip\n\tdowndone: sub si, 0x100\n\n\tsub si, 1\n\tmov ax, si\n\tcmp al, -1\n\tjg leftflip\n\tjmp leftdone\n\tleftflip: call flip\n\tleftdone: add si, 1\n\n\tadd si, 1\n\tmov ax, si\n\tcmp al, 5\n\tjl rightflip\n\tjmp rightdone\n\trightflip: call flip\n\trightdone: sub si, 1\n\ndone:\n\tcall drawboard\t\t; redraw the board\n\tcall cursor\t\t\t; redraw the cursor over the board\n\tcall wincheck\t\t; check to see if all lights are off\nret\n\n; Routine for setting on/off color\nlightoff:\npush 0\t\t\t\t; 0 for off, but default\njmp lighton_b\t\t; if so, we can draw it\nlighton:\t\t\t; enter on light on possibly\npush 0xee00\t\t\t; 'light-on' character\nlighton_b:\ncall getcoord\t\t; AX has an encoded coord, this gets the di value needed\npop ax\t\t\t\t; get color value off the stack into ax\ncall drawlight\t\t; draw the light\nret\n\n; Routine that gets the upper left coordinate of a 5x5 light\ngetcoord:\nmov di, 0x2aa\t\t; starting corner of first (top left) light\nmov cl, ah\t\t\t; y coord\ncoordloop1:\nadd di, 0x280\t\t; for each y, add 0x280\nloop coordloop1\nmov cl, al\t\t\t; x coord\ncoordloop2:\nadd di, 0xc\t\t\t; for each x, add 12\nloop coordloop2\nret\n\n; Routine for drawing the light, color should be already chosen before entering\ndrawlight:\nmov bx, 3\t\t\t; 3 rows\nlightgrid:\nmov cx, 5\t\t\t; A row of 5\nrep stosw\t\t\t; Draw the row\nadd di, 150\t\t\t; offset to get to next row\ndec bx\t\t\t\t; next iteration\njne lightgrid\t\t; repeat if not done\nret\n\n; Taking the on/off (1 or 0) values of our 25 byte board array and\n; drawing them to screen. It may seem like a waste of data/bytes to\n; store 1/0 flags in each byte, but this memory does not account\n; for any of the 512 byte limit and a decoding routine for packed data\n; WOULD account for using the 512 byte limit.\ndrawboard:\nxor ax, ax\t\t; init\nxor bx, bx\t\t; init\nfillboard:\npush ax\t\t\t; safe keeping for ax and bx, they get mangled in some calls\npush bx\nmov cl, byte [board + bx]\t; Get a light value\ncmp cl, 0\t\t\t\t\t; is it off\njne otherlight\t\t\t\t; if its not off (on), go and call 'lighton'\ncall lightoff\t\t\t\t; otherwise we call 'lightoff'\njmp nextdraw\notherlight:\ncall lighton\nnextdraw:\npop bx\t\t\t\t; restore our ax, and bx values\npop ax\ninc ah\t\t\t\t; increment our y coordinate\ninc bx\t\t\t\t; increment to next light value in data array\ncmp ah, 5\t\t\t; is it last column?\njne fillboard\t\t; if not, keep processing\ninc al\t\t\t\t; increment our x coordinate\ncbw\t\t\t\t\t; clear ah/(y coord)\ncmp al, 5\t\t\t; is it the lsat row?\njne fillboard\t\t; if not, keep going\nret\n\ncursor:\nmov ax, si\t\t\t; get encoded light coordinate\ncall getcoord\t\t; decode it to di value\nadd di, 164\t\t\t; adjust to center of light, not upper left corner\nmov ah, 0x88\t\t; Make color grey\nstosw\t\t\t\t; paint it\nret\n\n; This is a routine for just toggling one light\nflip:\nmov ax, si\t\t\t; get encoded coordinate into ax\nxor bx, bx\t\t\t; init\nmov cl, al\t\t\t; row into cl\nfliploop:\nadd bx, 5\t\t\t; add columns for how many are encoded\nloop fliploop\nadd bl, ah\t\t\t; add rows for how many are encoded\ncmp byte [board + bx], 1\t; is light on?\nje flipoff\t\t\t\t\t; if so, turn off\nmov byte [board + bx], 1\t; turn light on\nret\nflipoff:\nmov byte [board + bx], 0\t; turn light off\nret\n\n; See if all lights are turned off, if so, do 'winning' sequence.\nwincheck:\nxor bx, bx\t\t\t\t; init\nwinloop:\nmov al, [board + bx]\t; check light at current coord\ncmp al, 1\t\t\t\t; is it on?\nje checkdone\t\t\t; if it as, we haven't won\ninc bx\t\t\t\t\t; go to next light\ncmp bx, 26\t\t\t\t; have we checked all lights?\njl winloop\t\t\t\t; if not, keep processing\nmov ch, 0x10\t\t\t; otherwise, win screen, 0x10xx iterations\nwin:\nin al,(0x40) ; Get random\nshl ax, 8\t\t\t\t; Get into ah\nin al,(0x40)\t\t\t; Get random again and keep in al (for full ax random)\nmov di, ax\t\t\t\t; make it the coordinate (but it's also the character)\nand di, 0x0fff\t\t\t; mask coordinate so it's likely within screen\nstosw\t\t\t\t\t; print it\nmov bx, [0x046C] \t; Get timer state\nadd bx, 1 \t\t\t; 1 tick\ndelay:\ncmp [0x046C], bx\njb delay\nloop win\nint 0x19\t\t\t; Restart the game after it does a visual for a minute or so\ncheckdone:\nret\n\n;BIOS sig and padding\ntimes 510-($-$$) db 0\ndw 0xAA55\n\n; Board data, pointed to in a memory area after the executable code image. It consumes 25 bytes of data\nboard:\n", | |
"data": "McCO2I7QvACctLiOwLADzRC0AbUmzRC50AeYMf/zq7sVALiyeL8IArkfAPOrg8diS3X1sRnGhwB+AEPi+McGFH4BAccGF34BAeRAPBB3CsYGAH4BxgZAfUS9/wBNdBrkQCQDaHR8dEM8AXROPAJ0WjwDdGRoXHzrbbQBzRZ0+pjNFmh5fID8SHQhgPxQdCuA/Et0NoD8TXQ/gPw5dEiA/AF0AcO4AgDNEM0gge4AAYP+/398gcYAAet2gcYAAYH+BQR8bIHuAAHrZoPuAYnwPP9/XYPGAetYg8YBifA8BXxPg+4B60rouQCB7gABg/7/fwLrA+irAIHGAAGBxgABgf4FBHwC6wPomACB7gABg+4BifA8/38C6wPohgCDxgGDxgGJ8DwFfALrA+h1AIPuAeg5AOhfAOiJAMNqAOsDaADu6AUAWOgUAMO/qgKI4YHHgALi+ojBg8cM4vvDuwMAuQUA86uBx5YAS3X0wzHAMdtQU4qPAH6A+QB1Bei8/+sD6Lv/W1j+xEOA/AV14/7AmDwFddzDifDor/+Bx6QAtIirw4nwMduIwYPDBeL7AOOAvwB+AXQGxocAfgHDxocAfgDDMduKhwB+PAF0J0OD+xp88rUQ5EDB4AjkQInHgef/D6uLHmwEg8MBOR5sBHL64uPNGcMAAAAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"Original: Snake in my Boot": { | |
"link": "https://github.com/w-shackleton/snake-in-my-boot", | |
"description": "Another snake clone. Though there are many snake-like boot sector clones, the thing that makes this one unique is that the main 'business logic' was written in C (though there are many routines written in assembly). Though the code is written really well, this is a good example of the bloat that a compiler can add to something as small as a boot sector. Below is a fork that demonstrates a true-to-the-original assembly source (snake.asm) and a heavily commented and optimized version (snake2.asm) that shaves off 40% of the bytes that the compiler didn't need to add", | |
"asm": "; not available as single file", | |
"data": "McCO2I7AjtBmvAB8AADqE3wAAPy5JAS/AH7zqrQAsBPNEGbopgAAAPr0ZpBmkGaQtBHNFnUHZrgAAAAAy7QQzRa0AMtmYLSGuQIAMdLNFWZhy2ZgZonlZrgAoAAAjsBmuwoAAABLPmZni0UkiOJmuQoAAAD24WaJx4jQ9uEB2Ga5QAEAAPfhAcdmuQoAAAA+Z4pFKPOqg/sAdcoxwI7AZonsZmHLZmC4AKCOwLkAyDH/sBTzqjHAjsBmYcu4AVMx280VuAdTuwEAuQMAzRVnZo1MJARmg+TwZ2b/cfxmVWaJ5WZXZlZmvgEAAABmU2ZRZoPsGGbHBgB+BgAAAGboLf///4TAdA7Q+GaD4ANni7QAzH0AAGaJ92YDPiB+OT4EfnUSacdVYmb/BgB+BRk2JR8PowR+ZoseAH5mhdt0KGZLZ4uEGyB+AAA5x3UQZ2aJReRm6GT///9nZotF5GeJhBsifgAA69NmifBmAwYgfqMgfmap4PAAAHQGZug8////Zugf////Zg+/BgR+ZlJmUmZqCWZQZui3/v//ZoPEEGY5HgB+fh1mUGZQZ2YPv4QbIH4AAGZDZmoPZlBm6JH+///r2Gboe/7//+kx////AAEBAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"Optimised: Snake in my Boot": { | |
"link": "https://github.com/XlogicX/snake-in-my-boot", | |
"description": "Another snake clone. Though there are many snake-like boot sector clones, the thing that makes this one unique is that the main 'business logic' was written in C (though there are many routines written in assembly). Though the code is written really well, this is a good example of the bloat that a compiler can add to something as small as a boot sector. Below is a fork that demonstrates a true-to-the-original assembly source (snake.asm) and a heavily commented and optimized version (snake2.asm) that shaves off 40% of the bytes that the compiler didn't need to add", | |
"asm": "; Optimized\n[ORG 0x7c00]\nxor ax,ax\nmov ds,ax\nmov es,ax\nmov ss,ax\nmov sp,0x7c00\ncld \nmov\t ch,0x5\t ; cx = 0x500, enough to clear rest of screen\nmov di,0x7e00 ; starting at this coord\nrep stosb\nmov al, 0x13\nint 0x10\njmp init \nreadkey:\n mov ah,0x10 ; Read expanded keyboard character \n int 0x16 ; keyoard call\n mov ah,0x0 ; clear buffer\n ret \nsleep:\n pusha ; Save register states \n mov ah,0x86 ; sleep\n mov cl,0x2 ; 150,000 microseconds, each integer counts for 75,000 usecs it seems\n xor dx,dx\n int 0x15 ; int15/ah=86h: sleep in microseconds\n popa ; Restore register states\n ret \ngameover:\n int 0x19 ; Reboot the system and restart the game.\ncheck_keys:\n mov ah,0x11 ; Obtain status of the expanded keyboard buffer \n int 0x16 ; keyboard call\n jne readkey ; if there's a key, read the character\n xor ax,ax ; clear buffer\n ret \ninit:\n mov si,0x1 ; Start motion is 1 (going right)\n mov BYTE [0x7e00],0x6 ; Init the length to 6\n mov ax,0xa000 ; VGA Segment\n mov es,ax ; gets it into es \n gameloop:\n call check_keys\n\ttest al,al ; was there a key?\n\tje growth_check ; no keys, just keep going in same direction\n ; 00 = a - left /-1\n ; 01 = s - down / 0x100\n ; 10 = d - right / 1\n ; 11 = w - up / -0x100\n ; Extract bits 1 and 2 and lookup motion in table above. We do no\n ; validation of the key so other keys will go in random directions\n ; but oh well \n\tsar al,1\t\t\t; asdw conveniently unique to bits 1 and 2, this shaves off bit 0\n\tand ax,0x3\t\t; just keep these 2 bits at the end now, and use as offset to lookup\n\tmov si,WORD [eax+eax*1+direction] ; Get decoded motion from direction lookup\n growth_check:\n\tmov di,si\n\tadd di,WORD [0x7e20] ; new position based on motion direction\n\tcmp WORD [0x7e04],di ; does new head equal food?\n\tjne advance_init ; if not, don't grow snake and put out new food\n ; Predictable RNG behavior here for food placement\n\timul ax,di,0x6255 ; 25173\n\tinc WORD [0x7e00] ; increment length\n\tadd ax,0x3619 ; 13849\n\tand ax,0xf1f ; game area mask\n\tmov [0x7e04],ax ; new food value is: food * 25173 + 13849 AND game mask\n ; Advance snake\n advance_init:\n\tmov bx,WORD [0x7e00] ; length into bx\n advance:\n test bx,bx ; keep going until all of length has been processed\n\tje boundscheck ; if done, do bounds check\n\tdec bx ; process next peice of length\n\tmov ax,WORD [ebx+ebx*1+0x7e20] ; stored snake motion/direction\n cmp di,ax ; compare it to video coord\n\tjne notded ; see if new location is on it's own body, skip if not\n\tcall gameover\n notded:\n\tmov WORD [ebx+ebx*1+0x7e22],ax ; store body part in array\n jmp advance ; and go to next body part\n ; See if snake got out of bounds\n boundscheck:\n\tmov ax,si ; get location\n\tadd ax,WORD [0x7e20] ; add stored coordinate\n\ttest ax,0xf0e0 ; test with bounds at end of screen\n\tmov [0x7e20],ax ; put processed value back into memory\n\tje inbounds ; if equal, in bounds\n\tcall gameover ; otherwise, gameover\n inbounds:\n\tcall clear_screen\n\t; Draw the food\n movsx eax,WORD [0x7e04] ; Food location\n\tpush dx\n\tpush dx\n\tpush word 0x9 ; Push Blue Apple as argument\n\tpush ax ; Push food location as argument\n\tpush ax ; Just for stack adjustment\n\tcall draw_cell\n drawsnake:\n ; Loop to draw rest of snake\n\tadd sp,0x0a ; Adjust stack\n\tcmp WORD [0x7e00],bx ; Has bx reached length?\n\tjle snakedrawn ; then done drawing\n\tpush ax\n\tpush ax\n\tmovsx eax,WORD [ebx+ebx*1+0x7e20] ; Get stored body part\n push dword 0xf ; Body part is white, push that to stack\n\tinc bx ; next body part\n\tpush ax ; location of body part\n\tpush ax ; Just for stack adjustment\n\tcall draw_cell ; draw it\n\tjmp drawsnake ; draw next part\n snakedrawn:\n\tcall sleep\n\tjmp gameloop\nclear_screen:\n pusha \n mov ch,0xc8 ; 320 * 160\n xor di,di ; init corner of screen\n mov al,0x14 ; grey\n rep stosb \n popa \n ret \ndraw_cell:\n ; Requires argument of coordinate and color prepopulated in stack\n pushad \n mov bp,sp\n mov bx,0xa ; Cell size (10)\ndraw_line:\n dec bx ; decrement Y counter\n ; Calculate intiial VGA X offset into di\n mov ax,WORD [bp+0x24] ; load X coord into al, Y coord into ah\n mov dl,ah ; load Y coord into dl\n mov cl,0xa ; cx will have cell size\n mul cl ; mul X coord by 10, store in ax\n mov di,ax ; store result into di\n mov al,dl ; load Y coord into dl?\n mul cl ; mul by 10\n add ax,bx ; add on current Y counter\n mov cx,0x140 ; Get X resolution into cx\n mul cx ; multiply by X resolution\n add di,ax ; add to video memory coord\n ; write a row of pixels of the given color\n mov cx,0xa ; so 10 goes into cx\n mov al,BYTE [bp+0x26] ; Cell Color\n rep stosb ; draw it\n cmp bx,0x0 ; is Y counter finished?\n jne draw_line ; If not, keep going\n popad \n ret \n\n; Data lookup table, 4 16-bit values: -1(left), 256(down), 1(right), -256(up)\ndirection:\n db 0xff, 0xff, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff\n\n;BIOS sig and padding\ntimes 510-($-$$) db 0\ndw 0xAA55\n", | |
"data": "McCO2I7AjtC8AHz8tQW/AH7zqrATzRDrHbQQzRa0AMNgtIaxAjHSzRVhw80ZtBHNFnXmMcDDvgEAxgYAfga4AKCOwOjn/4TAdA3Q+IPgA2eLtAAafQAAifcDPiB+OT4EfnURacdVYv8GAH4FGTYlHw+jBH6LHgB+hdt0Gktni4QbIH4AADnHdQPon/9niYQbIn4AAOviifADBiB+qeDwoyB+dAPohP/oNQBmD78GBH5SUmoJUFDoMQCDxAo5HgB+fhdQUGZnD7+EGyB+AABmag9DUFDoEwDr4OhE/+lk/2C1yDH/sBTzqmHDZmCJ5bsKAEuLRiSI4rEK9uGJx4jQ9uEB2LlAAffhAce5CgCKRibzqoP7AHXaZmHD//8AAQEAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"Sokoban": { | |
"link": "https://ish.works/bootsector/sokoban.asm", | |
"description": "A really great looking Sokoban clone", | |
"asm": "bits 16 ; tell NASM this is 16 bit code\norg 0x7c00 ; tell NASM that our code will be loaded at offset 0x7c00\n\n%define CURRENT_LEVEL 0x1000\n%define CURRENT_LEVEL_4 0x1004\n%define SCREEN_DS 0xb800\n\nboot:\n ; clear screen (re-set text mode)\n xor ah, ah\n mov al, 0x03 ; text mode 80x25 16 colours\n int 0x10\n\n ; disable cursor\n mov ah, 0x01\n mov ch, 0x3f\n int 0x10\n\n ; set up stack\n mov ax, 0x9000\n mov ss, ax\n mov sp, 0x400\n\n ; set current level to test level by copying\n mov si, test_level\n\n ; get width and height and multiply by each other\n mov ax, [si]\n mul ah\n\n ; set multiplied width and height + 4 as counter\n mov cx, ax\n ;add cx, 4\n\n mov di, CURRENT_LEVEL ; next address to copy to\n xor ax, ax\n mov es, ax\n\n ; copy map size and player position (\"uncompressed\")\n lodsw\n stosw\n lodsw\n stosw\n\n.copy_level_loop:\n ; load \"compressed\" byte: e.g. 0x28 or 0x44 into AL\n lodsb\n\n mov ah, al ; AX = 0x2828\n and ax, 0x0FF0 ; AX = 0x0820 (little endian: 20 08)\n shr al, 4 ; AX = 0x0802 (little endian: 02 08)\n\n ; save \"uncompressed\" word: e.g. 02 08 or 04 04 from AX\n stosw\n\n loop .copy_level_loop\n\n call draw_current_level\n\n.mainloop:\n ; read key\n xor ax, ax\n int 0x16\n\n cmp ah, 0x01 ; esc\n je boot\n\n cmp ah, 0x50 ; down arrow\n je .try_move_down\n\n cmp ah, 0x48 ; up arrow\n je .try_move_up\n\n cmp ah, 0x4b ; left arrow\n je .try_move_left\n\n cmp ah, 0x4d ; right arrow\n je .try_move_right\n\n.redraw:\n call draw_current_level\n\n.check_win:\n\n ; get width and height\n mov ax, [CURRENT_LEVEL] ; al = width; ah = height\n mul ah\n mov cx, ax ; cx = size of map\n\n xor bx, bx ; bx = number of bricks-NOT-on-a-spot\n\n mov si, CURRENT_LEVEL_4\n.check_win_loop:\n lodsb\n cmp al, 2\n jne .not_a_brick\n inc bx\n.not_a_brick:\n loop .check_win_loop\n\n ; so, did we win? is the number of spotless bricks == 0??\n cmp bx, 0\n je win\n jmp .mainloop\n\n\n.try_move_down:\n mov al, byte [CURRENT_LEVEL] ; (width of current level) to the right = 1 down\n call try_move\n jmp .redraw\n\n.try_move_up:\n mov al, byte [CURRENT_LEVEL]\n neg al ; (width of current level) to the left = 1 up\n call try_move\n jmp .redraw\n\n.try_move_left:\n mov al, -1 ; one to the left\n call try_move\n jmp .redraw\n\n.try_move_right:\n mov al, 1 ; one to the right\n call try_move\n jmp .redraw\n\nwin:\n ; print a nice win message to the middle of the screen\n mov si, str_you_win\n\n ; destination position on screen\n mov ax, SCREEN_DS\n mov es, ax\n mov di, (80 * 12 + 40 - 6) * 2\n\n mov ah, 0x0F\n.loop:\n lodsb\n\n cmp al, 0\n je wait_for_esc\n\n stosw\n jmp .loop\n\nwait_for_esc:\n ; read key\n xor ax, ax\n int 0x16\n\n cmp ah, 0x01 ; esc\n je boot\n jmp wait_for_esc\n; halt:\n; cli ; clear interrupt flag\n; hlt ; halt execution\n\n\n;; functions:\n\ndraw_current_level:\n ; get width and height\n mov cx, [CURRENT_LEVEL] ; cl = width; ch = height\n push cx ; put it in the stack for later reuse\n\n ; print in the middle and not in the corner\n mov di, 2000; middle of screen\n\n ; offset by half of width\n mov bl, cl\n and bx, 0x00FE\n sub di, bx\n\n ; offset by half of height\n mov cl, ch\n and cx, 0x00FE\n mov ax, 80\n mul cx\n sub di, ax\n\n\n mov si, CURRENT_LEVEL_4 ; source byte\n\n ; screen memory in text mode\n mov ax, SCREEN_DS\n mov es, ax\n\n.loop:\n mov bl, [si]\n xor bh, bh\n add bx, bx\n add bx, display_chars\n mov dx, [bx]\n mov [es:di], dx\n\n inc si\n add di, 2\n pop cx ; get counters\n dec cl ; subtract 1 from X axis counter\n jz .nextrow\n push cx\n jmp .loop\n\n.nextrow:\n dec ch ; subtract 1 from Y axis counter\n jz .finished\n mov cl, [CURRENT_LEVEL]\n push cx\n\n ; jump to next row down\n xor ch, ch\n neg cx\n add cx, 80\n add cx, cx\n add di, cx\n\n jmp .loop\n\n.finished:\n ret\n\ntry_move:\n ; try to move the player\n ; al = offset of how much to move by\n pusha\n\n ; extend al into ax (signed)\n test al, al ; check if negative\n js .negative_al\n xor ah, ah\n jmp .after_al\n.negative_al:\n mov ah, 0xFF\n.after_al:\n push ax\n\n ; calculate total level size\n mov ax, [CURRENT_LEVEL]\n mul ah\n\n ; calculate requested destination position\n pop bx\n push bx\n mov dx, [CURRENT_LEVEL + 2]\n add bx, dx\n\n ; check if in bounds\n cmp bx, 0\n jl .finished\n cmp bx, ax\n jg .finished\n\n ; get value at destination position\n mov cl, [CURRENT_LEVEL_4 + bx]\n cmp cl, 4\n je .cant_push ; it's a wall\n test cl, 0x02\n jz .dont_push ; it's not a brick (on spot, or not), so don't try pushing\n\n ; try pushing brick\n pop cx ; get move offset\n push bx ; store player's destination position (brick's current position)\n\n mov dx, bx ; dx = current brick position\n add bx, cx ; bx = next brick position\n\n ; check bounds\n cmp bx, 0\n jl .cant_push\n cmp bx, ax\n jg .cant_push\n\n ; get value at destination position\n mov ch, [CURRENT_LEVEL_4 + bx]\n test ch, 0x0E ; test if the destination is occupied at all by ANDing with 0000 1110\n jnz .cant_push\n\n ; all checks passed! push the brick\n\n ; add new brick to screen\n or ch, 0x02 ; add brick bit, by ORing with 0000 0010\n mov [CURRENT_LEVEL_4 + bx], ch\n\n ; remove old brick from screen\n mov si, dx\n mov cl, [CURRENT_LEVEL_4 + si]\n and cl, 0xFD ; remove brick bit, by ANDing with 1111 1101\n mov [CURRENT_LEVEL_4 + si], cl\n\n mov dx, [CURRENT_LEVEL + 2] ; dx = current player position\n pop bx ; bx = next player position\n jmp .redraw_player\n\n.cant_push:\n pop bx\n jmp .finished\n\n.dont_push:\n pop cx ; don't need to have this offset in the stack anymore\n\n.redraw_player:\n ; remove old player from screen\n mov si, dx\n mov cl, [CURRENT_LEVEL_4 + si]\n and cl, 0xF7 ; remove player bit, by ANDing with 1111 0111\n mov [CURRENT_LEVEL_4 + si], cl\n\n ; add new player to screen\n mov ch, [CURRENT_LEVEL_4 + bx]\n or ch, 0x08 ; add player bit, by ORing with 0000 1000\n mov [CURRENT_LEVEL_4 + bx], ch\n\n ; update player position in memory\n mov [CURRENT_LEVEL + 2], bx\n\n.finished:\n\n popa\n ret\n\n\n; data section:\n\n; 0000 0000 EMPTY\n; 0000 0001 SPOT\n; 0000 0010 BRICK\n; 0000 0011 BRICK ON SPOT\n; 0000 0100 WALL\n; 0000 1000 PLAYER\n; 0000 1001 PLAYER ON SPOT\ntest_level:\n ; this was the original level format, which was quite big:\n\n ; db 9, 7 ; width, height\n ; dw 32 ; playerxy\n ; db 4,4,4,4,4,4,0,0,0\n ; db 4,0,0,0,0,4,0,0,0\n ; db 4,0,0,2,0,4,4,0,0\n ; db 4,0,2,4,1,9,4,4,4\n ; db 4,4,0,0,3,1,2,0,4\n ; db 0,4,0,0,0,0,0,0,4\n ; db 0,4,4,4,4,4,4,4,4\n ; db 14, 10 ;width, height\n ; dw 63 ;playerxy\n\n ; when i tried to put in THIS level (from https://www.youtube.com/watch?v=fg8QImlvB-k)\n ; i passed the 512 byte limit...\n\n ; db 4,4,4,4,4,4,4,4,4,4,4,4,0,0\n ; db 4,1,1,0,0,4,0,0,0,0,0,4,4,4\n ; db 4,1,1,0,0,4,0,2,0,0,2,0,0,4\n ; db 4,1,1,0,0,4,2,4,4,4,4,0,0,4\n ; db 4,1,1,0,0,0,0,8,0,4,4,0,0,4\n ; db 4,1,1,0,0,4,0,4,0,0,2,0,4,4\n ; db 4,4,4,4,4,4,0,4,4,2,0,2,0,4\n ; db 0,0,4,0,2,0,0,2,0,2,0,2,0,4\n ; db 0,0,4,0,0,0,0,4,0,0,0,0,0,4\n ; db 0,0,4,4,4,4,4,4,4,4,4,4,4,4\n\n ; so i compressed it! high nybble first, low nybble second\n db 14, 10 ;width, height\n dw 63 ;playerxy\n db 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x00\n db 0x41, 0x10, 0x04, 0x00, 0x00, 0x04, 0x44\n db 0x41, 0x10, 0x04, 0x02, 0x00, 0x20, 0x04\n db 0x41, 0x10, 0x04, 0x24, 0x44, 0x40, 0x04\n db 0x41, 0x10, 0x00, 0x08, 0x04, 0x40, 0x04\n db 0x41, 0x10, 0x04, 0x04, 0x00, 0x20, 0x44\n db 0x44, 0x44, 0x44, 0x04, 0x42, 0x02, 0x04\n db 0x00, 0x40, 0x20, 0x02, 0x02, 0x02, 0x04\n db 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x04\n db 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44\n\n\n\ndisplay_chars: db 0, 0x07 ; blank\n db 249, 0x07 ; spot\n db 4, 0x0C ; brick\n db 4, 0x0A ; brick on spot\n db 178, 0x71 ; wall\n db \"5\", 0x07 ; (no 5)\n db \"6\", 0x07 ; (no 6)\n db \"7\", 0x07 ; (no 7)\n db 1, 0x0F ; player\n db 1, 0x0F ; player on spot\n\nstr_you_win: db 'YOU WIN! ',1,1,1,0\n\ntimes 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes\ndw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable!\n\n; if you don't want your resulting file to be as big as a floppy, then comment the following line:\n\ntimes (1440 * 1024) - ($-$$) db 0 ; pad with zeroes to make a floppy-sized image\n", | |
"data": "MOSwA80QtAG1P80QuACQjtC8AAS+kH2LBPbkicG/ABAxwI7Arautq6yIxCXwD8DoBKvi9Oh9ADHAzRaA/AF0wID8UHQtgPxIdDCA/Et0NYD8TXQ36F0AoQAQ9uSJwTHbvgQQrDwCdQFD4viD+wB0IuvFoAAQ6JMA69qgABD22OiJAOvQsP/oggDrybAB6HsA68K+7n24ALiOwL/EB7QPrDwAdAOr6/gxwM0WgPwBD4RO/+vziw4AEFG/0AeIy4Hj/gAp34jpgeH+ALhQAPfhKce+BBC4ALiOwIocMP8B24HD2n2LFyaJFUaDxwJZ/sl0A1Hr5f7NdBKKDgAQUTDt99mDwVAByQHP68/DYITAeAQw5OsCtP9QoQAQ9uRbU4sWAhAB04P7AHxlOcN/YYqPBBCA+QR0OPbBAnQ2WVOJ2gHLg/sAfCg5w38kiq8EEPbFDnUbgM0CiK8EEInWiowEEIDh/YiMBBCLFgIQW+sEW+sdWYnWiowEEIDh94iMBBCKrwQQgM0IiK8EEIkeAhBhww4KPwBEREREREQAQRAEAAAEREEQBAIAIARBEAQkREAEQRAACARABEEQBAQAIEREREQEQgIEAEAgAgICBABAAAQAAAQAREREREREAAf5BwQMBAqycTUHNgc3BwEPAQ9ZT1UgV0lOISABAQEAAAAAVao=", | |
"help": "" | |
}, | |
"4-in-a-row": { | |
"link": "https://github.com/egtzori/4inabs", | |
"description": "A connect4 clone, intended to be played with 2 players", | |
"asm": "[bits 16] ; tell assembler that working in real mode(16 bit mode) \n[org 0x7c00] ; organize from 0x7C00 memory location where BIOS will load us \n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\nstart:\n ; cls\n mov ax, 0xb800\n mov es, ax\n xor di, di\n mov cx, 0x07d0\n mov ax, 0x0000\n rep stosw\n\n;;;;; print bottomrow - 1 to 7\n mov ax, rowstart+90\n mov es, ax\n xor di, di\n mov ax,0x0231\n.loop_printbr:\n mov word [es:di], ax\n inc di\n inc di\n inc ax\n cmp al, 0x38\n jne .loop_printbr\n.done:\n\n;;;;\n\nmov ax, rowstart+162\nmov es, ax\n\nmov si, msg_playerturn ; 'go' or 'Player' text\nmov bh, 12 ; color\ncall message\n\n.loop1:\n call showmsg_playerturn\n mov ah, 0 ; wait for key\n int 016h\n\n; action: ; key in al\n ; cmp al, 0x72 ; 'r'\n ; jne .next\n ; call reboot\n cmp al, 0x30\n jbe .loop1\n sub al, 0x30\n cmp al, 7\n jg .loop1\n call play\n\n jmp .loop1\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\nrowstart equ 0xb800\nbrowlen equ 80*2\nplayerturn db 0 ; 0 = player1, 1 = player2\nsyms db 'XO' ; to draw\n; symplayer db '12' ; to draw\nsymcolors db 0x40,0x12 ; to draw\n; turns db 0 ;turns counter - removed cuse no memory\n; barrier db 1 ; FIXME - this gets overwritten\n\nmsg_playerturn db 'Go',0\n; msg_gameend_draw db 'Game ended draw',0\nmsg_gameend_winner db 'Win ',0\n\nmessage: ; Dump si to screen.\n xor di, di\n.loop:\n mov cl, [si]\n test cl, cl\n jz .done\n mov ch, bh\n mov word [es:di], cx\n inc di\n inc di\n inc si\n jmp .loop\n.done:\n ret\n\nshowmsg_playerturn:\n mov ax, rowstart+162\n mov es, ax\n\n mov al, [playerturn]\n add al, 0x31\n mov ah, 12 ; color\n\n mov word [es:16], ax\n ret\n\nshowmsg_playerwin:\n mov ax, rowstart+200\n mov es, ax\n\n mov si, msg_gameend_winner\n mov bh, 13 ; color\n call message\n\n mov al, [playerturn]\n add al, 0x31\n mov ah, ch ; color\n mov word [es:di], ax\n\n mov ah, 0 ; wait for key\n int 0x16\n call reboot\n ret;\n\nplay: ; key in al\n ; double al and store key offset in bx\n dec ax ;; key 1 = row 0\n shl ax, 1\n xor ah, ah; and ax, 0xff\n mov bx, ax\n\n mov ax, 160\n mov cx, 7 ; ROWLEN\n imul cx\n\n add ax, bx\n mov si, ax\n\n push rowstart\n pop es\n\n.nextrow: ;; look for empty\n cmp word [es:si], 0\n jz .found\n sub si, 160\n inc cx\n jmp .nextrow\n ;; ERR cant play\n ret\n.found:\n ; mov di, playerturn\n mov al, byte [playerturn]\n mov bx, symcolors\n xlat ; tr colors\n mov ah, al\n mov al, byte [playerturn]\n mov bx, syms\n xlat\n mov word [es:si], ax\n\n call check_win ;; check before ending turn\n\n mov al, byte[playerturn] ;; next player\n xor al, 1\n mov byte [playerturn], al\n\n ; mov si, turns\n ; inc si\n ; mov byte [si], turns\n\n ret\n\ncheck_win: ; my symbol in ax\n ;; start line ...X\n ; mov ax, [es:si-6]\n ; add ax, [es:si-4]\n ; add ax, [es:si-2]\n ; add ax, [es:si]\n ; cmp ax, 0x0160\n ; je .win\n ; cmp ax, 0x493c\n ; je .win\n\n\n ; start line ...X\n mov al, byte [es:si] ; last played pos\n add al, byte [es:si-6]\n add al, byte [es:si-4]\n add al, byte [es:si-2]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n ; ..X.\n sub al, [es:si-6]\n add al, [es:si+2]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n ; .X..\n sub al, [es:si-4]\n add al, [es:si+4]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n ; X...\n sub al, [es:si-2]\n add al, [es:si+6]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n ;;;;;;;; end horizontal\n \n ;; do vertical\n mov al, byte [es:si] ; last played pos\n add al, [es:si+160*1]\n add al, [es:si+160*2]\n add al, [es:si+160*3]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n ;;;;;;;;; end vertical\n\n ;; do diagonals\n ; top left to bottom right\n mov al, byte [es:si] ; last played pos\n add al, [es:si-160*3-6]\n add al, [es:si-160*2-4]\n add al, [es:si-160*1-2]\n ;; call testwin\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*3-6]\n add al, [es:si+160*1+2]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*2-4]\n add al, [es:si+160*2+4]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*1-2]\n add al, [es:si+160*3+6]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n ; top right to bottom left\n mov al, byte [es:si] ; last played pos\n add al, [es:si-160*3+6]\n add al, [es:si-160*2+4]\n add al, [es:si-160*1+2]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*3+6]\n add al, [es:si+160*1-2]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*2+4]\n add al, [es:si+160*2-4]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n\n sub al, [es:si-160*1+2]\n add al, [es:si+160*3-6]\n cmp al, 0x3c\n je .win\n cmp al, 0x60\n je .win\n ;;;;;;;;; end diagonals\n\n\n ; mov bx, 68\n ; call _printal\n\n ret\n\n.win:\n call showmsg_playerwin\n ret;\n\nreboot:\n db 0EAh ; machine language to jump to FFFF:0000 (reboot)\n dw 0000h\n dw 0FFFFh\n\ntimes (510 - ($ - $$)) db 0x00 ;set 512 BS\ndw 0xAA55\n", | |
"data": "uAC4jsAx/7nQB7gAAPOruFq4jsAx/7gxAiaJBUdHQDw4dfa4oriOwL5LfLcM6CMA6DMAtADNFjwwdvUsMDwHf+/oUgDr6gBYT0ASR28AV2luIAAx/4oMhMl0Coj9JokNR0dG6/DDuKK4jsCgRnwEMbQMJqMQAMO4yLiOwL5OfLcN6M//oEZ8BDGI7CaJBbQAzRboYwHDSNHgMOSJw7igALkHAPfpAdiJxmgAuAcmgzwAdAiB7qAAQevzw6BGfLtJfNeIxKBGfLtHfNcmiQToCQCgRnw0AaJGfMMmigQmAkT6JgJE/CYCRP48PA+EBQE8YA+E/wAmKkT6JgJEAjw8D4TxADxgD4TrACYqRPwmAkQEPDwPhN0APGAPhNcAJipE/iYCRAY8PA+EyQA8YA+EwwAmigQmAoSgACYChEABJgKE4AE8PA+EqwA8YA+EpQAmigQmAoQa/iYChLz+JgKEXv88PA+EjQA8YA+EhwAmKoQa/iYChKIAPDx0eTxgdHUmKoS8/iYChEQBPDx0ZzxgdGMmKoRe/yYChOYBPDx0VTxgdFEmigQmAoQm/iYChMT+JgKEYv88PHQ7PGB0NyYqhCb+JgKEngA8PHQpPGB0JSYqhMT+JgKEPAE8PHQXPGB0EyYqhGL/JgKE2gE8PHQFPGB0AcPogP7D6gAA//8AVao=", | |
"help": "" | |
}, | |
"Follow the lights": { | |
"link": "https://github.com/nanochess/lights", | |
"description": "A Simon clone", | |
"asm": "\t;\n\t; Follow the lights\n\t;\n\t; by Oscar Toledo G.\n\t;\n\t; Creation date: Jan/16/2020.\n\t; Revision date: Jan/17/2020.\n\t; Added victory tune after 31 good lights.\n\t;\n\n\t;\n\t; Very useful info gathered from:\n\t; https://www.waitingforfriday.com/?p=586\n\t;\n\tcpu 8086\n\n %ifdef com_file\n\torg 0x0100\n %else\n\torg 0x7c00\n %endif\n\nold_time:\tequ 0x00\t; Old ticks\nbutton:\t\tequ 0x02\t; Button pressed\nnext_seq:\tequ 0x04\t; Next seq. number\ntiming:\t\tequ 0x06\t; Current timing\nmemory:\t\tequ 0x08\t; Start of memory\nmemory_end:\tequ 0x28\t; End of memory\n\nstart:\n\txor ax,ax\n\tmov cx,memory_end/2\n.0:\n\tpush ax\t\t; Zero word initialize\n\tloop .0\t\t; Repeat until completed\n\n\tmov al,0x02\t; Text mode 80x25\n\tint 0x10\t; Set up video mode\n\n\tmov bp,sp\t; Setup BP (Base Pointer)\n\n\tin al,0x40\t; Get a pseudorandom number\n\txchg ax,si\t; Put into SI\n\n\tcld\t\t; Clear Direction flag.\n\tmov ax,0xb800\t; Point to video segment\n\tmov ds,ax\n\tmov es,ax\n\n\tcall show_buttons\t; Show buttons\nrestart_game:\n\txor ax,ax\t; Restart sequence\n\tmov [bp+next_seq],ax\ngame_loop:\n\tmov cl,15\t; Wait 0.8 seconds.\n\tcall wait_ticks\n\n\t;\n\t; Add a new light to sequence\n\t;\n\tmov di,[bp+next_seq]\t; Curr. position.\n\n\tmov ax,97\t; Generate random number\n\tmul si\n\tadd ax,23\n\txchg ax,si\t; SI = next seed.\n\n\t\t\t; Notice it uses the\n\t\t\t; high byte because the\n\t\t\t; random period is\n\t\t\t; longer.\n\n\tand ah,0x03\t; Extract random from AH\n\tadd ah,0x31\t; Add ASCII 1\n\tmov [bp+di+memory],ah\t; Save into memory\n\n\tmov ax,8\t; 8 approx 0.42 secs.\n\tcmp di,5\t; For 5 or fewer lights.\n\tjb .2\n\tmov al,6\t; 6 approx 0.32 secs.\n\tcmp di,13\t; For 13 or fewer lights.\n\tjb .2\n\tmov al,4\t; 4 approx 0.22 secs.\n.2:\n\tmov [bp+timing],ax\n\tcmp di,31\t; Doing the 31st light?\n\tje victory\t; Yes, jump to victory.\n\tinc byte [bp+next_seq]\n\n\t;\n\t; Show current sequence\n\t;\n\txor di,di\t; Restart counter\n.1:\tmov al,[bp+di+memory]\t; Read light\n\tpush di\n\tmov [bp+button],al\t; Push button\n\tcall show_buttons\t; Show\n\tmov cx,[bp+timing]\t; Wait\n\tcall wait_ticks\n\tcall speaker_off\t; Turn off\n\n\tmov byte [bp+button],0\n\tcall show_buttons\t; Depress button\n\tcall wait_tick\n\tpop di\n\tinc di\t\t; Increase counter\n\tcmp di,[bp+next_seq]\n\tjne .1\n\n\t;\n\t; Empty keyboard buffer\n\t;\n.9:\n\tmov ah,0x01\t; Check for key pressed.\n\tint 0x16\n\tje .8\t\t; No, jump.\n\tmov ah,0x00\t; Read key.\n\tint 0x16\n\tjmp .9\t\t; Repeat loop\n.8:\n\t;\n\t; Comparison of player input with\n\t; sequence.\n\t;\n\txor di,di\t; Restart counter\n.4:\tmov ah,0x00\t; Wait for a key\n\tint 0x16\n\tcmp al,0x1b\t; Esc pressed?\n\tje exit_game\t; Yes, jump.\n\tcmp al,0x31\t; Less than ASCII 1?\n\tjb .4\t\t; Yes, jump.\n\tcmp al,0x35\t; Higher than ASCII 4?\n\tjnb .4\t\t; Yes, jump.\n\tpush ax\n\tpush di\n\tmov [bp+button],al\t; Push button\n\tcall show_buttons\t; Show\n\tmov cx,[bp+timing]\t; Wait\n\tcall wait_ticks\n\tcall speaker_off\t; Turn off\n\n\tmov byte [bp+button],0\n\tcall show_buttons\t; Depress button\n\tcall wait_tick\n\tpop di\n\tpop ax\n\tcmp al,[bp+di+memory]\t; Good hit?\n\tjne wrong\t\t; No, jump\n\t\n\tinc di\t\t; Increase counter\n\tcmp di,[bp+next_seq]\n\tjne .4\n\tjmp game_loop\n\n\t;\n\t; Player defeat by wrong button\n\t;\nwrong:\tmov cx,28409\t; 1193180 / 42\n\tcall speaker\t; Failure tone\n\tmov cl,27\t; 1.5 secs\n\tcall wait_ticks\n\tcall speaker_off\t; Turn off\n\tmov cl,27\t; 1.5 secs\n\tcall wait_ticks\n\tjmp restart_game\t; Restart game\n\n\t;\n\t; Victory\n\t;\nvictory:\n\tmov al,'2'\t; Victory tune\n\tmov cx,14\t; 14 notes\n.1:\tpush cx\n\tpush ax\n\tmov byte [bp+button],al\n\tcall show_buttons\t; Play\n\tmov cl,2\t; Wait 0.1 secs.\n\tcall wait_ticks\n\tmov byte [bp+button],0\t; Depress\n\tcall show_buttons\n\tpop ax\n\tinc ax\t\t; Next note\n\tcmp al,'5'\t; If goes to 5...\n\tjne .2\n\tmov al,'1'\t; ...go back to 1.\n.2:\n\tpop cx\n\tloop .1\n\tjmp wrong\t; Finish and restart\n\n\t;\n\t; Exit game\n\t;\nexit_game:\n\tmov ax,0x0002\t; Clear screen by...\n\tint 0x10\t; ...mode setup.\n\tint 0x20\t; Exit to DOS / bootOS.\n\n\t;\n\t; Show game buttons\n\t;\nshow_buttons:\n\tmov di,0x0166\t; Top left on screen\n\tmov bx,0x312f\t; ASCII 1, white on green\n\tmov cx,2873\t; 1193180 / 415.305 hz\n\tcall show_button\n\n\tmov di,0x0192\t; Top right on screen\n\tmov bx,0x324f\t; ASCII 2, white on red\n\tmov cx,3835\t; 1193180 / 311.127 hz\n\tcall show_button\n\n\tmov di,0x0846\t; Bottom left on screen\n\tmov bx,0x336f\t; ASCII 3, white on brown\n\tmov cx,4812\t; 1193180 / 247.942 hz\n\tcall show_button\n\n\tmov di,0x0872\t; Bottom right on screen\n\tmov bx,0x343f\t; ASCII 4, white on turquoise\n\tmov cx,5746\t; 1193180 / 207.652 hz\n\n\t; Fall-through\n\nshow_button:\n\tmov al,0x20\t; Fill with spaces\n\tcmp bh,[bp+button]\t; Is it pressed?\n\tjne .0\t\t; No, jump.\n\tcall speaker\t; Yes, play sound.\n\tmov al,0xb0\t; Semi-filled block\n.0:\n\tmov cx,10\t; 10 rows high.\n.1:\tpush cx\n\tmov ah,bl\t; Set attribute byte.\n\tmov cl,20\t; 20 columns width.\n\trep stosw\t; Fill on screen\n\tadd di,160-20*2\t\t; Go to next row\n\tpop cx\n\tloop .1\t\t; Repeat until filled\n\tmov al,bh\t; Get button number\n\tmov [di+20-5*160],ax\t; Put on center\n\tret\t\t; Return\n\n\t;\n\t; Wait for one tick\n\t;\nwait_tick:\n\tmov cl,1\n\n\t;\n\t; Wait for several ticks\n\t;\n\t; Input:\n\t; CL = Number of ticks\n\t;\nwait_ticks:\n\tmov ch,0\n.0:\n\tpush cx\t\t; Save counter\n.1:\n\tmov ah,0x00\t; Read ticks\n\tint 0x1a\t; Call BIOS\n\tcmp dx,[bp+old_time]\t; Wait for tick change\n\tje .1\n\tmov [bp+old_time],dx\t; Save new tick\n\tpop cx\t\t; Restore counter\n\tloop .0\t\t; Loop until complete\n\tret\t\t; Return\n\n\t;\n\t; Generate sound on PC speaker\n\t;\n\t; Input:\n\t; CX = Frequency value.\n\t; (calculate 1193180/freq = req. value)\n\t;\nspeaker:\n\tmov al,0xb6\t; Setup timer 2\n\tout 0x43,al\n\tmov al,cl\t; Low byte of timer count\n\tout 0x42,al\n\tmov al,ch\t; High byte of timer count\n\tout 0x42,al\n\tin al,0x61\n\tor al,0x03\t; Wire PC speaker to timer 2\n\tout 0x61,al\n\tret\n\n\t;\n\t; Turn speaker off\n\t;\nspeaker_off:\n\tin al,0x61\n\tand al,0xfc\t; Turn off\n\tout 0x61,al\n\tret\n\n\t;\n\t; Boot sector signature\n\t;\n %ifdef com_file\n %else\n\ttimes 510-($-$$) db 0x4f\n\tdb 0x55,0xaa\t; Make it a bootable sector\n %endif\n", | |
"data": "McC5FABQ4v2wAs0QieXkQJb8uAC4jtiOwOjwADHAiUYEsQ/oOAGLfgS4YQD35oPAF5aA5AOAxDGIYwi4CACD/wVyCbAGg/8NcgKwBIlGBoP/H3UD6YgA/kYEMf+KQwhXiEYC6KYAi04G6PIA6BUBxkYCAOiWAOjjAF9HO34Eddy0Ac0WdAa0AM0W6/Qx/7QAzRY8G3RvPDFy9Dw1c/BQV4hGAuhmAItOBuiyAOjVAMZGAgDoVgDoowBfWDpDCHUJRzt+BHXI6Vj/uflu6KIAsRvoigDorQCxG+iCAOk9/7AyuQ4AUVCIRgLoIACxAuhtAMZGAgDoFABYQDw1dQKwMVni4evEuAIAzRDNIL9mAbsvMbk5C+ghAL+SAbtPMrn7DugVAL9GCLtvM7nMEugJAL9yCLs/NLlyFrAgOn4CdQXoLgCwsLkKAFGI3LEU86uDx3hZ4vOI+ImF9PzDsQG1AFG0AM0aO1YAdPeJVgBZ4vDDsLbmQ4jI5kKI6OZC5GEMA+Zhw+RhJPzmYcNPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVao=", | |
"help": "" | |
}, | |
"Toledo-Atomchess": { | |
"link": "https://github.com/nanochess/Toledo-Atomchess", | |
"description": "A chess game with computer AI", | |
"asm": "; didnt get to work; warning uninitialised space ; got data from their compilation", | |
"data": "/LwAgA4ODh8HF7/4fYn5V1gkiHQCsAeq4vW+U32/AH6xCKyqDAiIRW/+RQ/GRV8J4vBoMnxofHxoQ3xodny+L31WVr74fbFJrLthfdc8DXUIg8YH6OgAsAro4wDi6sNC/s51PUaB/nh+dRlfXoTJdQqD/YF8BaTGRP8Aw70A/1VVvgB+rDDoSDwGc9kIwHUECO11AUBOBASIxIDkDLNs15KJ94jTlwIHl4oFQIokgPqH/sh0ZXIFgP4Dcqgw6CwJPAWKBXdCdQz+yb1OAHUCAe1YWMNQJAezW9eYgPkDcxFg6JL/gPUIQeiR/4njKW8OYTnFfweVdQTkQDyqWIgkiAV/BYPEBFZX/sww7HQJgPwEcwQIwHSM6UX/criA/gJ353WxllAsIDxAWJYYxuulh/foAQCXtADNFjwbdQLNIGC0DrcAzRBhJQ8Aaejw/427f37DAgUDBAYDBQIAAQUDCQMucHJicW5rDS5QUkJRTkuHi3+Df3d/3+Hu8g4SHyHwEP8BDxHx7/Hv8OAPERAgVG9sZWRvIEF0b21jaGVzcy4gQXVnLzIzLzIwMTkgKGMpMjAxNS0yMDE5IE9zY2FyIFRvbGVkbyBHLiB3d3cubmFub2NoZXNzLm9yZyBIYXBweSBjb2RpbmcgOikgTW9zdCBmdW4gTUJSIGV2ZXIhVao=", | |
"help": "" | |
}, | |
"chrome dino": { | |
"link": "https://github.com/franeklubi/dino", | |
"description": "Dino is a game that's based on Chrome's t-rex runner game! This clone has nice graphics and smooth gameplay", | |
"asm": "%define VAR_BASE 0xfa00\n%define @(name) [bp+(name)-VAR_BASE]\n\n[org 0x7c00]\n\ncpu 8086\nuse16\n\n_start:\n\tmov ax, 0x0013\n\tint 0x10\t\t; set video 320x200x256 mode\n\n\tmov ax, 0xa000\t; 0xa000 video segment\n\tmov es, ax\t\t; setup extended segment\n\n\tmov bp, VAR_BASE\n\n\t; init screen\n\tmov al, 0x0f\n\tmov cx, screen_width*screen_height\n\txor di, di\n\trep stosb\n\n\txor ax, ax\n\t; clearing variable memory\n\tmov di, VAR_END-VAR_BASE-1\n\t; use a manual loop since the destination is in ds\n\t.clear:\n\t\tmov [bp+di], al\n\t\tdec di\n\t\tjns .clear\n\n\t; initialize vars\n\tmov byte @(e_t_set_b), enemy_timer_max\t; enemy timer\n\n\t; draw ground line\n\tmov di, ground_start\n\tmov cl, screen_width/2\t; cx=0 from above\n\t; ax=0 from above\n\trep stosw\n\n\t; init_dirt generates random dirt\n\tinit_dirt:\n\t\tmov cx, dirt_rows\n\t_id_l:\n\t\tcall random_pixel\n\t\tloop _id_l\n\t; end init_dirt\n\n\n_game_loop:\n\tdec byte @(enemy_timer_b)\t; decreasing the enemy timer\n\n\n\t; clear playarea\n\tmov al, 0x0f\n\tmov di, playarea_start\n\tmov cx, playarea_lines*screen_width\n\trep stosb\n\n\n\tprint_score:\n\t\tmov cl, 0x27\t\t; cx=0, set initial column\n\t\tmov ax, [score_w]\t; get score\n\n\t_ps_div:\n\t\tcwd\t\t\t\t; clear dx\n\t\tdiv word [ten]\t; divide ax by 10\n\t\tadd dl, 0x30\t; convert number to ascii\n\n\t\t; saving registers so that interrupts don't interfere\n\t\tpush ax\n\n\t\tmov al, dl\t; get char from dl\n\t\tmov dl, cl\t; get column from cl\n\n\t\tmov bx, 0x000f\t; bh (page) = 0x00; bl (colour) = 0x0f (used later)\n\n\t\tmov ah, 0x02\t; set cursor\n\t\tint 0x10\n\n\t\tmov ah, 0x0e\t; printin chars bby\n\t\tint 0x10\n\n\t\t; recovering the registers\n\t\tpop ax\n\n\t\tdec cx\t; decrement column\n\n\t\tor ax, ax\n\t\tjnz _ps_div\n\t; end print_score\n\n\n\tmov dh, enemy_speed\t; setting enemy speed\n\t; dh = amount to advance the enemies\n\t; handle_draw_enemies loops through all the enemies\n\thandle_draw_enemies:\n\t\tmov cx, max_enemies\n\t\tmov di, enemies_start\n\t_dhe_l:\n\t\tmov bx, word [di]\t; get x,y coord\n\t\tor bl, bl\t\t\t; checking if the enemy is outside the screen\n\t\tjz _dhe_re\t\t\t; if so, try creating a new one\n\n\t\t; setting vars for draw_sprite\n\t\tmov dl, enemy_scaling\t; scaling\n\t\tmov si, [di+2]\t\t\t; get sprite address\n\t\tmov al, bh\t\t\t\t; y position\n\t\tcall draw_sprite\n\n\t\tsub byte [di], dh\t; subtract from x position\n\t\tjnb _dhe_after_point\n\t\tinc word @(score_w)\n\t\tmov byte [di], 0\t; just so it doesn't overflow on me\n\n\t\tmov ax, [score_w]\t\t; get score\n\t\tmov bl, score_divisor\n\t\tdiv bl\t\t\t\t\t; divide score by score_divisor\n\t\tor ah, ah\n\t\tjnz _dhe_after_point\t; if score%score_divisor != 0\n\n\t\t; decreasing the timer\n\t\tcmp byte @(e_t_set_b), enemy_timer_min\n\t\tjng _dhe_after_point\t; if e_t_set_b <= enemy_timer_min\n\t\tdec byte @(e_t_set_b)\n\t_dhe_after_point:\n\n\t\tjmp _dhe_i_end\t\t\t; jump to the end\n\n\t_dhe_re:\n\t\t; bl=0 on entry\n\t\t; random_enemy assumes di is set by handle_draw_enemies\n\t\trandom_enemy:\n\t\t\t; checking the timer\n\t\t\tcmp byte @(enemy_timer_b), bl\n\t\t\tjg _re_end\n\n\t\t\tmov al, [e_t_set_b]\n\t\t\tmov byte @(enemy_timer_b), al\t; setting the enemy timer\n\n\t\t\t; preparing enemy\n\t\t\tmov word [di], 255 | (139 << 8)\t; set horizontal & vertical position\n\t\t\tmov word [di+2], cactus+7\t\t; setting sprite to cactus\n\n\t\t\t; randomizing enemy\n\t\t\tin ax, 0x40\t; get 'random' number\n\n\t\t\tshr al, 1\n\t\t\tjnc _re_end\n\n\t\t\t; changing sprite from cactus to bomber\n\t\t\tadd word [di+2], bomber-cactus\n\t\t\tsub byte [di+1], 18\t; changing bomber's vertical position\n\n\t\t\tshr al, 1\n\t\t\tjc _re_end\n\n\t\t\tsub byte [di+1], 25\t; once again lifting bomber\n\t\t_re_end:\n\t\t; end random_enemy\n\n\t_dhe_i_end:\n\t\t; advance by enemy_size (di += 4)\n\t\tscasw\n\t\tscasw\n\t\tloop _dhe_l\n\t; end handle_draw_enemies\n\n\n\t; cx=0 on entry\n\t; handle_jump:\n\t\tmov si, rows_jump_b\n\t\tmov bx, rows_up_b\n\t\tcmp byte [bx], cl\t\t; cl=0, check if dino is in the air\n\t\tjng _hj_no_rows\n\t\tsub byte [bx], gravity\t; if so, subtract gravity from it's displacement\n\t\tjmp _hj_no_keystroke\t; and don't check for a keystroke\n\n\t_hj_no_rows:\n\t\tmov ah, 0x02\t\t; get shift flags\n\t\tint 0x16\n\t\ttest al, 0b11\t\t; testing for shift keys\n\t\tjz _hj_no_keystroke\n\t\tmov byte [si], jump\n\t_hj_no_keystroke:\n\n\t\tmov al, [si]\n\t\tcmp al, 0\t\t\t\t; check if jump force is greater than 0\n\t\tjng _hj_no_jump\n\t\tadd byte [bx], al\t\t; if it is - add it to the displacement\n\t\tsub byte [si], gravity\t; subtract gravity from the jump force\n\t_hj_no_jump:\n\t; end handle_jump\n\n\n\t; draw_dino draws dino accounting for the jump value\n\t; draw_dino:\n\t\tmov ax, dino_initial_y\t; ah=0\n\t\tmov bx, dino_initial_x\t; bh=0\n\n\t\t; check if to subtract the jump value\n\t\tmov cl, @(rows_up_b)\n\t\tcmp cl, ah\t; ah=0\n\t\tjng _dd_no_jump\n\t\tsub al, cl\n\t_dd_no_jump:\n\n\t\t; check for collisions\n\t\tpush ax\n\t\tmov dx, screen_width\n\t\tmul dx\n\t\tlea di, [bx+5*dino_scaling]\n\t\tadd di, ax\n\t\tmov byte cl, [es:di]\n\n\t\t; check for crouch\n\t\tmov ah, 0x02\n\t\tint 0x16\n\t\txor al, 0b100\t; check for ctrl key\n\t\tjnz _dd_no_crouch\n\n\t\tmov dl, dino_scaling_crouched\n\t\tmov byte @(rows_up_b), bh\t; bh is 0, thanks to previous mov\n\n\t\tjmp _dd_crouch_end\n\t_dd_no_crouch:\n\t\tmov dl, dino_scaling\n\n\t\tsub di, 7*dino_scaling*screen_width-2*dino_scaling\n\t\tand byte cl, [es:di]\n\n\t_dd_crouch_end:\n\t\tpop ax\n\n\t\t; draw dino!\n\t\tmov si, dino+7\n\t\tcall draw_sprite\n\n\t\t; finalize collision check\n\t\tor cl, cl\n\t\tjz game_over\n\t; end draw_dino\n\n\n\t; scrolls the ground at ground_start\n\tscroll_ground:\n\t\tmov si, ground_start+screen_width+1\n\t\tmov di, ground_start+screen_width\n\t\tmov cx, dirt_rows-1\n\t\trep es movsb\n\n\t\tcall random_pixel\t; generate random pixel at the end\n\t; end scroll_ground\n\n\n\t; waits for 1 system clock tick\n\tframe:\n\t\tmov ah, 0\n\t\tint 0x1a\n\t_f_l:\n\t\tmov bl, dl\n\t\tint 0x1a\n\t\txor bl, dl\n\t\tjz _f_l\n\t; end frame\n\n\tjmp _game_loop\n\n\n\n; prints game over string, waits for input, and then resets the game\ngame_over:\n\tmov bx, 0x000f\t; page 0, white colour\n\tmov dx, 0x0c0f\t; cursor row and col\n\tmov ah, 0x02\t; set cursor\n\tint 0x10\n\n\tmov ah, 0x0e\t\t\t\t; print char interrupt\n\tmov cx, str_go_end-str_go\t; 10 chars\n\tmov si, str_go\t\t\t\t; point to game_over string\n\n_go_l:\n\tlodsb\t\t; get char\n\tint 0x10\t; print it\n\tloop _go_l\n\n\tmov ah, 0x00\t; wait for an input\n\tint 0x16\n\n\tjmp _start\n\n\nrandom_pixel:\n\tin al, 0x40\n\tand al, 0x55\n\tjz _dd_black\n\tmov al, 0x0f\n_dd_black:\n\tstosb\n\tret\n\n\n; al = y coord, bl = x coord, dl = scaling;\n; modify coords and scaling; scaling - 1 for 8x8 pixels\n; mov the address of the sprite's last byte to the si register (addr+7)\ndraw_sprite:\n\tpush cx\n\tpush di\n\n\t; high bytes of the words will always be 0\n\tmov byte @(y_coord_w), al\n\tmov byte @(x_coord_w), bl\n\tmov bl, 8\t; bl will act as the sprite's byte counter\n\tmov bh, dl\t; bh will act as the row scaling counter\n\n_ds_coords:\n\t; prepare starting coords\n\tpush dx\n\tmov ax, @(y_coord_w)\t; get y coord\n\tmov dx, screen_width\t; size of pixel row\n\tmul dx\t\t\t\t\t; multiply ax by screen_width\n\tpop dx\n\n\tadd ax, @(x_coord_w)\t; add x coord\n\txchg ax, di\n\n\tmov cl, 8\t\t\t; cl will act as the sprite's pixel counter\n_ds_row_pixel:\n\tmov byte al, [si]\t; load sprite's byte\n\tshr al, cl\n\tmov al, 0\t\t\t; set colour to black\n\n\tpush cx\n\tmov cl, dl\t; horizontal scaling\n\n\tjc _ds_draw_pixel\t; perform a jump if carry is set because of shr\n\tadd di, cx\t\t\t; advance di\n\tjmp _ds_trans_done\n_ds_draw_pixel:\n\trep stosb\t\t\t; draw picked colour\n_ds_trans_done:\n\tpop cx\n\n\tloop _ds_row_pixel\t\t; loop for 8 pixels\n\n\tdec word @(y_coord_w)\t; increase the y coord\n\n\tdec byte bh\t\t\t\t; decrement the row counter\n\tjnz _ds_coords\t\t\t; repeat row\n\n\tmov byte bh, dl\t\t\t; reset row counter\n\n\tdec si\t\t\t\t\t; increase sprite address\n\tdec bl\t\t\t\t\t; decrease the byte counter\n\tjnz _ds_coords\n\n\tpop di\n\tpop cx\n\tret\n\n\n; general consts\nten\t\t\t\tequ 10\nscreen_width\tequ 320\nscreen_height\tequ 200\n\n; draw_dino consts\ndino_initial_y\t\t\tequ 139\ndino_initial_x\t\t\tequ 35\ndino_scaling\t\t\tequ 3\ndino_scaling_crouched\tequ 2\n\n; ground consts\nground_start\tequ 140*screen_width\ndirt_rows\t\tequ 10*screen_width\n\n; clear_playarea consts\nplayarea_start\tequ 26*screen_width\nplayarea_lines\tequ 114\n\n; handle_jump consts\ngravity\tequ 7\njump\tequ 30\n\n; handle_draw_enemies consts\nmax_enemies\t\tequ 40\nenemy_size\t\tequ 1+1+2\t; (byte, byte, word)\nenemy_speed\t\tequ 7\nenemy_scaling\tequ 2\n\n; enemy_timer_consts\nenemy_timer_max\tequ 20\nenemy_timer_min\tequ 10\n\n; score will be divided by this value when checking if to increase difficulty\nscore_divisor\tequ 10\n\n; game_over string const\nstr_go\tdb\t\"game over!\"\nstr_go_end:\n\n; sprite data\ndino\tdb \\\n\t0b00000110, \\\n\t0b00001101, \\\n\t0b00001111, \\\n\t0b00011110, \\\n\t0b10111100, \\\n\t0b01111010, \\\n\t0b00010000, \\\n\t0b00011000\n\ncactus\tdb \\\n\t0b00011100, \\\n\t0b00100010, \\\n\t0b01110011, \\\n\t0b00100110, \\\n\t0b01101011, \\\n\t0b00100010, \\\n\t0b01100111, \\\n\t0b00110010\n\nbomber\tdb \\\n\t0b00000011, \\\n\t0b00000111, \\\n\t0b01101110, \\\n\t0b10111111, \\\n\t0b11111111, \\\n\t0b00001110, \\\n\t0b00000111, \\\n\t0b00000001\n\ntimes 510-($-$$) db 0\n; the magic number\ndw 0xaa55\n\n[absolute VAR_BASE]\n; draw_sprite variables\ny_coord_w\tresw 1\t; word\nx_coord_w\tresw 1\t; word\n\n; score variable\nscore_w\tresw 1\n\n; handle_jump variables\nrows_up_b\tresb 1\nrows_jump_b\tresb 1\n\n; random_enemy variable\nenemy_timer_b\tresb 1\n\n; variable that the enemy_timer_b will be set to after overflowing\ne_t_set_b\tresb 1\n\n; handle_draw_enemies variable\nenemies_start\tresb enemy_size * max_enemies\n\nVAR_END:\n", | |
"data": "uBMAzRC4AKCOwL0A+rAPuQD6Mf/zqjHAv6kAiANPefvGRgkUvwCvsaDzq7mADOg5AeL7/k4IsA+/gCC5gI7zqrEnoQT6mfc2CgCAwjBQiNCIyrsPALQCzRC0Ds0QWEkJwHXitge5KAC/CvqLHQjbdCqyAot1Aoj46PkAKDVzGv9GBMYFAKEE+rMK9vMI5HUJgH4JCn4D/k4J6yo4Xgh/JaAJ+ohGCMcF/4vHRQLOfeVA0OhzEINFAgiAbQES0OhyBIBtARmvr+Kivgf6uwb6OA9+BYAvB+sLtALNFqgDdAPGBB6KBDwAfgUAB4AsB7iLALsjAIpOBjjhfgIoyFC6QAH34o1/DwHHJooNtALNFjQEdQeyAoh+BusJsgOB7zoaJiINWL7GfehKAAjJdB6+QbC/QLC5fwzzJqToLQC0AM0aiNPNGjDTdPjp5/67DwC6Dwy0As0QtA65CgC+tX2szRDi+7QAzRbplv7kQCRVdAKwD6rDUVeIRgCIXgKzCIjXUotGALpAAffiWgNGApexCIoE0uiwAFGI0XIEAc/rAvOqWeLs/04A/s911YjXTv7Ldc5fWcNnYW1lIG92ZXIhBg0PHrx6EBgcInMmayJnMgMHbr//DgcBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"pong": { | |
"link": "https://gitlab.com/queso_fuego/bootsector-pong", | |
"description": "A Pong clone with fairly challenging 'AI'", | |
"asm": "; 404 gitlab", | |
"data": "", | |
"help": "" | |
}, | |
"WASDMaze": { | |
"link": "https://github.com/Jethro82/Boot-Sector-Games", | |
"description": "A timed procedurally generated maze game, can you get to the finish before the timer runs out?", | |
"asm": "Org 7C00h\nStart:\nxor ax,ax\nmov ss,ax\nmov ds,ax ;set SS, DS to zero\nmov Ax,0B800h\nmov es,Ax ;set es to screen output\nmov sp,ax ;start stack pointer with away from code\n\n\n\nStartMazeWrite:\nmov ah,0\nint 1ah\nmov [7902],dx ;set initial timer\nmov dh,7Ch ;point to boot code as random data\nmov [7900],dx ;save random pointer\n\nmov ax,3\nint 10h ;cls for text mode\n\nmov ax,1112h\nint 10h ;set text mode to 50x80\n\nmov ax,0900h+219\nmov bx,4 ;set colour to red\nmov cx,3600\nint 10h ;write out solid blocks as maze\n\nmov bp,322 ;set pointer to first cell in maze\n\nFreeGrid:\n\tmov bl,32 ;set left to right length as 32 cells\n\tFG2:\n\t\tmov byte [es:bp],' ' ;clear grid pattern for both x,y as odd numbered cell\n\t\tcall random3bits\n\t\tcmp dl,1\n\t\tjg checkdown\n\t\t\tmov byte [es:bp+2],' ' ;one in eight chance of deleted next right space in grid\n\t\tcheckdown:\n\t\tcall random3bits\n\t\tcmp dl,1\n\t\tjg Advancegrid\n\t\t\tmov byte [es:bp+160],' ';one in eight chance of deleting next down space in grid\n\t\tadvancegrid:\n\t\tadd bp,4 ;advance grid two cells forward\n\t\tdec bx ;count down from 32\n\tjnz FG2\n\tadd bp,192 ;advance bp to first cell on next row down\n\tcmp bp,7000 ;check to see if 18 up down cells have been reached\njl FreeGrid\n;initialize X-Y coordinates of maze\n\n\nxor ax,ax ; set ah, al to zero\n\nNewRandomSeed1:\n\tRandom3Bit1:\n\t\tcall Random3Bits\n\t\tcmp dl,3\n\t\t\tjl GoDown1 ;25 percent chance of going down(if down is possible)\n\t\tcmp dl,6\n\t\t\tjl GoRight1 ;37.5 percent chance to go right\n\t\t\tje GoLeft1\n\t\t\tGoUp1:\n\t\t\t\tCMP AL,0\n\tjz AdvanceSeed1\n\t\t\t\tDEC AX\n\t\t\t\tCall GridCalcAndClear2\n\tjnc AdvanceSeed1\n\t\t\tGoDown1:\n\t\t\t\tCMP AL,20\n\tjz AdvanceSeed1\n\t\t\t\tCall GridCalcAndClear2\n\t\t\t\tinc ax\n\tjnz AdvanceSeed1\n\t\t\tGoLeft1:\n\t\t\t\tcmp ah,0\n\tjz AdvanceSeed1\n\t\t\t\tdec ah\n\t\t\t\tCall GridCalcAndClear\n\tjnz AdvanceSeed1\n\t\t\tGoRight1:\n\t\t\t\tcmp ah,31\n\tjz AdvanceSeed1\n\t\t\t\tCall GridCalcAndClear\n\t\t\t\tinc ah\n\tAdvanceSeed1:\n\t\tcmp ax,1f14h\njnz NewRandomSeed1\n\n\nWriteSecondPath:\nmov al,0\nNewRandomSeed2: \n\tRandom3Bit2:\n\t\tCall Random3Bits\n\t\tcmp dl,3\n\t\t\tjl GoDown2\n\t\tcmp dl,6\n\t\t\tjl GoLeft2\n\t\t\tje GoRight2\n\t\t\tGoUp2:\n\t\t\t\tCMP AL,0\n\tjz AdvanceSeed2\n\t\t\t\tdec ax\n\t\t\t\tCall GridCalcAndClear2\n\tjnc AdvanceSeed2\n\n\t\t\tGoDown2:\n\t\t\t\tCMP Al,20\n\tjz AdvanceSeed2\n\t\t\t\tCall GridCalcAndClear2\n\t\t\t\tinc ax\n\tjnz AdvanceSeed2\n\t\t\tGoLeft2:\n\t\t\t\tcmp ah,0\n\tjz AdvanceSeed2\n\t\t\t\tdec ah\n\t\t\t\tCall GridCalcAndClear\n\tjnc AdvanceSeed2\n\t\t\tGoRight2:\n\t\t\t\tcmp ah,31\n\tjz AdvanceSeed2\n\t\t\t\tCall GridCalcAndClear\n\t\t\t\tinc ah\n\tAdvanceSeed2:\n\t\tcmp ax,14h\njnz NewRandomSeed2\n\ninitrd:\n\n\nmov word [es:322],5700h+'G'\nmov bp,6846\nmov WORD [es:bp],301h\npush bp\n\nmov ah,02\nmov bh,0\nmov dx,2D00h\nint 10h ; set cursor location for progress bar\n\n\nmov ax,0a00h+178\nmov cx,364\nint 10h ; set time progress bar as shaded blocks\n\n\n\n\n\n\n\nGamePlayLoop:\n\tmov ah,1\n\tint 16h\n\tjz TimerCheck\n\t\tmov ah,0\n\t\tint 16h\n\t\tpop bp\n\t\tmov si,bp\n\t\tcmp al,'w'\n\t\tjnz CheckLeft\n\t\t\tsub si,160\n\t\tCheckLeft:\n\t\tcmp al, 'a'\n\t\tjnz checkdown2\n\t\t\tdec si\n\t\t\tdec si\n\t\tcheckdown2: \n\t\tcmp al,'s'\n\t\tjnz CheckRight\n\t\t\tadd si,160\n\t\tCheckRight:\n\t\tcmp al,'d'\n\t\tjnz Move\n\t\t\tinc si\n\t\t\tinc si\n\t\tMove:\n\t\tcmp byte [es:si],' ' \n\t\tjnz CheckGoal\n\t\t\tmov byte [es:bp],' '\n\t\t\tmov word [es:si],301h\n\t\t\tpush si\n\t\tjz TimerCheck\n\t\tCheckGoal:\n\t\tcmp byte [es:si],'G'\n\t\tjz EndGame1\n\t\tpush bp\n\tTimerCheck:\n\tmov ah,0\n\tint 1ah\n\tsub dx,[7902]\n\tcmp dx,364\n\tjz EndGame1\n\tmov cx,dx\n\tmov ax,0a20h\n\tinc cx\n\tint 10h \n\njmp GamePlayLoop\n\n\nEndGame1:\nmov ax,3\nint 10h\nxor di,di\nmov si,offset Victory\ncmp dx,364\njnz EndGame\n\tmov si,offset Defeat\n\tcall StringWrite\n\tmov si,offset NewGame\nEndGame:\ncall stringwrite\nNewGameKey:\n\tmov ah,0\n\tint 16h\n\tcmp al,'n'\njnz NewGameKey\n\nNewGame3ByteJump:\njmp StartMazeWrite\n\n\n\nGridCalcAndClear2:\n\tmov Dx,482\n\tdb 0ebh,3 ;jmp +3 - skip mov dx,324 instruction\nGridCalcAndClear:\n\tmov dx,324\n\tmov bh,0\n\tpush ax\n\tmov bl,ah\n\tshl bl,2\n\tadd bx,dx\n\tmov ah,0\n\tmov dx,320\n\tmul dx\n\tadd bx,ax\n\tmov byte [es:bx],' '\n\tpop ax\nRet\n\nStringWrite:\n\tCLD\n\tStringWrite2:\n\t\tmovsb\n\t\tinc di\n\t\tcmp byte [si],0\n\tjnz StringWrite2\nRet\ndefeat:\ndb 'Loss',0\n\norg 32206\nRandom3Bits:\n\tshr cx,3\n\tjnz Next3bits\n\t\tinc byte [7900]\n\t\tmov si,[7900]\n\t\tmov cx,[si]\n\tNext3bits:\n\tmov dl,cl\n\tand dl,7\nret\n\nTimeLabel:\nVictory:\ndb 'WIN'\nNewGame:\ndb ':n - New Game',0\n\norg 7dfeh\ndb 55h,0AAh\n", | |
"data": "M8CO0I7YuAC4jsCJxLQAzRqJFt4etnyJFtweuAMAzRC4EhHNELjbCbsEALkQDs0QvUIBsyAmxkYAIOiRAYD6AX8FJsZGAiDohAGA+gF/BibGhqAAIIPFBEt12oHFwACB/VgbfM4zwOhkAYD6A3wRgPoGfCJ0FDwAdCZI6AsBcyA8FHQc6AIBQHUWgPwAdBH+zOj6AHUKgPwfdAXo8AD+xD0UH3XCsADoJAGA+gN8EYD6BnwWdCA8AHQmSOjLAHMgPBR0HOjCAEB1FoD8AHQR/szougBzCoD8H3QF6LAA/sQ9FAB1wibHBkIBR1e9vhomx0YAAQNVtAK3ALoALc0QuLIKuWwBzRC0Ac0WdD20AM0WXYv1PHd1BIHuoAA8YXUCTk48c3UEgcagADxkdQJGRiaAPCB1DSbGRgAgJscEAQNWdAcmgDxHdBlVtADNGisW3h6B+mwBdAqLyrggCkHNEOuluAMAzRAz/77jfYH6bAF1Cb6zfegyAL7mfegsALQAzRY8bnX46YT+uuIB6wO6RAG3AFCK3MDjAgPatAC6QAH34gHDJsYHIFjD/KRHgDwAdfnDTG9zcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwekDdQr+BtweizbcHosMitGA4gfDV0lOOm4gLSBOZXcgR2FtZQAAAAAAAAAAAAAAVao=", | |
"help": "; compilation failed for me" | |
}, | |
"Atari Breakout x86": { | |
"link": "https://github.com/waternine9/bootsector-atari-breakout-x86", | |
"description": "'You can't break the blocks, but everything else is good'", | |
"asm": "[BITS 16]\n[ORG 0x7C00]\n\n;macros\n;definitions\n%define YELLOW 0x0E\n%define BLACK 0x00\n\n%define RECTANGLE_WIDTH 0x40\n%define RECTANGLE_WIDTH_HALF 0x20\n%define RECTANGLE_WIDTH_PLUS 0x51\n%define RECTANGLE_HEIGHT 0x10\n%define RECTANGLE_HEIGHT_PLUS 0x15\n\n\n;functions\n%macro CLS 0\n\txor di,di\n\tmov al, 0x0F\n\tmov cx, 0xFA00 ;framebuffer size\n\trepe stosb \n%endmacro \n\n%macro DRAW_RECTANGLE 0\n\txor dx, dx\n\t.ball_draw_loop_rect:\n\tpush dx\n\t\tmov word ax, 320\n\t\tmov word bx, [draw_y]\n\t\tadd word bx, dx\n\n\t\tmul bx\n\t\tadd word ax, [draw_x]\n\t\tmov di, ax\n\t\tmov al, YELLOW\n\t\tmov cx, RECTANGLE_WIDTH \n\t\trepe stosb\n\tpop dx\n\n\tinc dx \n\n\n\t\n\tcmp dx, RECTANGLE_HEIGHT\n\tjl .ball_draw_loop_rect\n%endmacro\n\n%macro DRAW_WALL_RECTANGLE 0\n\n\txor dx, dx\n\n\t\n\t.wall_draw_loop_rect:\n\tpush dx\n\t\tmov word ax, 320\n\t\tmov word bx, [draw_y]\n\t\tadd word bx, dx\n\t\tmul bx\n\t\tadd word ax, [draw_x]\n\t\tmov di, ax\n\t\tmov al, [draw_x]\n\t\tmov cx, RECTANGLE_WIDTH\n\t\t\n\t\trepe stosb\n\tpop dx\n\tinc dx\n\tcmp dx, RECTANGLE_HEIGHT\n\tjl .wall_draw_loop_rect\n%endmacro\n\n%macro DRAW_BALL 0\n\txor dx, dx\n\t.ball_draw_loop_ball:\n\tpush dx\n\t\tmov word ax, 320\n\n\t\tmov word bx, [ball_y]\n\t\tadd word bx, dx\n\t\tmul bx\n\t\tadd word ax, [ball_x]\n\t\tmov di, ax\n\t\tmov al, BLACK\n\t\t\n\t\tmov cx, 0x02\n\t\trepe stosb\n\tpop dx\n\tinc dx \n\n\tcmp dx, 0x02\n\tjl .ball_draw_loop_ball\n%endmacro\n\n%macro BALL_COLLISION 0\n\tmov ax, [ball_dx]\n\tmov bx, [ball_dy]\n\tmov cx, [ball_x]\n\tcmp cx, 0\n\t\n\tcmovl ax, [one]\n\tcmp cx, 320\n\tcmovg ax, [neg_one]\n\tmov cx, [ball_y]\n\tcmp cx, 0\n\tcmovl bx, [one]\n\tcmp cx, 200\n\tcmovg cx, [zero]\n\n\tmov [ball_y], cx\n\tmov [ball_dx], ax\n\tmov [ball_dy], bx\n%endmacro\n\n%macro BALL_PLAYER_COLLISION 0\n\tmov cx, [ball_dx]\n\tmov ax, [ball_y]\n\tmov bx, [player_y]\n\tadd ax, 0x02\n\tcmp ax, bx \n\tjl .continue_ball_player_collision\n\t\n\tmov ax, [ball_x]\n\tmov bx, [player_x]\n\tcmp ax, bx\n\tjl .continue_ball_player_collision\n\tadd bx, RECTANGLE_WIDTH\n\tcmp ax, bx\n\tjg .continue_ball_player_collision\n\t\n\tsub bx, RECTANGLE_WIDTH_HALF\n\n\t\n\tsub ax, bx\n\tjl .branch\n\n\tshr ax, 2\n\n\tjmp .branch_continue\n\t.branch:\n\n\tmov ax, -1\n\n\t.branch_continue:\n\t\n\tcmovz ax, [one]\n\tmov [ball_dx], ax\n\tmov word [ball_dy], word -1\n\n\n\t.continue_ball_player_collision:\n%endmacro\n\n%macro BALL_RECT_COLLISION 0\n\tmov cx, [ball_dx]\n\tmov dx, [ball_dy]\n\tmov ax, [ball_x]\n\tmov bx, [draw_x]\n\n\tcmp ax, bx\n\n\tcmove cx, [neg_one]\n\tjl .continue_ball_rect_collision\n\tadd bx, RECTANGLE_WIDTH\n\tcmp ax, bx\n\tcmove cx, [one]\n\tjg .continue_ball_rect_collision\n\tmov ax, [ball_y]\n\tmov bx, [draw_y]\n\tcmp ax, bx \n\tcmove dx, [neg_one]\n\tjl .continue_ball_rect_collision\n\tadd bx, RECTANGLE_HEIGHT\n\tcmp ax, bx \n\tcmove dx, [one]\n\t\n\tjg .continue_ball_rect_collision\n\tmov [ball_dx], cx\n\tmov [ball_dy], dx\n\n\t.continue_ball_rect_collision:\n%endmacro\n\n\n%macro WAIT_FOR_RTC 0\n\t;synchronizing game to real time clock (18.2 ticks per sec)\n\t.sync:\n\t\txor ah,ah\n\t\tsti\n\t\tint 0x1a ;returns the current tick count in dx\n\t\tcli\n\t\tcmp word [timer_current], dx\n\tje .sync ;reloop until new tick\n\t\tmov word [timer_current], dx ;save new tick value\n%endmacro\n\nstart:\ncli\nxor ax, ax\nmov ds, ax; setting data segment to zero\nmov ss, ax; setting up stack segment\nmov sp, 0x7C00 ;setting up stackpointer (just before the loaded bootsector)\nmov ax, 0xA000 ;beginning of the framebuffer\nmov es, ax; setting the extra segment for pixel drawing purposes\n\n;setting 320x200 256 colors graphics mode\nmov ax, 0x0013\nsti\nint 0x10 \ncli\n\nmain_gameloop:\n\tCLS \n\n\tin al, 0x60 ;reading current keyboard input\n\n\tcmp al, 0x1F ;Key S\n\tje .player1_input_w\n\t.player1_input_w_continue:\n\n\tcmp al, 0x20 ;Key D\n\tje .player1_input_s\n\t.player1_input_s_continue:\n\n\tjmp .continue\n\n\t.player1_input_w:\n\t\n\n\n\tmov ax, [player_x]\n\tsub ax, 4\n\tmov [player_x], ax\n\n\tjmp .continue\n\n\t.player1_input_s:\n\t\n\n\n\tmov ax, [player_x]\n\tadd ax, 4\n\n\tmov [player_x], ax\n\t\n\n\t.continue:\n\tmov dx, [player_x]\n\tmov [draw_x], dx\n\tmov dx, [player_y]\n\n\n\tmov [draw_y], dx\n\n\t\n\tDRAW_RECTANGLE\n\n\tBALL_COLLISION\n\n\tBALL_PLAYER_COLLISION\n\t\n\tmov word [draw_x], 10\n\n\tmov word [draw_y], 10 \n\n\txor ax, ax\n\n\t.scan:\n\t\n\tBALL_RECT_COLLISION\n\tDRAW_WALL_RECTANGLE\n\t\n\n\t.scan_continue:\n\t\n\tmov ax, [draw_y]\n\tadd ax, RECTANGLE_HEIGHT_PLUS\n\tmov [draw_y], ax \n\tcmp ax, 0x5E\n\tjl .scan\n\n\tmov word [draw_y], 10\n\n\tmov ax, [draw_x]\n\tadd ax, RECTANGLE_WIDTH_PLUS \n\tmov [draw_x], ax \n\tcmp ax, 0x14E\n\tjl .scan\n\n\tmov ax, [ball_x]\n\tadd ax, [ball_dx]\n\tmov [ball_x], ax\n\tmov ax, [ball_y]\n\tadd ax, [ball_dy]\n\n\n\tmov [ball_y], ax\n\tDRAW_BALL\n\tWAIT_FOR_RTC\n\njmp main_gameloop\n\n\n\n\n.data:\ntick dw 0\nplayer_x dw 100\nplayer_y dw 180\n\nwall_x dw 0\nwall_y dw 0\n\ndraw_x dw 0\ndraw_y dw 0\n\n\nball_x dw 160\n\nball_y dw 100\nball_dx dw 1\nball_dy dw 1\nzero dw 0\none dw 1\nneg_one dw -1\n\ntimer_current dw 0\n\nMARK dq 0xFFFFFFFF\n\n%assign sizeOfProgram $ - $$\n%warning Size of the program: sizeOfProgram bytes\n\n;padding to fill up the bootsector\ntimes 510 - ($-$$) db 0\n\n;bootsector marker\ndw 0xAA55\n", | |
"data": "+jHAjtiO0LwAfLgAoI7AuBMA+80Q+jH/sA+5APrzquRgPB90BjwgdA3rFKHWfYPoBKPWfesJodZ9g8AEo9Z9ixbWfYkW3n2LFth9iRbgfTHSUrhAAYse4H0B0/fjAwbefYnHsA65QADzqlpCg/oQfOCh5n2LHuh9iw7ifYP5AA9MBux9gflAAQ9PBu59iw7kfYP5AA9MHux9gfnIAA9PDup9iQ7kfaPmfYke6H2LDuZ9oeR9ix7YfYPAAjnYfC+h4n2LHtZ9Odh8JIPDQDnYfx2D6yAp2HwFwegC6wO4//8PRAbsfaPmfccG6H3//8cG3n0KAMcG4H0KADHAiw7mfYsW6H2h4n2LHt59OdgPRA7ufXwwg8NAOdgPRA7sfX8koeR9ix7gfTnYD0QW7n18FIPDEDnYD0QW7H1/CIkO5n2JFuh9MdJSuEABix7gfQHT9+MDBt59iceg3n25QADzqlpCg/oQfN+h4H2DwBWj4H2D+F58h8cG4H0KAKHefYPAUaPefT1OAQ+Mcf+h4n0DBuZ9o+J9oeR9AwbofaPkfTHSUrhAAYse5H0B0/fjAwbifYnHsAC5AgDzqlpCg/oCfOAw5PvNGvo5FvB9dPSJFvB96UL+AABkALQAAAAAAAAAAACgAGQAAQABAAAAAQD//wAA/////wAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"x86 pong": { | |
"link": "https://github.com/tevoran/x86-Pong", | |
"description": "A pong clone with some basic CPU AI for tracking the ball", | |
"asm": "[BITS 16]\n[ORG 0x7C00]\n\n;macros\n;definitions\n%define YELLOW 0x0E\n%define BLACK 0x00\n\n%define RES_X 320\n%define RES_Y 200\n%define PLAYER1_X 20\n%define PLAYER2_X 290 \n%define PLAYER_WIDTH 0x06\n%define PLAYER_WIDTH_HALF 0x03\n%define PLAYER_HEIGHT 0x20\n%define PLAYER_LOWEST 168\n%define PLAYER_HIGHEST 0xFFFF\n%define BALL_WIDTH 3\n%define BALL_HEIGHT 3\n%define BALL_Y_RESOLUTION 10\n\n\n;functions\n%macro CLS 0\n\txor di,di\n\tmov al, BLACK\n\tmov cx, 0xFA00 ;framebuffer size\n\trepe stosb \n%endmacro \n\n%macro DRAW_BALL 0\n\txor ax,ax\n\tmov word [i], ax\n\tball_draw_loop:\n\t\tmov word ax, RES_X\n\t\tmov word bx, [ball_y]\n\t\tadd word bx, [i]\n\t\tmul bx\n\t\tadd word ax, [ball_x]\n\t\tmov di, ax\n\t\tmov al, YELLOW\n\t\tmov cx, BALL_WIDTH\n\t\trepe stosb\n\n\tmov word ax, [i]\n\tinc ax \n\tmov word [i], ax\n\tcmp ax, BALL_HEIGHT\n\tjl ball_draw_loop\n%endmacro\n\n%macro UPDATE_BALL_LOCATION 0\n\t;X - axis\n\tmov word ax, [ball_x]\n\tadd word ax, [ball_dx]\n\tmov word [ball_x], ax\n\n\t;Y - axis\n\tmov ax, word [ball_y]\n\tmov bx, word [ball_dy]\n\tadd ax, bx\n\tmov word [ball_y], ax\n\t;fwait\n\t;\tfld dword [ball_y_float] ;load current ball y-value\n\t;\tfadd dword [ball_dy_float] ;add delta value\n\t;\tfst dword [ball_y_float] ;save new ball y-value\n\t;\tfld dword [ball_y_float] ;load ball y-value again\n\t;\tfistp word [ball_y] ;convert ball y-value to integer and save it into memory\n\n%endmacro\n\n%macro WAIT_FOR_RTC 0\n\t;synchronizing game to real time clock (18.2 ticks per sec)\n\t.sync:\n\t\txor ah,ah\n\t\tsti\n\t\tint 0x1a ;returns the current tick count in dx\n\t\tcli\n\t\tcmp word [timer_current], dx\n\tje .sync ;reloop until new tick\n\t\tmov word [timer_current], dx ;save new tick value\n%endmacro\n\n%macro ENEMY_AI 0\n\txor cx,cx\n\n\tmov word ax, [player2_y]\n\tmov word bx, [ball_y]\n\tcmp ax, bx\n\tjae .enemy_ai_below\n\t\t;if ball is above the enemy\n\t\tmov cx, 1\n\t\tinc ax\n\t\tjmp .enemy_ai_done\n\n\t;if ball is below the enemy\n\t.enemy_ai_below:\n\tmov cx, -1\n\tdec ax\n\t\n\t.enemy_ai_done:\n\tmov word [player2_y], ax\n\tmov word [player2_dy], cx\n%endmacro\n;\tCODE BEGIN\n;\t\n;\njmp 0x00:start ;setting code segment\n\nstart:\ncli\nxor ax, ax\nmov ds, ax; setting data segment to zero\nmov ss, ax; setting up stack segment\nmov sp, 0x7C00 ;setting up stackpointer (just before the loaded bootsector)\nmov ax, 0xA000 ;beginning of the framebuffer\nmov es, ax; setting the extra segment for pixel drawing purposes\n\n;setting 320x200 256 colors graphics mode\nmov ax, 0x0013\nsti\nint 0x10 \ncli\n\n;initializing keyboard\n;wait until keyboard is ready\nkeyboard_check.loop:\nxor ax,ax\nin al,0x64\nbt ax, 1 ;test if buffer is still full\njc keyboard_check.loop\n\n;activating keyboard\n\tmov al, 0xF4\n\tout 0x60, al\n\n;main game loop\nmain_loop:\n\tCLS\n\n\t;drawing stuff\n\tmov si, player1_y\n\tmov dx, word [player1_x]\n\tcall draw_player\n\n\tmov si, player2_y\n\tmov dx, word [player2_x]\n\tcall draw_player\n\n\tDRAW_BALL\n\n\t;updates\n\tUPDATE_BALL_LOCATION\n\n\t;get keyboard input\n\tin al, 0x60 ;reading current keyboard input\n\n\txor cx, cx ;resetting player y-speed variable\n\tcmp al, 0x11 ;Key W\n\tje .player1_input_w\n\t.player1_input_w_continue:\n\n\tcmp al, 0x1F ;Key S\n\tje .player1_input_s\n\t.player1_input_s_continue:\n\n\tmov word [player1_dy], cx ;writing player y-speed\n\n\t;collisions\n\tmov si, player1_y\n\tcall player_screen_collision\n\tmov si, player2_y\n\tcall player_screen_collision\n\n\n\t;ball outside of screen\n\t;horizontal\n\tmov word ax, [ball_x]\n\tmov bx, RES_X\n\tcmp ax, bx\n\tja .ball_out_of_screen\n\t.ball_out_of_screen_continue:\n\n\t;vertical\n\tmov word ax, [ball_y]\n\tmov bx, RES_Y\n\tcmp ax, bx\n\tjna .ball_out_of_screen_vertical_continue\n\t\tcall reflect_ball_y\n\t.ball_out_of_screen_vertical_continue:\n\n\t;player ball collision\n\tmov cx, 1 \n\tcall player_ball_check ;player 1 paddle collision\n\tmov cx, 2\n\tcall player_ball_check ;player 2 paddle collision\n\t\n\tENEMY_AI\n\n\t;waiting for the next frame\tto start\n\tWAIT_FOR_RTC\n\n\njmp main_loop\n\n;INPUT IFS\n;KEY W\n.player1_input_w:\nmov word bx, [player1_y]\ndec bx\nmov word [player1_y], bx\nmov cx, -1 ;set player y direction\njmp .player1_input_w_continue\n\n;KEY S\n.player1_input_s:\nmov word bx, [player1_y]\ninc bx\nmov word [player1_y], bx\nmov cx, 1 ;set player y direction\njmp .player1_input_s_continue\n\n;ball collision ifs\n;ball out of screen\n.ball_out_of_screen:\nmov ax, bx\nshr ax,1 ;division by two\nmov word [ball_x], ax\nxor ax, ax\nmov word [ball_dy], ax ;reset y-speed\njmp .ball_out_of_screen_continue\n\n\n.functions:\n;drawing player\n;si=adress of the player paddle's y-position\n;dx=player paddle's x-position\ndraw_player:\n\txor ax,ax\n\tmov word [i], ax ;reset loop variable\n\tplayer_draw_loop:\n\t\t;calculating framebuffer offset\n\t\tmov ax, RES_X\n\t\tmov word bx, [si] ;loading y-position\n\t\tadd word bx, [i]\n\t\t\tpush dx ;save dx because the multiplication breaks it\n\t\tmul bx\n\t\t\tpop dx\n\t\tadd ax, dx ;adding player x-position\n\t\tmov di, ax ;writing the framebuffer offset for the actual writing purposes\n\t\tmov al, YELLOW ;color code within the 256 color palette\n\t\tmov cx, PLAYER_WIDTH ;number of repitions in x direction\n\t\trepe stosb ;write the line of player paddle\n\n\t;incrementing loop counting variable\n\tmov word ax, [i] ;reading loop variable\n\tinc ax ;incrementing loop variable\n\tmov word [i], ax ;writing loop variable\n\tcmp ax, PLAYER_HEIGHT ;check if loop counter is smaller than PLAYER_HEIGHT\n\tjl player_draw_loop\t;jump if less\nret\n\n;player screen collision\n;si=address of the player1/2 y-value\nplayer_screen_collision:\n\t;keeping player 1 inside the screen\n\tmov word ax, [si]\n\t;if player is too low on the screen set him a bit higher\n\tcmp ax, PLAYER_LOWEST\n\tjae .player_screen_collision_too_low\n\t.player_screen_collision_too_low_continue:\n\n\t;if player is too high on the screen set him a bit lower\n\tcmp ax, 0\n\tje .player_screen_collision_too_high\n\t.player_screen_collision_too_high_continue:\n\tmov word [si], ax\nret\n\n;if player is too low on the screen\n.player_screen_collision_too_low:\nmov ax, PLAYER_LOWEST\njmp .player_screen_collision_too_low_continue\n\n;if player is too high on the screen\n.player_screen_collision_too_high:\nmov ax, 1\njmp .player_screen_collision_too_high_continue\n\n\n;player_ball_collision\n;cx=1 player one collision check\n;else player two collision check\nplayer_ball_check:\nmov word ax, [ball_x]\ncmp cx, 1\n\tcmove word bx, [player1_x]\n\tcmovne word bx, [player2_x]\nadd bx, PLAYER_WIDTH_HALF\ncmp ax, bx\njne .player_y_ball_check_continue\n\t;check if ball is below the top edge of the paddle\n\tmov word ax, [ball_y]\n\tcmp cx, 1\n\t\tcmove word bx, [player1_y]\n\t\tcmovne word bx, [player2_y]\n\tcmp ax, bx\n\tjl .player_y_ball_check_continue\n\n\t;check if ball is above the bottom edge of the paddle\n\tadd bx, PLAYER_HEIGHT\n\tcmp ax, bx\n\tjae .player_y_ball_check_continue\n\n\t\t;reflect ball\n\t\tmov word ax, [ball_dx]\n\t\tmov bx, -1\n\t\tmul bx\n\t\tmov word [ball_dx], ax\n\n\t\t;add the y-speed of the paddle\n\t\tcmp cx, 1 ;if player1\n\t\t\tcmove ax, word [player1_dy]\n\t\t\tcmovne ax, word [player2_dy]\n\t\tadd ax, word [ball_dy]\n\t\tmov word [ball_dy], ax\n\n.player_y_ball_check_continue:\nret\n\nreflect_ball_y:\n\tmov word ax, [ball_dy]\n\tmov bx, -1\n\tmul bx\n\tmov word [ball_dy], ax\nret\n\n.data:\ntimer_current dw 0\ni dw 0 ;loop variable\nplayer1_x dw 20\nplayer1_y dw 80\nplayer2_x dw 290\nplayer2_y dw 80\nplayer1_dy dw 0\nplayer2_dy dw 0\nball_x dw 100\nball_y dw 85\nball_dy dw 0\nball_dx dw -1\n\nMARK dq 0xFFFFFFFF\n;padding to fill up the bootsector\ntimes 510 - ($-$$) db 0\n\n;bootsector marker\ndw 0xAA55\n\n; fill up to make a floppy image\ntimes 1474560 - ($-$$) db 0\n", | |
"data": "6gV8AAD6McCO2I7QvAB8uACgjsC4EwD7zRD6McDkZA+64AFy9rD05mAx/7AAuQD686q+yH2LFsZ96OUAvsx9ixbKfejbADHAo8R9uEABix7UfQMexH334wMG0n2Jx7AOuQMA86qhxH1Ao8R9g/gDfNqh0n0DBth9o9J9odR9ix7WfQHYo9R95GAxyTwRdGg8H3RyiQ7Ofb7IfeiuAL7MfeioAKHSfbtAATnYd2ah1H27yAA52HYD6PwAuQEA6KQAuQIA6J4AMcmhzH2LHtR9OdhzBrkBAEDrBLn//0ijzH2JDtB9MOT7zRr6ORbCfXT0iRbCfeky/4seyH1LiR7Ifbn//+uKix7IfUOJHsh9uQEA64CJ2NHoo9J9McCj1n3rjDHAo8R9uEABixwDHsR9UvfjWgHQicewDrkGAPOqocR9QKPEfYP4IHzcw4sEPagAcwiD+AB0CIkEw7ioAOvzuAEA6/Oh0n2D+QEPRB7GfQ9FHsp9g8MDOdh1OqHUfYP5AQ9EHsh9D0UezH052Hwmg8MgOdhzH6HYfbv///fjo9h9g/kBD0QGzn0PRQbQfQMG1n2j1n3DodZ9u///9+Oj1n3DAAAAABQAUAAiAVAAAAAAAGQAVQAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVao=", | |
"help": "" | |
}, | |
"Rainbow Snake": { | |
"link": "https://gitlab.com/Palma3000/rainbowsnakeos", | |
"description": "A very fast paced snake game in graphics mode with a colorful snake. It's a quickly winnable game with SNAKE_MAX_LENGTH = 0xF, so don't give up.", | |
"asm": "; ______ _ _ _____ _ _____ _____ \n; | ___ \\ (_) | | / ___| | | | _ |/ ___|\n; | |_/ / __ _ _ _ __ | |__ ___ __ __\\ `--. _ __ __ _ | | __ ___ | | | |\\ `--. \n; | / / _` || || '_ \\ | '_ \\ / _ \\\\ \\ /\\ / / `--. \\| '_ \\ / _` || |/ // _ \\| | | | `--. \\\n; | |\\ \\| (_| || || | | || |_) || (_) |\\ V V / /\\__/ /| | | || (_| || <| __/\\ \\_/ //\\__/ /\n; \\_| \\_|\\__,_||_||_| |_||_.__/ \\___/ \\_/\\_/ \\____/ |_| |_| \\__,_||_|\\_\\\\___| \\___/ \\____/ \n; \n;\n; DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n; Version 2, December 2004\n; \n;Copyright (C) 2023 Palma3000 <[email protected]>\n;\n;Everyone is permitted to copy and distribute verbatim or modified\n;copies of this license document, and changing it is allowed as long\n;as the name is changed.\n; \n; DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n; TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n;\n; 0. You just DO WHAT THE FUCK YOU WANT TO.\n;\n\nformat binary\n\n\n; Macroinstructions for defining data structures\n\nmacro struct name\n { virtual at 0\n fields@struct equ name\n match child parent, name \\{ fields@struct equ child,fields@\\#parent \\}\n sub@struct equ\n struc db [val] \\{ \\common define field@struct .,db,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc dw [val] \\{ \\common define field@struct .,dw,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc du [val] \\{ \\common define field@struct .,du,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc dd [val] \\{ \\common define field@struct .,dd,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc dp [val] \\{ \\common define field@struct .,dp,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc dq [val] \\{ \\common define field@struct .,dq,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc dt [val] \\{ \\common define field@struct .,dt,<val>\n\t\t\t fields@struct equ fields@struct,field@struct \\}\n struc rb count \\{ define field@struct .,db,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n struc rw count \\{ define field@struct .,dw,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n struc rd count \\{ define field@struct .,dd,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n struc rp count \\{ define field@struct .,dp,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n struc rq count \\{ define field@struct .,dq,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n struc rt count \\{ define field@struct .,dt,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro db [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,db,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro dw [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,dw,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro du [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,du,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro dd [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,dd,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro dp [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,dp,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro dq [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,dq,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro dt [val] \\{ \\common \\local anonymous\n\t\t define field@struct anonymous,dt,<val>\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rb count \\{ \\local anonymous\n\t\t define field@struct anonymous,db,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rw count \\{ \\local anonymous\n\t\t define field@struct anonymous,dw,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rd count \\{ \\local anonymous\n\t\t define field@struct anonymous,dd,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rp count \\{ \\local anonymous\n\t\t define field@struct anonymous,dp,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rq count \\{ \\local anonymous\n\t\t define field@struct anonymous,dq,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro rt count \\{ \\local anonymous\n\t\t define field@struct anonymous,dt,count dup (?)\n\t\t fields@struct equ fields@struct,field@struct \\}\n macro union \\{ fields@struct equ fields@struct,,union,<\n\t\t sub@struct equ union \\}\n macro struct \\{ fields@struct equ fields@struct,,substruct,<\n\t\t sub@struct equ substruct \\} }\n\nmacro ends\n { match , sub@struct \\{ restruc db,dw,du,dd,dp,dq,dt\n\t\t\t restruc rb,rw,rd,rp,rq,rt\n\t\t\t purge db,dw,du,dd,dp,dq,dt\n\t\t\t purge rb,rw,rd,rp,rq,rt\n\t\t\t purge union,struct\n\t\t\t match name tail,fields@struct, \\\\{ if $\n\t\t\t\t\t\t\t display 'Error: definition of ',\\\\`name,' contains illegal instructions.',0Dh,0Ah\n\t\t\t\t\t\t\t err\n\t\t\t\t\t\t\t end if \\\\}\n\t\t\t match name=,fields,fields@struct \\\\{ fields@struct equ\n\t\t\t\t\t\t\t make@struct name,fields\n\t\t\t\t\t\t\t define fields@\\\\#name fields \\\\}\n\t\t\t end virtual \\}\n match any, sub@struct \\{ fields@struct equ fields@struct> \\}\n restore sub@struct }\n\nmacro make@struct name,[field,type,def]\n { common\n local define\n define equ name\n forward\n local sub\n match , field \\{ make@substruct type,name,sub def\n\t\t define equ define,.,sub, \\}\n match any, field \\{ define equ define,.#field,type,<def> \\}\n common\n match fields, define \\{ define@struct fields \\} }\n\nmacro define@struct name,[field,type,def]\n { common\n virtual\n db `name\n load initial@struct byte from 0\n if initial@struct = '.'\n display 'Error: name of structure should not begin with a dot.',0Dh,0Ah\n err\n end if\n end virtual\n local list\n list equ\n forward\n if ~ field eq .\n name#field type def\n sizeof.#name#field = $ - name#field\n else\n label name#.#type\n rb sizeof.#type\n end if\n local value\n match any, list \\{ list equ list, \\}\n list equ list <value>\n common\n sizeof.#name = $\n restruc name\n match values, list \\{\n struc name value \\\\{ \\\\local \\\\..base\n match any, fields@struct \\\\\\{ fields@struct equ fields@struct,.,name,<values> \\\\\\}\n match , fields@struct \\\\\\{ label \\\\..base\n forward\n match , value \\\\\\\\{ field type def \\\\\\\\}\n match any, value \\\\\\\\{ field type value\n\t\t\t if ~ field eq .\n\t\t\t rb sizeof.#name#field - ($-field)\n\t\t\t end if \\\\\\\\}\n common label . at \\\\..base \\\\\\}\n \\\\}\n macro name value \\\\{\n match any, fields@struct \\\\\\{ \\\\\\local anonymous\n\t\t\t\t fields@struct equ fields@struct,anonymous,name,<values> \\\\\\}\n match , fields@struct \\\\\\{\n forward\n match , value \\\\\\\\{ type def \\\\\\\\}\n match any, value \\\\\\\\{ \\\\\\\\local ..field\n\t\t\t ..field = $\n\t\t\t type value\n\t\t\t if ~ field eq .\n\t\t\t rb sizeof.#name#field - ($-..field)\n\t\t\t end if \\\\\\\\}\n common \\\\\\} \\\\} \\} }\n\nmacro enable@substruct\n { macro make@substruct substruct,parent,name,[field,type,def]\n \\{ \\common\n\t\\local define\n\tdefine equ parent,name\n \\forward\n\t\\local sub\n\tmatch , field \\\\{ match any, type \\\\\\{ enable@substruct\n\t\t\t\t\t make@substruct type,parent,sub def\n\t\t\t\t\t purge make@substruct\n\t\t\t\t\t define equ define,.,sub, \\\\\\} \\\\}\n\tmatch any, field \\\\{ define equ define,.\\#field,type,<def> \\\\}\n \\common\n\tmatch fields, define \\\\{ define@\\#substruct fields \\\\} \\} }\n\nenable@substruct\n\nmacro define@union parent,name,[field,type,def]\n { common\n virtual at parent#.#name\n forward\n if ~ field eq .\n virtual at parent#.#name\n parent#field type def\n sizeof.#parent#field = $ - parent#field\n end virtual\n if sizeof.#parent#field > $ - parent#.#name\n rb sizeof.#parent#field - ($ - parent#.#name)\n end if\n else\n virtual at parent#.#name\n label parent#.#type\n type def\n end virtual\n label name#.#type at parent#.#name\n if sizeof.#type > $ - parent#.#name\n rb sizeof.#type - ($ - parent#.#name)\n end if\n end if\n common\n sizeof.#name = $ - parent#.#name\n end virtual\n struc name [value] \\{ \\common\n label .\\#name\n last@union equ\n forward\n match any, last@union \\\\{ virtual at .\\#name\n\t\t\t field type def\n\t\t\t end virtual \\\\}\n match , last@union \\\\{ match , value \\\\\\{ field type def \\\\\\}\n\t\t\t match any, value \\\\\\{ field type value \\\\\\} \\\\}\n last@union equ field\n common rb sizeof.#name - ($ - .\\#name) \\}\n macro name [value] \\{ \\common \\local ..anonymous\n\t\t\t ..anonymous name value \\} }\n\nmacro define@substruct parent,name,[field,type,def]\n { common\n virtual at parent#.#name\n forward\n if ~ field eq .\n parent#field type def\n sizeof.#parent#field = $ - parent#field\n else\n label parent#.#type\n rb sizeof.#type\n end if\n common\n sizeof.#name = $ - parent#.#name\n end virtual\n struc name value \\{\n label .\\#name\n forward\n match , value \\\\{ field type def \\\\}\n match any, value \\\\{ field type value\n\t\t\t if ~ field eq .\n\t\t\t rb sizeof.#parent#field - ($-field)\n\t\t\t end if \\\\}\n common \\}\n macro name value \\{ \\local ..anonymous\n\t\t\t..anonymous name \\} }\n\n\nuse16\norg 0x0\n\nSTATE_STOP = 0x0\nSTATE_UP = 0x1\nSTATE_DOWN = 0x2\nSTATE_LEFT = 0x3\nSTATE_RIGHT = 0x4\nSTATE_GAMEOVER = 0x5\nSTATE_WIN = 0x6\n\nKEY_UP = 0x4800\nKEY_DOWN = 0x5000\nKEY_LEFT = 0x4B00\nKEY_RIGHT = 0x4D00\nKEY_ESC = 0x011B\n\nSNAKE_MAX_LENGTH = 0xF\n\nSCREEN_WIDTH = 320\nSCREEN_HEIGHT = 200\n\nstruct VARS \n len rw 1\n coords rw SNAKE_MAX_LENGTH\n food rw 1\n state rb 1\n res rb 1\nends\n\n\nentry_point:\n cld\n cmp Byte [cs:0], 0xCD\n jnz short .dos\n mov ax, cs\n mov ds, ax\n mov es, ax\n cli\n mov ss, ax\n mov sp, ax\n sti\n.dos: \n mov ax, 0x13\n int 0x10\n sub sp, sizeof.VARS\n mov bp, sp\nreset:\n mov di, bp\n xor ax, ax\n inc ax\n stosw\n mov ax, 0x505\n stosw\n mov Word [bp + VARS.food], 0x1010\n mov Byte [bp + VARS.state], STATE_STOP\n xor ax, ax\n call draw\nmain_loop:\n mov al, [bp + VARS.state]\n cmp al, STATE_GAMEOVER\n jz short .gameover\n cmp al, STATE_WIN\n jz short .win\n call move\n mov al, [bp + VARS.state]\n cmp al, STATE_GAMEOVER\n jae short main_loop\n mov ah, 0x1\n int 0x16\n jz short main_loop\n.read_key:\n xor ah, ah \n int 0x16\n xchg dx, ax\n call @f\n.key_table:\n dw KEY_UP, (STATE_DOWN shl 0x8) + STATE_UP\n dw KEY_DOWN, (STATE_UP shl 0x8) + STATE_DOWN\n dw KEY_LEFT, (STATE_RIGHT shl 0x8) + STATE_LEFT\n dw KEY_RIGHT, (STATE_LEFT shl 0x8) + STATE_RIGHT\n dw KEY_ESC, (0xFF shl 0x8) + STATE_GAMEOVER\n dw 0\n@@:\n pop si\n mov bx, Word [bp]\n.check_loop:\n lodsw\n test ax, ax\n jz short main_loop\n mov di, ax\n lodsw\n cmp di, dx\n jnz short .check_loop\n dec bx\n je .set_state\n cmp Byte [bp + VARS.state], ah\n je short main_loop\n.set_state:\n mov Byte [bp + VARS.state], al\n jmp short main_loop\n.gameover:\n call @f\n.str_gameover db 'GAME OVER'\nlen_str_gameover = $-.str_gameover\n@@:\n push len_str_gameover\n jmp short .exit\n.win:\n call @f\n .str_win db 'U WIN'\n len_str_win = $-.str_win\n@@:\n push len_str_win\n.exit:\n mov ax, 0x3\n int 0x10\n mov ax, 0x1300\n mov bx, 0x2\n pop cx\n pop bp\n mov dx, 0xA20\n int 0x10\n xor ax, ax\n int 0x16\n xor bx, bx\n cmp Byte [cs:bx], 0xCD\n jz .dos\n db 0x0ea, 0x00, 0x00, 0xff, 0xff\n.dos:\n xor ax, ax\n int 0x20\n\n\nmove:\n pusha\n mov si, bp\n lodsw\n dec ax\n shl ax, 1\n add si, ax\n mov di, si\n lodsw\n xchg cx, ax\n mov al, Byte [bp + VARS.state]\n cmp al, STATE_STOP\n jz .down\n cmp al, STATE_UP\n jnz @f\n test ch, ch\n je short .gameover\n dec ch\n jmp short .process\n@@:\n cmp al, STATE_DOWN\n jnz @f\n cmp ch, SCREEN_HEIGHT/4 - 1\n jae short .gameover\n inc ch\n jmp short .process\n@@:\n cmp al, STATE_LEFT\n jnz @f\n test cl, cl\n je short .gameover\n dec cl\n jmp short .process\n@@:\n cmp al, STATE_RIGHT\n jnz short .gameover\n cmp cl, SCREEN_WIDTH/4 - 1\n jae short .gameover\n inc cl\n.process:\n call check_snake_block\n jz short .gameover\n cmp cx, Word [bp + VARS.food]\n jnz short .not_food\n cmp Word [bp], SNAKE_MAX_LENGTH\n jae short .win\n inc Word [bp]\n mov Word [di+2], cx\n mov ax, (SCREEN_WIDTH/4)-1\n call get_random\n inc ax\n mov Byte [bp + VARS.food], al\n mov ax, (SCREEN_HEIGHT/4)-1\n call get_random\n inc ax\n mov Byte [bp + VARS.food + 1], al\n xor ax, ax\n jmp short .draw\n.not_food:\n mov si, bp\n lodsw\n mov dx, ax\n push Word [si]\n.shift_loop:\n dec dx\n jz short .end_shift\n mov ax, Word [si+0x2]\n mov Word [si], ax\n lodsw\n jmp short .shift_loop\n.end_shift:\n mov Word [si], cx\n pop ax\n.draw:\n call draw\n jmp short .down\n.gameover:\n mov al, STATE_GAMEOVER\n jmp short .set_state\n.win:\n mov al, STATE_WIN\n.set_state:\n mov Byte [bp + VARS.state], al\n.down:\n popa\n ret\n\n \nget_random:\n push dx\n push cx\n push ax\n xor ax, ax\n int 0x1A\n pop cx\n xchg ax, dx\n xor dx, dx\n div cx\n xchg ax, dx\n pop cx\n pop dx\n ret\n\n\ndraw_snake_rect:\n pusha\n mov si, 320\n xor dx, dx\n xchg dl, ah\n xor ah, ah\n shl dx, 0x2\n shl ax, 0x2\n push ax\n xchg dx, ax\n mul si\n pop dx\n add ax, dx\n xchg di, ax\n mov dx, 0x4\n.draw_line:\n push di\n mov cx, 0x4\n mov al, bl\n rep stosb\n pop di\n add di, si\n dec dl\n jnz short .draw_line\n popa\n ret\n\n\ncheck_snake_block:\n pusha\n mov di, bp\n mov ax, cx\n mov cx, Word [di]\n inc di\n inc di\n repne scasw\n popa\n ret\n\n\ndraw:\n push es\n pusha\n push 0xA000\n pop es\n push ax\n mov dx, 3DAh\n.l1:\n in al,dx\n and al,08h\n jnz short .l1\n.l2:\n in al,dx\n and al,08h\n jz short .l2\n mov si, bp\n lodsw\n mov cx, ax\n mov bx, cx\n inc bx\n.draw_snake_loop:\n lodsw\n dec bx\n call draw_snake_rect\n dec cx\n jnz short .draw_snake_loop\n.draw_food:\n mov ax, Word [bp + VARS.food]\n mov bl, 0xA\n call draw_snake_rect\n pop ax\n test ax, ax\n jz @f\n xor bx, bx\n call draw_snake_rect\n@@:\n xor cx, cx\n mov dx, 0x7000\n mov ah, 0x86\n int 0x15\n popa\n pop es\n ret\n \n \nrb 0x200-2-$\ndw 0xAA55\n", | |
"data": "/C6APgAAzXUMjMiO2I7A+o7QicT7uBMAzRCD7CSJ5YnvMcBAq7gFBavHRiAQEMZGIgAxwOh6AYpGIjwFdE88BnRb6IkAikYiPAVz67QBzRZ05TDkzRaS6BYAAEgBAgBQAgEASwMEAE0EAxsBBf8AAF6LXgCthcB0vonHrTnXdfRLdAU4ZiJ0r4hGIuuq6AkAR0FNRSBPVkVSagnrCugFAFUgV0lOagW4AwDNELgAE7sCAFlduiAKzRAxwM0WMdsugD/NdAXqAAD//zHAzSBgie6tSNHgAcaJ962RikYiPAAPhIEAPAF1CITtdHD+zeskPAJ1CYD9MXNj/sXrFzwDdQiEyXRX/snrCzwEdU+A+U9zSv7B6I0AdEM7TiB1JIN+AA9zPP9GAIlNArhPAOg3AECIRiC4MQDoLQBAiEYhMcDrFYnurYnC/zRKdAiLRAKJBK3r9YkMWOhXAOsJsAXrArAGiEYiYcNSUVAxwM0aWZIx0vfxkllaw2C+QAEx0obUMOTB4gLB4AJQkvfmWgHQl7oEAFe5BACI2POqXwH3/sp18WHDYInviciLDUdH8q9hwwZgaACgB1C62gPsJAh1++wkCHT7ie6ticGJy0OtS+im/0l1+ItGILMK6Jv/WIXAdAUx2+iR/zHJugBwtIbNFWEHwwAAAAAAAAAAAAAAVao=", | |
"help": "; didnt compile for me" | |
}, | |
"Code Golf": { | |
"link": "https://github.com/taylor-hartman/codegolf", | |
"description": "A Golf game with a few challenging levels and a winnable state. This game is equally a game of skill as it is a puzzle game. This is an excellent and fun game to play.", | |
"asm": "[ORG 0x7c00]\n\n;memory offsets\nball_x: equ 0\nball_y: equ 2\nball_xs: equ 4\nball_ys: equ 6\nxs_hold: equ 8\nys_hold: equ 10\nlevel: equ 12\nstrokes: equ 14\n\n;irdk if this is necissary\n;SETUP STACK\n;xor ax, ax\n;mov ss, ax\n;mov sp, 0x9c00\n;mov bp, sp\n\nxor bp, bp ;bp is zero from now on\n\n;point es to video memory\nmov ax, 0xA000\nmov es, ax\n;switch to video mode 0x13\ncbw ;mov ah, 0\nmov al, 0x13\nint 0x10\n\n;screen len is 320x200\n;colors: https://en.wikipedia.org/wiki/Mode_13h\n\nmov word [bp+level], -1 ;level is inc-ed to 0 directly after\n\nlevel_start:\n inc word [bp+level]\n\nstart:\n\tmov word [bp+ball_x], 55\n\tmov word [bp+ball_y], 30\n mov word [bp+strokes], bp\nreset:\n ;xor ax, ax ;less space than moving 0 four times because 0s are words here\n mov word [bp+ball_xs], bp\n\tmov word [bp+ball_ys], bp\n\tmov word [bp+xs_hold], bp\n\tmov word [bp+ys_hold], bp\ntimer_reset:\n xor dx, dx\n\nmain_loop: ;this is looped after every delay\nclear_screen:\n\txor al, al ;set color to black\n\tmov cx, 320*200\n\txor di, di\n rep stosb\n\n;each level is comprised of multiple series\n;each series is defined by a start point and a list of line lengths\n;lines are drawn in alternating order horizontal -> vertical -> horiztonal ...\n;series start with alternating line types horizontal -> vertical -> horiztonal ...\n;For the level drawing loop:\n;2 series are run (a horizontal and a vertical), then we move to the next point\n;we continue this trend until we have run out of points in the given level\n;at this point we offset the starting point and repeat 5x to make the lines thicc\ndraw_level: ;this is run once per main loop\n push dx\n mov cx, 5 ;this is the thiccness of the level lines\n xor bx, bx\ndraw_level_loop_outer:\n push bx \n mov bx, [bp+level]\n movzx dx, byte [bx+point_offsets] ;dx is the point_offset for this level\n pop bx\n push cx\n mov si, len_offsets ;si is address of len_offsets\n add si, word [bp+level] ;si is the address of the current level's len_offset\n movzx si, byte [si] ; si is the current level offset\n add si, lens ; si is address of begining of list of lens for current level \ndraw_level_loop_inner: ;this is run for each \n call set_point \n\tcall draw_series_hstart\t\n call set_point \n\tcall draw_series_vstart\n add dx, 2\n push bx\n mov bx, [bp+level]\n movzx bx, byte [bx+point_offsets+1] ;if the offset is equal to the next lvls offset then we are done\n cmp dx, bx\n pop bx\n jne draw_level_loop_inner\n pop cx\n add bx, 319\n loop draw_level_loop_outer\n pop dx\n\ndraw_hole:\n\tmov cx, 7\n\tmov al, 0x28\n\tmov di, 165*320+265\ndraw_hole_loop:\n\tpush cx\n\tmov cx, 7\n\tcall draw_horizontal_line\n\tadd di, 313\n\tpop cx \n\tloop draw_hole_loop\n\nnot_moving_skip_check:\n\tcmp word [bp+ball_xs], bp ;if x velocity != 0\n\tjne ball_is_moving\n\tcmp word [bp+ball_ys], bp ; or if y velocity != 0\n\tjne ball_is_moving\n\nget_input:\n\tin al, 0x60\nget_d:\tcmp al, 0x20 ; D key\n\tjne get_a\n cmp word [bp+xs_hold], 3\n\tjge get_input_end\n inc word [bp+xs_hold]\nget_a:\tcmp al, 0x1e ;A key\n\tjne get_w\n\tcmp word [bp+xs_hold], -3\n jle get_input_end \n\tdec word [bp+xs_hold]\nget_w:\tcmp al, 0x11 ;W key\n\tjne get_s\n\tcmp word [bp+ys_hold], 3\n\tjge get_input_end\n\tinc word [bp+ys_hold]\nget_s: cmp al, 0x1f ;S key\n\tjne get_x\n\tcmp word [bp+ys_hold], -3\n\tjle get_input_end\n\tdec word [bp+ys_hold]\t\nget_x:\tcmp al, 0x2d ;X key\n\tjne get_input_end\n\tmov word bx, [bp+xs_hold]\n\tmov [bp+ball_xs], bx\n\tmov word bx, [bp+ys_hold]\n\tmov [bp+ball_ys], bx\n\tinc word [bp+strokes]\n jmp timer_reset \n\tget_input_end:\n\ndraw_velocity:\n\tmov cx, [bp+ball_x]\n mov bx, [bp+ball_y]\n call compute_di\n push di\n mov cx, [bp+xs_hold]\n\tcmp cx, bp\n\tjge pos_hor\n\t; draw negative velocity indicator horizontal\n\timul cx, -5\n\tsub di, cx\n\tjmp draw_hor\n\tpos_hor: ;draw positive velocity indicator horizontal\n\timul cx, 5\n\tdraw_hor:\n\tmov al, 0x20\n call draw_horizontal_line\n\tpop di\n\tmov cx, [bp+ys_hold]\n\tcmp cx, bp\n\tje draw_velocity_end ;idk what part of the following causes it, but it this is not here the vertical lines do some wrapping shit\n\tjl neg_vert\n\t; draw positive velocity indicator vertical\n\timul cx, 5\n\tmov bx, 320 ;len of screen is 320\n\timul bx, cx\n\tsub di, bx ;di has to be negatively offset so we can draw down\n\tjmp draw_vert_vel\n\tneg_vert: ; draw negative velocity indicator vertical\n\timul cx, -5\n\tdraw_vert_vel:\n\tcall draw_vertical_line\n draw_velocity_end:\n jmp skip_inc_bc_ball_still\n\nball_is_moving:\n inc dx ;if the ball is moving inc the timer\nskip_inc_bc_ball_still:\n\n;the collision does not work 100% of the time\n;because of the way the corners overlap, sometimes the ball flips horizontally when it should vertically\n;this has been minimized by designing levels positioned in a way that this is unlikely\ndraw_ball:\n\tmov cx, [bp+ball_x]\n\tmov bx, [bp+ball_y]\n\tadd cx, word [bp+ball_xs]\n\tsub bx, word [bp+ball_ys] ;idfk y this is sub and not add, butt it werks \n\tcall compute_di\n\tmov ah, [es:di]\n\tcmp ah, 0x28 ;in hole\n\tje level_start\nx_check:\n\tcmp ah, 0x2f ;if new position collides x then reverse xs\n\tjne y_check\n\tneg word [bp+ball_xs]\n jmp end_draw_ball\ny_check: ;if new position collides y then reverse ys\n\tcmp ah, 0x30\n\tjne no_collision\n\tneg word [bp+ball_ys]\n jmp end_draw_ball\nno_collision: ;if no collision draw the ball\t \n\tmov word [bp+ball_x], cx\n\tmov word [bp+ball_y], bx\n\tmov al, 0x0f\n\tmov [es:di], al\n end_draw_ball:\n\nslow_ball:\n\tcmp dx, 105\n\tjle slow_ball_end ;if not 105 delays, proceed\n cmp word [bp+strokes], 3\n jge start ;reset ball position / velocity if tm strokes\n jmp reset ;otherwise just reset velocity \n slow_ball_end:\n\t\ndelay:\n\tmov cx, [0x046c] ;get timer thing\n inc cx ;add 1\n delay_loop:\n\tcmp [0x046c], cx ;wait for timer thing to have gone up by one\n\tjb delay_loop \n\t\njmp main_loop\n\n;---Helper Functions---\n\ndraw_series_hstart:\n\tmovzx cx, [si] ; moves first len of series into cx\n\tjcxz draw_series_end ;if the len == 0, stop drawing\n\tmov al, 0x30 ; color for horizontal lines\n\tcall draw_horizontal_line\n\tinc si ;point to next length in series\ndraw_series_vstart:\n\tmovzx cx, [si] ;move next len of series to cx\n\tjcxz draw_series_end ;if len == 0, stop drawing\n\tmov al, 0x2f ;color for vertical line\n\tcall draw_vertical_line\n\tinc si\n\tjmp draw_series_hstart\ndraw_series_end:\n\tinc si\n\tret\n\t\ndraw_horizontal_line: ;di=start, cx=len\n\trep stosb\n\tret\n\t\ndraw_vertical_line: ;di=start, cx=len\n\tmov [es:di], al\n\tdec cx\n\tadd di, 320\n\tcmp cx, bp\n\tjne draw_vertical_line\n\tret\n\ncompute_di: ; \n\tmov di, 320\n imul di, bx\n add di, cx\n\tret\n\nset_point:\n push bx\n mov bx, dx ;bx is point_offset\n mov di, word [points+bx] ;di is current point\n pop bx\n sub di, bx ;offset the start of the line horizontally\n ret\n\npoints:\n\tdw 10*320+30,10*320+35,10*320+75,50*320+115,10*320+30,10*320+200,30*320+243\n\nlens:\n db 120,140,130,40,0,40,108,140,142,0 ;lvl 0\n db 250,180,0,180,250,0,0,140,130,0,130,140,0,60,90,0 ;lvl 1\n db 90,140,80,0,40,50,140,205,0,85,180,0,140,0,0,160 ;lvl 2\n\nlen_offsets: \n db 0,10,26\n\npoint_offsets: ;offsets are multiple of 2, bc points are words\n db 0,2,8,14 \n\ntimes 510-($-$$) db 0\ndw 0xAA55\n\t\n", | |
"data": "Me24AKCOwJiwE80Qx0YM////RgzHRgA3AMdGAh4AiW4OiW4EiW4GiW4IiW4KMdIwwLkA+jH/86pSuQUAMdtTi14MD7aX+n1bUb73fQN2DA+2NIHGzX3oWwHoJQHoVQHoKgGDwgJTi14MD7af+3052lt141mBwz8B4sRauQcAsCi/Sc9RuQcA6BEBgcc5AVni8jluBA+FkgA5bgYPhYsA5GA8IHUJg34IA31A/0YIPB51CYN+CP1+M/9OCDwRdQmDfgoDfSb/Rgo8H3UJg34K/X4Z/04KPC11EoteCIleBIteColeBv9GDulC/4tOAIteAui3AFeLTgg56X0Ha8n7Kc/rA2vJBbAg6JAAX4tOCjnpdBV8DWvJBbtAAQ+v2Snf6wNryfvodgDrAUKLTgCLXgIDTgQrXgbocQAmiiWA/CgPhM3+gPwvdQX3XgTrFYD8MHUF914G6wuJTgCJXgKwDyaIBYP6aX4Lg34OAw+NpP7prv6LDmwEQTkObARy+umu/g+2DOMTsDDoEABGD7YM4wiwL+gIAEbr6EbD86rDJogFSYHHQAE56XX0w79AAQ+v+wHPw1OJ04u/v31bKd/DngyjDMsM8z6eDEgNcyZ4jIIoAChsjI4A+rQAtPoAAIyCAIKMADxaAFqMUAAoMozNAFW0AIwAAKAAChoAAggOVao=", | |
"help": "" | |
}, | |
"_next_name_here_": { | |
"link": "", | |
"description": "", | |
"asm": "", | |
"data": "", | |
"help": "" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Hii this is Finns last 30 min mini-hackathon turned to 4h | |
# https://www.linkedin.com/in/finnmglas | |
# Compile using | |
# nasm -03 -f bin game.asm -o game.bin | |
# see also https://gist.github.com/XlogicX/8204cf17c432cc2b968d138eb639494e | |
# see also https://www.pouet.net/prodlist.php?type%5B%5D=512b | |
# len as of 20250220 33 Games | |
import json | |
boot_sector_games = json.load(open("boot_sector_games_dataset.json", "r")) | |
def play_boot_sector_game(game_name=""): | |
"""Play boot sector game using qemu | |
:param game_name: a searchterm filtering the list of games, if empty, random choice | |
:returns: Much Fun! | |
""" | |
import os | |
import subprocess | |
game_data = None | |
if game_name: | |
for game_name_i in boot_sector_games.keys(): | |
if (game_name.lower() in game_name_i.lower() or game_name.lower() == game_name_i.lower()) and boot_sector_games[game_name_i]["data"]: | |
game_data = boot_sector_games[game_name_i] | |
break | |
else: | |
import random | |
game_data = random.choice([g for g in boot_sector_games.values() if "data" in g and g["data"]]) | |
if not game_data: | |
print(f"No game {game_name} found") | |
return | |
# prevent gtk initialization failed if execed from Jupyter | |
os.environ["DISPLAY"] = ":0" | |
os.environ["XDG_RUNTIME_DIR"] = "/run/user/1000" # /tmp not owned | |
boot_file = "/tmp/boot.img" | |
with open(boot_file, "wb") as f: | |
f.write(base64.b64decode(bytes(game_data["data"], "utf8"))) | |
print("Playing using qemu, press CTRL+Shift+G to exit window") | |
if "help" in game_data: | |
print("Help:", game_data["help"]) | |
subprocess.run(["qemu-system-x86_64", boot_file]) | |
return "Much Fun :D" | |
# default: play a random boot sector game | |
search_term = "" | |
play_boot_sector_game(search_term) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Cool, nah? 😎