tetros/tetros.asm

325 lines
8.3 KiB
NASM
Raw Normal View History

2016-09-30 22:41:07 +02:00
; Tetris
2016-09-29 23:42:22 +02:00
org 7c00h
2016-09-29 08:50:29 +02:00
2016-09-27 08:51:22 +02:00
; ==============================================================================
; DEBUGGING MACROS
; ==============================================================================
2016-09-29 23:42:22 +02:00
%ifdef DEBUG
2016-09-30 22:42:49 +02:00
%include "debug_macros.asm"
2016-09-29 23:42:22 +02:00
%endif
2016-09-27 08:51:22 +02:00
; ==============================================================================
; MACROS
; ==============================================================================
2016-09-26 10:59:40 +02:00
2016-09-30 22:41:07 +02:00
; Sleeps for the given number of microseconds.
2016-09-26 10:59:40 +02:00
%macro sleep 1
pusha
xor cx, cx
2016-09-26 08:58:01 +02:00
mov dx, %1
2016-09-23 09:03:40 +02:00
mov ah, 0x86
int 0x15
2016-09-26 10:59:40 +02:00
popa
2016-09-23 09:03:40 +02:00
%endmacro
2016-09-30 22:41:07 +02:00
; Choose a brick at random.
2016-09-27 08:51:22 +02:00
%macro select_brick 0
2016-09-28 09:00:02 +02:00
mov ah, 2 ; get current time
2016-09-27 08:51:22 +02:00
int 0x1a
2016-09-29 23:42:22 +02:00
mov al, byte [seed_value]
2016-09-27 08:51:22 +02:00
xor ax, dx
2016-09-28 22:55:00 +02:00
mov bl, 31
2016-09-27 08:51:22 +02:00
mul bx
inc ax
2016-09-29 23:42:22 +02:00
mov byte [seed_value], al
2016-09-27 08:51:22 +02:00
xor dx, dx
mov bx, 7
div bx
2016-09-28 09:00:02 +02:00
shl dl, 3
2016-09-30 22:41:07 +02:00
xchg ax, dx ; mov al, dl
2016-09-28 09:00:02 +02:00
%endmacro
2016-09-30 22:41:07 +02:00
; Sets video mode and hides cursor.
2016-09-28 09:00:02 +02:00
%macro clear_screen 0
2016-09-30 22:41:07 +02:00
xor ax, ax ; clear screen (40x25)
int 0x10
mov ah, 1 ; hide cursor
mov cx, 0x2607
2016-09-28 09:00:02 +02:00
int 0x10
2016-09-27 08:51:22 +02:00
%endmacro
2016-09-22 12:44:11 +02:00
2016-09-30 22:41:07 +02:00
field_left_col: equ 13
field_width: equ 14
inner_width: equ 12
inner_first_col: equ 14
start_row_col: equ 0x0412
2016-09-27 08:51:22 +02:00
%macro init_screen 0
2016-09-28 09:00:02 +02:00
clear_screen
2016-09-30 22:41:07 +02:00
mov dh, 3 ; row
mov cx, 18 ; number of rows
2016-09-28 22:55:00 +02:00
ia: push cx
inc dh ; increment row
2016-09-30 22:41:07 +02:00
mov dl, field_left_col ; set column
mov cx, field_width ; width of box
2016-10-01 19:12:20 +02:00
mov bx, 0x78 ; color
2016-09-28 09:00:02 +02:00
call set_and_write
2016-09-28 22:55:00 +02:00
cmp dh, 21 ; don't remove last line
je ib ; if last line jump
inc dx ; increase column
2016-09-30 22:41:07 +02:00
mov cx, inner_width ; width of box
2016-09-28 22:55:00 +02:00
xor bx, bx ; color
2016-09-28 09:00:02 +02:00
call set_and_write
2016-09-28 22:55:00 +02:00
ib: pop cx
2016-09-27 08:51:22 +02:00
loop ia
%endmacro
2016-09-26 08:58:01 +02:00
2016-09-27 08:51:22 +02:00
; ==============================================================================
2016-09-23 09:03:40 +02:00
2016-09-29 23:42:22 +02:00
delay: equ 0x7f00
seed_value: equ 0x7f02
2016-09-29 08:50:29 +02:00
2016-09-27 08:51:22 +02:00
section .text
2016-09-22 12:44:11 +02:00
2016-09-26 08:58:01 +02:00
start_tetris:
2016-09-29 23:42:22 +02:00
xor ax, ax
mov ds, ax
2016-09-27 08:51:22 +02:00
init_screen
2016-09-23 09:03:40 +02:00
new_brick:
2016-09-29 23:42:22 +02:00
mov byte [delay], 100 ; 3 * 100 = 300ms
2016-09-28 09:00:02 +02:00
select_brick ; returns the selected brick in AL
2016-09-30 22:41:07 +02:00
mov dx, start_row_col ; start at row 4 and col 38
2016-09-23 09:03:40 +02:00
loop:
2016-09-26 08:58:01 +02:00
call check_collision
2016-09-29 23:42:22 +02:00
jne $ ; collision -> game over
call print_brick
2016-09-22 12:44:11 +02:00
2016-09-23 09:03:40 +02:00
wait_or_keyboard:
2016-09-29 23:42:22 +02:00
xor cx, cx
mov cl, byte [delay]
2016-09-23 09:03:40 +02:00
wait_a:
2016-09-27 08:51:22 +02:00
push cx
2016-09-29 23:42:22 +02:00
sleep 3000 ; wait 3ms
2016-09-22 12:44:11 +02:00
2016-09-23 09:03:40 +02:00
push ax
2016-09-26 10:59:40 +02:00
mov ah, 1 ; check for keystroke; AX modified
2016-09-23 09:03:40 +02:00
int 0x16 ; http://www.ctyme.com/intr/rb-1755.htm
2016-09-27 08:51:22 +02:00
mov cx, ax
2016-09-23 09:03:40 +02:00
pop ax
jz no_key ; no keystroke
call clear_brick
2016-09-26 10:59:40 +02:00
; 4b left, 48 up, 4d right, 50 down
2016-09-27 08:51:22 +02:00
cmp ch, 0x4b ; left arrow
2016-09-23 09:03:40 +02:00
je left_arrow ; http://stackoverflow.com/questions/16939449/how-to-detect-arrow-keys-in-assembly
2016-09-27 08:51:22 +02:00
cmp ch, 0x48 ; up arrow
2016-09-23 09:03:40 +02:00
je up_arrow
2016-09-27 08:51:22 +02:00
cmp ch, 0x4d
2016-09-23 09:03:40 +02:00
je right_arrow
2016-09-26 10:59:40 +02:00
2016-09-29 23:42:22 +02:00
mov byte [delay], 10 ; every other key is fast down
2016-09-23 09:03:40 +02:00
jmp clear_keys
left_arrow:
2016-09-27 08:51:22 +02:00
dec dx
2016-09-23 09:03:40 +02:00
call check_collision
je clear_keys ; no collision
2016-09-27 08:51:22 +02:00
inc dx
2016-09-23 09:03:40 +02:00
jmp clear_keys
right_arrow:
2016-09-27 08:51:22 +02:00
inc dx
2016-09-23 09:03:40 +02:00
call check_collision
je clear_keys ; no collision
2016-09-27 08:51:22 +02:00
dec dx
2016-09-23 09:03:40 +02:00
jmp clear_keys
up_arrow:
2016-09-26 10:59:40 +02:00
mov bl, al
2016-09-28 22:55:00 +02:00
inc ax
inc ax
test al, 00000111b ; check for overflow
jnz nf ; no overflow
sub al, 8
nf: call check_collision
2016-09-26 10:59:40 +02:00
je clear_keys ; no collision
mov al, bl
2016-09-23 09:03:40 +02:00
clear_keys:
call print_brick
push ax
xor ah, ah ; remove key from buffer
int 0x16
pop ax
no_key:
2016-09-27 08:51:22 +02:00
pop cx
2016-09-23 09:03:40 +02:00
loop wait_a
call clear_brick
inc dh ; increase row
call check_collision
2016-09-28 09:00:02 +02:00
je loop ; no collision
2016-09-23 09:03:40 +02:00
dec dh
2016-09-22 12:44:11 +02:00
call print_brick
2016-09-27 08:51:22 +02:00
call check_filled
2016-09-23 09:03:40 +02:00
jmp new_brick
2016-09-27 08:51:22 +02:00
2016-09-28 09:00:02 +02:00
; ------------------------------------------------------------------------------
2016-09-27 08:51:22 +02:00
2016-09-28 09:00:02 +02:00
set_and_write:
2016-09-27 08:51:22 +02:00
mov ah, 2 ; set cursor
int 0x10
2016-09-29 23:42:22 +02:00
mov ax, 0x0920 ; write boxes
2016-09-27 08:51:22 +02:00
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
2016-09-28 09:00:02 +02:00
; ------------------------------------------------------------------------------
; DH = current row
%macro replace_current_row 0
2016-09-29 23:42:22 +02:00
pusha ; replace current row with row above
2016-09-30 22:41:07 +02:00
mov dl, inner_first_col
mov cx, inner_width
2016-09-28 09:00:02 +02:00
cf_aa:
2016-09-28 22:55:00 +02:00
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
2016-09-28 09:00:02 +02:00
popa
%endmacro
2016-09-27 08:51:22 +02:00
check_filled:
pusha
2016-09-28 22:55:00 +02:00
mov dh, 21 ; start at row 21
2016-09-27 08:51:22 +02:00
next_row:
2016-09-28 22:55:00 +02:00
dec dh ; decrement row
jz cf_done ; at row 0 we are done
2016-09-27 08:51:22 +02:00
xor bx, bx
2016-09-30 22:41:07 +02:00
mov cx, inner_width
mov dl, inner_first_col ; start at first inner column
2016-09-27 08:51:22 +02:00
cf_loop:
call set_and_read
2016-09-28 22:55:00 +02:00
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
2016-09-27 08:51:22 +02:00
cf_is_zero:
loop cf_loop
2016-09-30 22:41:07 +02:00
cmp bl, inner_width ; if counter is 12 full we found a full row
2016-09-27 08:51:22 +02:00
jne next_row
2016-09-28 22:55:00 +02:00
replace_next_row: ; replace current row with rows above
2016-09-28 09:00:02 +02:00
replace_current_row
2016-09-28 22:55:00 +02:00
dec dh ; replace row above ... and so on
2016-09-27 08:51:22 +02:00
jnz replace_next_row
2016-09-28 22:55:00 +02:00
call check_filled ; check for other full rows
2016-09-27 08:51:22 +02:00
cf_done:
popa
ret
clear_brick:
2016-09-28 09:00:02 +02:00
xor bx, bx
2016-09-27 08:51:22 +02:00
jmp print_brick_no_color
2016-09-30 22:41:07 +02:00
print_brick: ; al = 0AAAARR0
2016-09-28 09:00:02 +02:00
mov bl, al ; select the right color
2016-09-30 22:41:07 +02:00
shr bl, 3
inc bx
shl bl, 4
2016-09-27 08:51:22 +02:00
print_brick_no_color:
2016-09-28 22:55:00 +02:00
inc bx ; set least significant bit
2016-09-27 08:51:22 +02:00
mov di, bx
jmp check_collision_main
2016-09-28 09:00:02 +02:00
; BL = color of brick
2016-09-29 23:42:22 +02:00
; DX = position (DH = row), AL = brick offset
2016-09-26 10:59:40 +02:00
; return: flag
2016-09-23 09:03:40 +02:00
check_collision:
2016-09-27 08:51:22 +02:00
mov di, 0
2016-09-28 09:00:02 +02:00
check_collision_main: ; DI = 1 -> check, 0 -> print
2016-09-23 09:03:40 +02:00
pusha
2016-09-29 23:42:22 +02:00
xor bx, bx ; load the brick into AX
mov bl, al
mov ax, word [bricks + bx]
2016-09-30 22:41:07 +02:00
xor bx, bx ; BH = page number, BL = collision counter
2016-09-23 09:03:40 +02:00
mov cx, 4
cc:
push cx
mov cl, 4
dd:
2016-09-29 08:50:29 +02:00
test ah, 10000000b
2016-09-23 09:03:40 +02:00
jz is_zero
2016-09-26 10:59:40 +02:00
2016-09-30 22:41:07 +02:00
push ax
2016-09-27 08:51:22 +02:00
or di, di
2016-09-30 22:41:07 +02:00
jz ee ; we just want to check for collisions
pusha ; print space with color stored in DI
mov bx, di ; at position in DX
2016-09-29 08:50:29 +02:00
xor al, al
2016-09-27 08:51:22 +02:00
mov cx, 1
2016-09-29 08:50:29 +02:00
call set_and_write
2016-09-27 08:51:22 +02:00
popa
jmp is_zero_a
ee:
2016-09-29 08:50:29 +02:00
call set_and_read
2016-09-26 10:59:40 +02:00
shr ah, 4 ; rotate to get background color in AH
2016-09-27 08:51:22 +02:00
jz is_zero_a ; jmp if background color is 0
inc bx
is_zero_a:
2016-09-23 09:03:40 +02:00
pop ax
2016-09-30 22:41:07 +02:00
2016-09-23 09:03:40 +02:00
is_zero:
2016-09-29 23:42:22 +02:00
shl ax, 1 ; move to next bit in brick mask
2016-09-27 08:51:22 +02:00
inc dx ; move to next column
2016-09-23 09:03:40 +02:00
loop dd
2016-09-26 08:58:01 +02:00
sub dl, 4 ; reset column
inc dh ; move to next row
2016-09-23 09:03:40 +02:00
pop cx
loop cc
2016-09-29 08:50:29 +02:00
or bl, bl ; bl != 0 -> collision
2016-09-23 09:03:40 +02:00
popa
ret
2016-09-29 23:42:22 +02:00
; ==============================================================================
2016-09-22 12:44:11 +02:00
bricks:
2016-09-29 23:42:22 +02:00
; in AL in AH
; 3rd + 4th 1st + 2nd row
db 01000100b, 01000100b, 00000000b, 11110000b
db 01000100b, 01000100b, 00000000b, 11110000b
db 01100000b, 00100010b, 00000000b, 11100010b
db 01000000b, 01100100b, 00000000b, 10001110b
db 01100000b, 01000100b, 00000000b, 00101110b
db 00100000b, 01100010b, 00000000b, 11101000b
db 00000000b, 01100110b, 00000000b, 01100110b
db 00000000b, 01100110b, 00000000b, 01100110b
db 00000000b, 11000110b, 01000000b, 00100110b
db 00000000b, 11000110b, 01000000b, 00100110b
db 00000000b, 01001110b, 01000000b, 01001100b
db 00000000b, 11100100b, 10000000b, 10001100b
db 00000000b, 01101100b, 01000000b, 10001100b
db 00000000b, 01101100b, 01000000b, 10001100b
%ifndef DEBUG
; It seems that I need a dummy partition table entry for my notebook.
times 446-($-$$) db 0
db 0x80 ; bootable
db 0x00, 0x01, 0x00 ; start CHS address
db 0x17 ; partition type
db 0x00, 0x02, 0x00 ; end CHS address
db 0x00, 0x00, 0x00, 0x00 ; LBA
db 0x02, 0x00, 0x00, 0x00 ; number of sectors
; At the end we need the boot sector signature.
times 510-($-$$) db 0
db 0x55
db 0xaa
%endif