; TODO reduce size ; TODO decrease timer / increase level ; TODO intro ; TODO show next brick ; TODO scores ; ============================================================================== ; DEBUGGING MACROS ; ============================================================================== %macro print_reg 1 mov dx, %1 mov cx, 16 print_reg_loop: push cx mov al, '0' test dh, 10000000b jz print_reg_do mov al, '1' print_reg_do: mov bx, 0x0006 ; page = 0 (BH), color = gray on black (BL) mov ah, 0x09 ; write character stored in AL mov cx, 1 int 0x10 mov ah, 3 ; move cursor one column forward int 0x10 inc dx mov ah, 2 ; set cursor int 0x10 pop cx shl dx, 1 loop print_reg_loop jmp $ %endmacro ; ============================================================================== ; MACROS ; ============================================================================== %macro sleep 1 pusha xor cx, cx mov dx, %1 mov ah, 0x86 int 0x15 popa %endmacro %macro select_brick 0 mov ah, 2 ; get current time int 0x1a mov al, byte [seed_value + 0x7c00] xor ax, dx mov bl, 31 mul bx inc ax mov byte [seed_value + 0x7c00], al xor dx, dx mov bx, 7 div bx shl dl, 3 mov al, dl %endmacro %macro clear_screen 0 mov ax, 3 ; clear screen int 0x10 mov ah, 1 ; hide cursor mov cx, 0x2607 int 0x10 %endmacro %macro init_screen 0 clear_screen mov dh, 3 mov cx, 18 ia: push cx inc dh ; increment row mov dl, 33 ; set column mov cx, 14 ; width of box mov bx, 0x77 ; color call set_and_write cmp dh, 21 ; don't remove last line je ib ; if last line jump inc dx ; increase column mov cx, 12 ; width of box xor bx, bx ; color call set_and_write ib: pop cx loop ia %endmacro %macro brick_offset 0 xor ah, ah ; AL = brick offset mov si, ax add si, bricks + 0x7c00 %endmacro ; ============================================================================== section .text xor ax, ax ; init ds for lodsb mov ds, ax mov es, ax start_tetris: ;call initial_animation init_screen new_brick: mov word [delay + 0x7c00], 500 ; reset timer select_brick ; returns the selected brick in AL mov dx, 0x0426 ; start at row 4 and col 38 loop: call check_collision jne game_over call print_brick ; if you modify AL or DX here, you should know what you're doing wait_or_keyboard: mov cx, word [delay + 0x7c00] wait_a: push cx sleep 100 ; wait 100 microseconds push ax mov ah, 1 ; check for keystroke; AX modified int 0x16 ; http://www.ctyme.com/intr/rb-1755.htm mov cx, ax pop ax jz no_key ; no keystroke call clear_brick ; 4b left, 48 up, 4d right, 50 down cmp ch, 0x4b ; left arrow je left_arrow ; http://stackoverflow.com/questions/16939449/how-to-detect-arrow-keys-in-assembly cmp ch, 0x48 ; up arrow je up_arrow cmp ch, 0x4d je right_arrow mov word [delay + 0x7c00], 30 ; every other key is fast down jmp clear_keys left_arrow: dec dx call check_collision je clear_keys ; no collision inc dx jmp clear_keys right_arrow: inc dx call check_collision je clear_keys ; no collision dec dx jmp clear_keys up_arrow: mov bl, al inc ax inc ax test al, 00000111b ; check for overflow jnz nf ; no overflow sub al, 8 nf: call check_collision je clear_keys ; no collision mov al, bl clear_keys: call print_brick push ax xor ah, ah ; remove key from buffer int 0x16 pop ax no_key: pop cx loop wait_a call clear_brick inc dh ; increase row call check_collision je loop ; no collision dec dh call print_brick call check_filled jmp new_brick ; ------------------------------------------------------------------------------ set_and_write: mov ah, 2 ; set cursor int 0x10 mov ax, 0x0900 ; write boxes int 0x10 ret set_and_read: mov ah, 2 ; set cursor position int 0x10 mov ah, 8 ; read character and attribute, BH = 0 int 0x10 ; result in AX ret ; ------------------------------------------------------------------------------ ; DH = current row %macro replace_current_row 0 pusha mov dl, 34 ; replace current row with row above mov cx, 12 cf_aa: push cx dec dh ; decrement row call set_and_read inc dh ; increment row mov bl, ah ; color from AH to BL mov cl, 1 call set_and_write inc dx ; next column pop cx loop cf_aa popa %endmacro check_filled: pusha mov dh, 21 ; start at row 21 next_row: dec dh ; decrement row jz cf_done ; at row 0 we are done xor bx, bx mov cx, 12 ; 12 columns mov dl, 34 ; start at column 34 cf_loop: call set_and_read shr ah, 4 ; rotate to get background color in AH jz cf_is_zero ; jmp if background color is 0 inc bx ; increment counter inc dx ; go to next column cf_is_zero: loop cf_loop cmp bl, 12 ; if counter is 12 full we found a full row jne next_row replace_next_row: ; replace current row with rows above replace_current_row dec dh ; replace row above ... and so on jnz replace_next_row call check_filled ; check for other full rows cf_done: popa ret game_over: mov cx, 10 mov dx, 0x0323 mov bx, 0x8c mov bp, game_over_msg + 0x7c00 mov ax, 0x1300 int 0x10 xor ax, ax ; wait for keyboard int 16h jmp start_tetris clear_brick: xor bx, bx jmp print_brick_no_color print_brick: mov bl, al ; select the right color shl bl, 1 and bl, 11110000b ; ((bl >> 3) + 9) << 4 add bl, 144 print_brick_no_color: inc bx ; set least significant bit mov di, bx jmp check_collision_main ; BL = color of brick ; DX = position (DH = row), AL = brick ; return: flag check_collision: mov di, 0 check_collision_main: ; DI = 1 -> check, 0 -> print pusha brick_offset ; result in SI lodsw xchg ah, al xor bx, bx ; set BH = BL = 0 mov cx, 4 cc: push cx mov cl, 4 dd: test ax, 1000000000000000b jz is_zero push ax mov ah, 2 ; set cursor position, BH = 0 int 0x10 or di, di jz ee ; jump if we just want to check for collisions ; print space with color stored in DI at postion DX pusha mov bx, di dec bx mov ax, 0x0920 ; print brick mov cx, 1 int 0x10 popa jmp is_zero_a ee: mov ah, 8 ; read character and attribute, BH = 0 int 0x10 ; result in AX shr ah, 4 ; rotate to get background color in AH jz is_zero_a ; jmp if background color is 0 inc bx is_zero_a: pop ax is_zero: shl ax, 1 inc dx ; move to next column loop dd sub dl, 4 ; reset column inc dh ; move to next row pop cx loop cc cmp bl, 0 ; bl != 0 -> collision popa ret ; ====================================================================== ; ---------------------------------------------------------------------- initial_animation: ; call clear_screen ; mov ah, 2 ; set cursor position ; xor bx, bx ; mov dh, 5 ; mov dl, 10 ; int 0x10 ; mov si, message + 0x7c00 ; MBR is loaded at address 0000:7C00 initial_animation_next: ; cld ; lodsb ; cmp al, 0 ; jne initial_animation_do ; xor ax, ax ; wait for keyboard ; int 16h ; ret initial_animation_do: ; mov bx, 0x0a ; write character ; mov cx, 1 ; mov ah, 9 ; int 0x10 ; call cursor_right ; push dx ; mov cx, 2 ; wait 2x65536 microseconds ; xor dx, dx ; call wait_abit ; pop dx ; jmp initial_animation_next ; ---------------------------------------------------------------------- ;message: db "Let's play tetris ...", 0 game_over_msg: db "GAME OVER!" seed_value: db 0x34 delay: dw 500 bricks: db 01000100b, 01000100b, 11110000b, 00000000b db 01000100b, 01000100b, 11110000b, 00000000b db 00100010b, 01100000b, 11100010b, 00000000b db 01100100b, 01000000b, 10001110b, 00000000b db 01000100b, 01100000b, 00101110b, 00000000b db 01100010b, 00100000b, 11101000b, 00000000b db 01100110b, 00000000b, 01100110b, 00000000b db 01100110b, 00000000b, 01100110b, 00000000b db 11000110b, 00000000b, 00100110b, 01000000b db 11000110b, 00000000b, 00100110b, 01000000b db 01001110b, 00000000b, 01001100b, 01000000b db 11100100b, 00000000b, 10001100b, 10000000b db 01101100b, 00000000b, 10001100b, 01000000b db 01101100b, 00000000b, 10001100b, 01000000b ;times 510-($-$$) db 0 ;db 0x55 ;db 0xaa