mirror of
https://github.com/mariiaan/Bugtris.git
synced 2026-05-14 10:11:20 +02:00
1341 lines
25 KiB
NASM
1341 lines
25 KiB
NASM
|
|
ExitProcess PROTO
|
|
RegisterClassA PROTO
|
|
GetModuleHandleA PROTO
|
|
CreateWindowExA PROTO
|
|
ShowWindow PROTO
|
|
GetLastError PROTO
|
|
DefWindowProcA PROTO
|
|
GetMessageA PROTO
|
|
TranslateMessage PROTO
|
|
DispatchMessageA PROTO
|
|
PostQuitMessage PROTO
|
|
SetTimer PROTO
|
|
InvalidateRect PROTO
|
|
BeginPaint PROTO
|
|
TextOutA PROTO
|
|
CreateSolidBrush PROTO
|
|
FillRect PROTO
|
|
DeleteObject PROTO
|
|
EndPaint PROTO
|
|
GetClientRect PROTO
|
|
GetTickCount PROTO
|
|
PlaySoundA PROTO
|
|
|
|
RECT struct
|
|
left dd ?
|
|
top dd ?
|
|
right dd ?
|
|
bottom dd ?
|
|
RECT ends
|
|
|
|
.data
|
|
wndClass_start:
|
|
wndClass_style dd 0 ; 4 bytes
|
|
; Align to 8-byte boundary
|
|
align 8
|
|
wndClass_wndProc dq 0 ; 8 bytes
|
|
wndClass_cbClsExtra dd 0 ; 4 bytes
|
|
wndClass_cbWndExtra dd 0 ; 4 bytes
|
|
; align 8 not needed, we have two dds above
|
|
wndClass_hInstance dq 0 ; 8 bytes
|
|
wndClass_hIcon dq 0 ; 8 bytes
|
|
wndClass_hCursor dq 0 ; 8 bytes
|
|
wndClass_hBrush dq 0 ; 8 bytes
|
|
wndClass_menName dq 0 ; 8 bytes
|
|
wndClass_className dq 0 ; 8 bytes
|
|
wndClass_sizeof EQU $ - wndClass_start
|
|
|
|
.const ; Game settings ---
|
|
myClassName db "TestKlasse", 0
|
|
myWindowTitle db "Tetris", 0
|
|
|
|
GRID_SIZE_X EQU 20
|
|
GRID_SIZE_Y EQU 30
|
|
BLOCK_PIXEL_LENGTH EQU 24
|
|
|
|
GRID_NUM_ELEMENTS EQU GRID_SIZE_X * GRID_SIZE_Y
|
|
GRID_BYTE_SIZE EQU GRID_NUM_ELEMENTS
|
|
|
|
.const ; Win32 Definitions ---
|
|
WS_OVERLAPPEDWINDOW EQU 13565952
|
|
WS_SYSMENU EQU 00080000H
|
|
CW_USEDEFAULT EQU -2147483648
|
|
WM_CREATE EQU 1
|
|
WM_DESTROY EQU 2
|
|
WM_PAINT EQU 15
|
|
WM_TIMER EQU 275
|
|
WM_KEYDOWN EQU 256
|
|
WM_ERASEBACKGROUND EQU 014h
|
|
VK_W EQU 110001h
|
|
VK_A EQU 1E0001h
|
|
VK_S EQU 1F0001h
|
|
VK_D EQU 200001h
|
|
SND_ASYNC EQU 1
|
|
|
|
; Tetromino shape definitions
|
|
TETRO_0 db 2, 4, "x x x xx" ; L
|
|
TETRO_1 db 2, 4, " x x xxx" ; Reverse L
|
|
TETRO_2 db 2, 2, "xxxx" ; Block
|
|
TETRO_3 db 4, 1, "xxxx" ; Line
|
|
TETRO_4 db 3, 2, " xxxx " ; snake from BL to TR
|
|
TETRO_5 db 3, 2, "xx xx" ; snake from TR to BL
|
|
TETRO_6 db 3, 2, " x xxx" ; penith
|
|
|
|
poolStart:
|
|
TETRO_SHAPE_POOL dq TETRO_0, TETRO_1, TETRO_2, TETRO_3, TETRO_4, TETRO_5, TETRO_6
|
|
; Tetromino shape pool
|
|
TETRO_SHAPE_POOL_SIZE EQU ($-poolStart)/8
|
|
TETRO_MAX_WIDTH EQU 4
|
|
TETRO_MAX_HEIGHT EQU 4
|
|
TETRO_BUFFER_SIZE EQU TETRO_MAX_WIDTH * TETRO_MAX_HEIGHT
|
|
|
|
.data? ; --------------------------
|
|
moduleHandle dq ?
|
|
hwndWindow dq ?
|
|
msg db 48 dup(?)
|
|
playField db GRID_BYTE_SIZE dup(?)
|
|
tetroBuffer db TETRO_BUFFER_SIZE dup(?)
|
|
tetroBufferRotateTmp db TETRO_BUFFER_SIZE dup(?)
|
|
tetroBufferCurrentWidth db ?
|
|
tetroBufferCurrentHeight db ?
|
|
playerPosX db ?
|
|
playerPosY db ?
|
|
hdc dq ?
|
|
rndSeed dq ?
|
|
skipNextGL db ?
|
|
|
|
.code ; --------------------------
|
|
|
|
main PROC
|
|
call InitRandom
|
|
|
|
push rbp
|
|
mov rbp, rsp
|
|
sub rsp, 120h
|
|
|
|
; mov rax, OFFSET TETRO_0
|
|
;mov qword ptr [TETRO_SHAPE_POOL], rax
|
|
|
|
call InitPlayField
|
|
|
|
call LoadRandomTetromino
|
|
|
|
mov rcx, 0
|
|
mov rdx, 0
|
|
call GetTetroState
|
|
mov rcx, 1
|
|
mov rdx, 0
|
|
call GetTetroState
|
|
|
|
|
|
mov byte ptr [playerPosX], 0
|
|
mov byte ptr [playerPosY], 0
|
|
|
|
|
|
; Get Module handle
|
|
mov rax, GetModuleHandleA(0)
|
|
mov [moduleHandle], rax
|
|
|
|
; Set instance handle
|
|
mov rax, [moduleHandle]
|
|
mov [wndClass_hInstance], rax
|
|
|
|
; Set the class name
|
|
lea rax, myClassName
|
|
mov [wndClass_className], rax
|
|
|
|
lea rax, Offset WndProc
|
|
mov qword ptr [wndClass_wndProc], rax
|
|
|
|
; Register the window class
|
|
mov rcx, wndClass_start
|
|
call RegisterClassA
|
|
test rax, rax
|
|
jz fail
|
|
|
|
mov r11, BLOCK_PIXEL_LENGTH
|
|
mov rax, GRID_SIZE_X
|
|
mul r11
|
|
mov r12, rax
|
|
mov rax, GRID_SIZE_Y
|
|
mul r11
|
|
mov r13, rax
|
|
add r13, 12
|
|
|
|
; Create Window
|
|
xor ecx, ecx ; dwExStyle
|
|
lea rdx, OFFSET myClassName ; lpClassName
|
|
lea r8, OFFSET myWindowTitle ; lpWindowName
|
|
mov r9d, WS_SYSMENU ; dwStyle
|
|
mov dword ptr [rsp + 32], CW_USEDEFAULT ; X
|
|
mov dword ptr [rsp + 40], CW_USEDEFAULT ; Y
|
|
mov dword ptr [rsp + 48], r12d ; nWidth
|
|
mov dword ptr [rsp + 56], r13d ; nHeight
|
|
mov qword ptr [rsp + 64], 0 ; hWndParent
|
|
mov qword ptr [rsp + 72], 0 ; hMenu
|
|
mov rax, qword ptr [moduleHandle]
|
|
mov qword ptr [rsp + 80], rax ; hInstance
|
|
mov qword ptr [rsp + 88], 0 ; lpParam
|
|
|
|
call CreateWindowExA
|
|
|
|
mov [hwndWindow], rax
|
|
test rax, rax
|
|
jz fail
|
|
|
|
; Show the window
|
|
mov rcx, [hwndWindow]
|
|
mov rdx, 5
|
|
call ShowWindow
|
|
|
|
; Enter the message loop
|
|
message_loop:
|
|
; Get Message
|
|
lea rcx, msg
|
|
mov rdx, 0
|
|
mov r8, 0
|
|
mov r9, 0
|
|
call GetMessageA
|
|
cmp rax, 1
|
|
jne message_loop_break
|
|
; Translate Message
|
|
lea rcx, msg
|
|
call TranslateMessage
|
|
; Dispatch Message
|
|
lea rcx, msg
|
|
call DispatchMessageA
|
|
|
|
jmp message_loop
|
|
|
|
message_loop_break:
|
|
mov rcx, 0
|
|
call ExitProcess
|
|
|
|
fail:
|
|
call GetLastError
|
|
mov rcx, 1
|
|
call ExitProcess
|
|
|
|
mov rsp, rbp
|
|
pop rbp
|
|
main ENDP
|
|
|
|
InitPlayField PROC
|
|
push rbx
|
|
lea rax, playField
|
|
lea rbx, [playField + GRID_BYTE_SIZE]
|
|
_loop:
|
|
mov byte ptr [rax], 0
|
|
inc rax
|
|
cmp rax, rbx
|
|
je _loop_break
|
|
jmp _loop
|
|
_loop_break:
|
|
pop rbx
|
|
ret
|
|
InitPlayField ENDP
|
|
|
|
; (out) rax Start Seed
|
|
InitRandom PROC
|
|
call GetTickCount
|
|
mov [rndSeed], rax
|
|
ret
|
|
InitRandom ENDP
|
|
|
|
; (out) rax random
|
|
Random64 PROC
|
|
push rbx
|
|
mov rax, [rndSeed]
|
|
add rax, 12h
|
|
rol rax, 12
|
|
xor rax, [rndSeed]
|
|
mov rbx, 41C64E6Dh
|
|
mul rbx
|
|
ror rax, 8
|
|
mov [rndSeed], rax
|
|
pop rbx
|
|
ret
|
|
Random64 ENDP
|
|
|
|
; (in) rcx min
|
|
; (in) rdx max
|
|
; (out) rax random
|
|
RandomRange PROC
|
|
push r8
|
|
mov r8, rcx ; min
|
|
push r9
|
|
mov r9, rdx ; max
|
|
push r10
|
|
mov r10, r9
|
|
sub r10, r8 ; interval
|
|
push rdx
|
|
|
|
call Random64
|
|
|
|
xor rdx, rdx
|
|
div r10
|
|
|
|
mov rax, rdx
|
|
add rax, r8
|
|
|
|
pop rdx
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
ret
|
|
RandomRange ENDP
|
|
|
|
LoadRandomTetromino PROC
|
|
push rcx
|
|
push rdx
|
|
mov rcx, 0
|
|
mov rdx, TETRO_SHAPE_POOL_SIZE
|
|
call RandomRange
|
|
mov rcx, rax
|
|
call LoadTetromino
|
|
pop rdx
|
|
pop rcx
|
|
ret
|
|
LoadRandomTetromino ENDP
|
|
|
|
; (in) rcx tetro index
|
|
LoadTetromino PROC
|
|
call ClearTetroBuffer
|
|
|
|
push r8
|
|
push rax
|
|
push rdx
|
|
; Calculate offset address
|
|
mov rax, rcx
|
|
mov r8, 8
|
|
mul r8 ; rax = rcx * 8
|
|
|
|
lea r8, TETRO_SHAPE_POOL ;
|
|
add r8, rax ; r8 = (byte*)TETRO_SHAPE_POOL[rcx]
|
|
mov r8, [r8]
|
|
; r8 now points to the desired tetromino
|
|
; Layout (byte array):
|
|
; [0] tetro width
|
|
; [1] tetro height
|
|
; [2..(width * height)] tetromino data
|
|
|
|
; r8 -> [0] tetro width
|
|
mov al, byte ptr [r8]
|
|
mov [tetroBufferCurrentWidth], al
|
|
inc r8
|
|
|
|
; r8 -> [1] tetro height
|
|
mov al, byte ptr [r8]
|
|
mov [tetroBufferCurrentHeight], al
|
|
inc r8
|
|
|
|
; r8 -> [2] tetromino data
|
|
|
|
push r10 ; x = 0
|
|
push r11 ; y = 0
|
|
xor r10, r10
|
|
xor r11, r11
|
|
|
|
push r12
|
|
push r13
|
|
xor r12, r12
|
|
xor r13, r13
|
|
mov r12b, byte ptr [tetroBufferCurrentWidth] ; maxX
|
|
mov r13b, byte ptr [tetroBufferCurrentHeight] ; maxY
|
|
push r14
|
|
push r15
|
|
|
|
_loopY:
|
|
cmp r11b, r13b
|
|
je _loopY_break
|
|
xor r10b, r10b ; x = 0
|
|
|
|
_loopX:
|
|
cmp r10b, r12b
|
|
je _loopX_break
|
|
|
|
; Calculate read address
|
|
|
|
push rax
|
|
mov rax, r11 ; <- y
|
|
mul r12
|
|
add rax, r10
|
|
add rax, r8
|
|
mov r14b, byte ptr [rax]
|
|
|
|
pop rax
|
|
|
|
; -> r14b now contains the read state
|
|
cmp r14b, 'x'
|
|
jne _loopX_continue
|
|
|
|
push rcx
|
|
push rdx
|
|
push r8
|
|
mov rcx, r10
|
|
mov rdx, r11
|
|
mov r8, 1
|
|
call SetTetroState
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
|
|
|
|
_loopX_continue:
|
|
inc r10b
|
|
jmp _loopX
|
|
|
|
_loopX_break:
|
|
_loopY_continue:
|
|
inc r11b
|
|
jmp _loopY
|
|
|
|
_loopY_break:
|
|
pop r15
|
|
pop r14
|
|
pop r13
|
|
pop r12
|
|
pop r11
|
|
pop r10
|
|
|
|
pop rdx
|
|
pop rax
|
|
pop r8
|
|
ret
|
|
LoadTetromino ENDP
|
|
|
|
ClearTetroBuffer PROC
|
|
push rbx
|
|
lea rax, tetroBuffer
|
|
lea rbx, [tetroBuffer + TETRO_BUFFER_SIZE]
|
|
_loop:
|
|
mov byte ptr [rax], 0
|
|
inc rax
|
|
cmp rax, rbx
|
|
je _loop_break
|
|
jmp _loop
|
|
_loop_break:
|
|
pop rbx
|
|
ret
|
|
ClearTetroBuffer ENDP
|
|
|
|
ClearTetroRotateBuffer PROC
|
|
push rbx
|
|
lea rax, tetroBufferRotateTmp
|
|
lea rbx, [tetroBufferRotateTmp + TETRO_BUFFER_SIZE]
|
|
_loop:
|
|
mov byte ptr [rax], 0
|
|
inc rax
|
|
cmp rax, rbx
|
|
je _loop_break
|
|
jmp _loop
|
|
_loop_break:
|
|
pop rbx
|
|
ret
|
|
ClearTetroRotateBuffer ENDP
|
|
|
|
CopyTetroTmpToActual PROC
|
|
push rcx
|
|
xor rcx, rcx ; i = 0
|
|
push rdx
|
|
mov rdx, TETRO_BUFFER_SIZE ; maxI = TBUFSIZE
|
|
push r8 ; tmpChar
|
|
push r9 ; tmpAddr
|
|
|
|
_loop:
|
|
cmp rcx, rdx
|
|
je loop_break
|
|
|
|
; lea r8, byte ptr [tetroBuffer + rcx]
|
|
lea r9, tetroBufferRotateTmp
|
|
add r9, rcx
|
|
mov r8b, byte ptr [r9]
|
|
|
|
lea r9, tetroBuffer
|
|
add r9, rcx
|
|
mov byte ptr [r9], r8b
|
|
|
|
inc rcx
|
|
jmp _loop
|
|
|
|
loop_break:
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
ret
|
|
CopyTetroTmpToActual ENDP
|
|
|
|
ClearScreen PROC
|
|
LOCAL bgBrush: QWORD
|
|
LOCAL rectToInvalidate: RECT
|
|
|
|
enter 32, 0
|
|
push rcx
|
|
push rdx
|
|
|
|
mov rcx, 00220202h
|
|
call CreateSolidBrush
|
|
mov bgBrush, rax
|
|
|
|
; Get area to fill
|
|
mov rcx, [hwndWindow]
|
|
lea rdx, rectToInvalidate
|
|
call GetClientRect
|
|
|
|
mov rcx, [hdc]
|
|
lea rdx, rectToInvalidate
|
|
mov r8, bgBrush
|
|
call FillRect
|
|
|
|
; Destroy brush
|
|
mov rcx, bgBrush
|
|
call DeleteObject
|
|
|
|
pop rdx
|
|
pop rcx
|
|
leave
|
|
ret
|
|
ClearScreen ENDP
|
|
|
|
; Rotates the currently selected tetromino (in tetroBuffer) Clockwise
|
|
RotateTetroCW PROC
|
|
push rcx
|
|
push rdx
|
|
xor rcx, rcx ; x = 0
|
|
xor rdx, rdx ; y = 0
|
|
push r8
|
|
push r9
|
|
mov r8, TETRO_MAX_WIDTH ; maxWidth
|
|
mov r9, TETRO_MAX_HEIGHT ; maxHeight
|
|
push r10 ; readX
|
|
push r11 ; readY
|
|
push r12 ; tmpChar
|
|
push r13
|
|
push r14
|
|
xor r13, r13
|
|
xor r14, r14
|
|
mov r13b, [tetroBufferCurrentWidth]
|
|
mov r14b, [tetroBufferCurrentHeight]
|
|
|
|
call ClearTetroRotateBuffer
|
|
|
|
y_loop:
|
|
cmp rdx, r9
|
|
je y_loop_break
|
|
xor rcx, rcx ; x = 0
|
|
x_loop:
|
|
cmp rcx, r8
|
|
je x_loop_break
|
|
|
|
; putX = rcx
|
|
; putY = rdx
|
|
; Calculate ReadX, ReadY
|
|
mov r10, rdx ; readX = putY
|
|
|
|
mov r11, r14 ; readY = maxHeight
|
|
dec r11 ; --readY
|
|
sub r11, rcx ; readY -= putX
|
|
|
|
; readX = r10
|
|
; readY = r11
|
|
|
|
; Retrieve Read state
|
|
push rcx
|
|
push rdx
|
|
mov rcx, r10
|
|
mov rdx, r11
|
|
call GetTetroState
|
|
mov r12, rax
|
|
|
|
pop rdx
|
|
pop rcx
|
|
|
|
; Set new state
|
|
push r8
|
|
mov r8, r12
|
|
call SetTetroTmpState
|
|
pop r8
|
|
|
|
inc rcx
|
|
jmp x_loop
|
|
|
|
y_loop_continue:
|
|
x_loop_break:
|
|
inc rdx
|
|
jmp y_loop
|
|
|
|
y_loop_break:
|
|
|
|
mov byte ptr [tetroBufferCurrentWidth], r14b ; swap x and y
|
|
mov byte ptr [tetroBufferCurrentHeight], r13b
|
|
|
|
pop r14
|
|
pop r13
|
|
pop r12
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
|
|
; copy tmpRotateBuffer to tetro buffer
|
|
call CopyTetroTmpToActual
|
|
|
|
ret
|
|
RotateTetroCW ENDP
|
|
|
|
RotateTetroCCW PROC
|
|
call RotateTetroCW
|
|
call RotateTetroCW
|
|
call RotateTetroCW
|
|
ret
|
|
RotateTetroCCW ENDP
|
|
|
|
; (in) rcx X
|
|
; (in) rdx Y
|
|
GetTetroState PROC
|
|
push r8
|
|
push rdx
|
|
mov r8, TETRO_MAX_WIDTH
|
|
|
|
mov rax, rdx
|
|
mul r8
|
|
add rax, rcx
|
|
|
|
lea r8, tetroBuffer
|
|
add rax, r8
|
|
|
|
mov r8b, byte ptr [rax]
|
|
xor rax, rax
|
|
mov al, r8b
|
|
|
|
pop rdx
|
|
pop r8
|
|
ret
|
|
GetTetroState ENDP
|
|
|
|
; (in) rcx X
|
|
; (in) rdx Y
|
|
; (in) r8 state
|
|
SetTetroState PROC
|
|
push r8
|
|
push rdx
|
|
mov r8, TETRO_MAX_WIDTH
|
|
|
|
mov rax, rdx ; rax = Y
|
|
mul r8 ; rax *= GRID_SIZE_X
|
|
add rax, rcx ; rax += X
|
|
|
|
lea r8, tetroBuffer ;
|
|
add rax, r8 ; rax += &playField
|
|
|
|
pop rdx
|
|
pop r8
|
|
mov byte ptr [rax], r8b
|
|
|
|
ret
|
|
SetTetroState ENDP
|
|
|
|
SetTetroTmpState PROC
|
|
push r8
|
|
push rdx
|
|
mov r8, TETRO_MAX_WIDTH
|
|
|
|
mov rax, rdx ; rax = Y
|
|
mul r8 ; rax *= GRID_SIZE_X
|
|
add rax, rcx ; rax += X
|
|
|
|
lea r8, tetroBufferRotateTmp
|
|
add rax, r8 ; rax += &playField
|
|
|
|
pop rdx
|
|
pop r8
|
|
mov byte ptr [rax], r8b
|
|
|
|
ret
|
|
SetTetroTmpState ENDP
|
|
|
|
; (in) rcx X
|
|
; (in) rdx Y
|
|
GetFieldState PROC
|
|
push r8
|
|
push rdx
|
|
mov r8, GRID_SIZE_Y
|
|
cmp rdx, r8
|
|
jge false_oob
|
|
mov r8, GRID_SIZE_X
|
|
cmp rcx, r8
|
|
jge false_oob
|
|
|
|
mov rax, rdx
|
|
mul r8
|
|
add rax, rcx
|
|
|
|
lea r8, playField
|
|
add rax, r8
|
|
|
|
mov r8b, byte ptr [rax]
|
|
xor rax, rax
|
|
mov al, r8b
|
|
|
|
pop rdx
|
|
pop r8
|
|
ret
|
|
false_oob:
|
|
xor rax, rax
|
|
pop rdx
|
|
pop r8
|
|
ret
|
|
GetFieldState ENDP
|
|
|
|
; (in) rcx X
|
|
; (in) rdx Y
|
|
; (in) r8 state
|
|
SetFieldState PROC
|
|
push r8
|
|
push rdx
|
|
mov r8, GRID_SIZE_X
|
|
|
|
mov rax, rdx ; rax = Y
|
|
mul r8 ; rax *= GRID_SIZE_X
|
|
add rax, rcx ; rax += X
|
|
|
|
lea r8, playField ;
|
|
add rax, r8 ; rax += &playField
|
|
|
|
pop rdx
|
|
pop r8
|
|
mov byte ptr [rax], r8b
|
|
|
|
ret
|
|
SetFieldState ENDP
|
|
|
|
; (in) rcx x
|
|
; (in) rdx y
|
|
; (in) r8 color
|
|
RenderBlock PROC
|
|
LOCAL blockRect: RECT
|
|
LOCAL blockBrush: QWORD
|
|
|
|
push rcx
|
|
push rdx
|
|
push r8
|
|
mov r8, BLOCK_PIXEL_LENGTH
|
|
|
|
|
|
; Scale coordinates with PIXEL_LENGTH
|
|
push rdx
|
|
|
|
mov rax, rcx
|
|
mul r8
|
|
mov rcx, rax
|
|
|
|
pop rdx
|
|
|
|
mov rax, rdx
|
|
mul r8
|
|
mov rdx, rax
|
|
|
|
; Setup rect with scaled coordinates
|
|
mov dword ptr blockRect.left, ecx
|
|
add ecx, BLOCK_PIXEL_LENGTH
|
|
mov dword ptr blockRect.right, ecx
|
|
|
|
mov dword ptr blockRect.top, edx
|
|
add edx, BLOCK_PIXEL_LENGTH
|
|
mov dword ptr blockRect.bottom, edx
|
|
|
|
; Create brush
|
|
pop r8 ; restore original color
|
|
mov rcx, r8
|
|
push r9
|
|
|
|
sub rsp, 32 + 8
|
|
call CreateSolidBrush
|
|
|
|
mov blockBrush, rax
|
|
|
|
mov rcx, [hdc]
|
|
lea rdx, blockRect
|
|
mov r8, blockBrush
|
|
|
|
call FillRect
|
|
|
|
; Destroy brush
|
|
mov rcx, blockBrush
|
|
|
|
call DeleteObject
|
|
add rsp, 32 + 8
|
|
pop r9
|
|
|
|
pop rdx
|
|
pop rcx
|
|
ret
|
|
RenderBlock ENDP
|
|
|
|
; (in) rcx row index
|
|
CheckIfRowFull PROC
|
|
push rdx
|
|
xor rdx, rdx
|
|
push r8
|
|
xor r8, r8
|
|
mov r8b, byte ptr [GRID_SIZE_X]
|
|
|
|
_loop:
|
|
cmp rdx, r8
|
|
je _loop_break_true
|
|
|
|
push rcx
|
|
push rdx
|
|
mov rdx, rcx
|
|
mov rcx, r8
|
|
call GetFieldState
|
|
pop rdx
|
|
pop rcx
|
|
test rax, rax
|
|
jz _loop_break_false
|
|
|
|
jmp _loop
|
|
|
|
_loop_break_false:
|
|
mov rax, 0
|
|
jmp _loop_break
|
|
|
|
_loop_break_true:
|
|
mov rax, 1
|
|
_loop_break:
|
|
pop r8
|
|
pop rdx
|
|
|
|
ret
|
|
CheckIfRowFull ENDP
|
|
|
|
; (in) rcx row to clear
|
|
ClearAndMoveDown PROC
|
|
|
|
ret
|
|
ClearAndMoveDown ENDP
|
|
|
|
CheckRowClear PROC
|
|
push rcx
|
|
mov rcx, GRID_SIZE_Y - 1 ; row index
|
|
|
|
|
|
pop rdx
|
|
ret
|
|
CheckRowClear ENDP
|
|
|
|
RenderPlayerField PROC
|
|
push rcx
|
|
push rdx
|
|
xor rcx, rcx ; x = 0
|
|
xor rdx, rdx ; y = 0
|
|
push r8
|
|
push r9
|
|
mov r8, GRID_SIZE_X ; maxX
|
|
mov r9, GRID_SIZE_Y ; maxY
|
|
push r10
|
|
push r11
|
|
|
|
; outer Y loop
|
|
loop_y:
|
|
cmp rdx, r9
|
|
je loop_y_break
|
|
|
|
xor rcx, rcx
|
|
; do x loop
|
|
loop_x:
|
|
cmp rcx, r8
|
|
je loop_x_break
|
|
|
|
call GetFieldState
|
|
cmp al, 1 ; Set field;
|
|
je draw_single_block
|
|
|
|
jmp loop_x_continue
|
|
|
|
draw_single_block:
|
|
push r8
|
|
mov r8, 00FF0000h
|
|
call RenderBlock
|
|
pop r8
|
|
jmp loop_x_continue
|
|
|
|
loop_x_continue:
|
|
inc rcx
|
|
jmp loop_x
|
|
loop_x_break:
|
|
inc rdx
|
|
jmp loop_y
|
|
|
|
loop_y_break:
|
|
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
|
|
ret
|
|
RenderPlayerField ENDP
|
|
|
|
RenderPlayer PROC
|
|
push rax
|
|
push rbx
|
|
push rcx
|
|
push rdx
|
|
push r8
|
|
push r9
|
|
push r10
|
|
xor rax, rax ; x
|
|
xor rbx, rbx ; y
|
|
xor rcx, rcx
|
|
mov cl, byte ptr [tetroBufferCurrentWidth] ; maxX
|
|
xor rdx, rdx
|
|
mov dl, byte ptr [tetroBufferCurrentHeight] ; maxY
|
|
xor r8, r8
|
|
mov r8b, byte ptr [playerPosX] ; playerX
|
|
xor r9, r9
|
|
mov r9b, byte ptr [playerPosY] ; playerY
|
|
|
|
_loopY:
|
|
cmp bl, dl ; if (y == maxY)
|
|
je _loopY_break
|
|
|
|
_loopX:
|
|
cmp al, cl ; if (x == maxX)
|
|
je _loopX_break
|
|
; inner loop body
|
|
|
|
; check if player tetro block is set
|
|
|
|
push rcx
|
|
push rdx
|
|
mov rcx, rax
|
|
mov rdx, rbx
|
|
push rax
|
|
call GetTetroState
|
|
mov r10, rax
|
|
pop rax
|
|
pop rdx
|
|
pop rcx
|
|
|
|
; tetro state is now in r10
|
|
test r10, r10
|
|
jz _loopX_continue
|
|
|
|
push rcx
|
|
push rdx
|
|
mov rcx, rax
|
|
add rcx, r8
|
|
mov rdx, rbx
|
|
add rdx, r9
|
|
push r8
|
|
mov r8, 000000FFh ; Todo state to color
|
|
push rax
|
|
call RenderBlock
|
|
pop rax
|
|
pop r8
|
|
|
|
pop rdx
|
|
pop rcx
|
|
|
|
_loopX_continue:
|
|
inc al
|
|
jmp _loopX
|
|
|
|
_loopX_break:
|
|
_loopY_continue:
|
|
inc bl
|
|
xor rax, rax
|
|
jmp _loopY
|
|
|
|
_loopY_break:
|
|
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
pop rbx
|
|
pop rax
|
|
ret
|
|
RenderPlayer ENDP
|
|
|
|
GameUpdate PROC
|
|
call MovePlayerDown
|
|
ret
|
|
GameUpdate ENDP
|
|
|
|
GamePaint PROC
|
|
LOCAL ps: QWORD
|
|
|
|
sub rsp, 72
|
|
mov ps, rsp
|
|
|
|
mov rcx, hwndWindow
|
|
mov rdx, ps
|
|
call BeginPaint
|
|
mov [hdc], rax
|
|
|
|
call ClearScreen
|
|
|
|
call RenderPlayerField
|
|
|
|
call RenderPlayer
|
|
|
|
|
|
|
|
mov rcx, [hwndWindow]
|
|
mov rdx, ps
|
|
call EndPaint
|
|
|
|
add rsp, 72
|
|
|
|
ret
|
|
GamePaint ENDP
|
|
|
|
RequestPaint PROC
|
|
push rcx
|
|
push rdx
|
|
push r8
|
|
mov rcx, [hwndWindow]
|
|
mov rdx, 0
|
|
mov r8, 0
|
|
call InvalidateRect
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
ret
|
|
RequestPaint ENDP
|
|
|
|
IsPlayerJammedInPlayfield PROC
|
|
xor rax, rax
|
|
push rcx
|
|
push rdx
|
|
;xor rcx, rcx ; x = 0
|
|
xor rdx, rdx ; y = 0
|
|
push r8
|
|
push r9
|
|
mov r8b, [tetroBufferCurrentWidth] ; maxX
|
|
mov r9b, [tetroBufferCurrentHeight] ; maxY
|
|
|
|
loopY:
|
|
cmp dl, r9b ; if(y == maxY)
|
|
je loopY_break
|
|
xor rcx, rcx
|
|
loopX:
|
|
cmp cl, r8b ; if(x == maxX)
|
|
je loopX_break
|
|
|
|
call GetTetroState
|
|
test rax, rax
|
|
jz loopX_continue
|
|
; check if there is a block in playfield at [x + playerPosX, y + playerPosY]
|
|
; if yes, do loopY_break and set rax to 1
|
|
|
|
add cl, byte ptr [playerPosX]
|
|
add dl, byte ptr [playerPosY]
|
|
call GetFieldState
|
|
test rax, rax
|
|
jz noEarlyReturn
|
|
mov rax, 1
|
|
jmp loopY_break
|
|
|
|
noEarlyReturn:
|
|
sub cl, byte ptr [playerPosX]
|
|
sub dl, byte ptr [playerPosY]
|
|
|
|
loopX_continue:
|
|
inc cl
|
|
jmp loopX
|
|
loopX_break:
|
|
loopY_continue:
|
|
inc dl
|
|
jmp loopY
|
|
loopY_break:
|
|
|
|
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
ret
|
|
IsPlayerJammedInPlayfield ENDP
|
|
|
|
IsPlayerJammedInBounds PROC
|
|
mov rax, 0
|
|
push rcx
|
|
push rbx
|
|
mov bl, byte ptr [playerPosX]
|
|
cmp bl, -1
|
|
jle return_true
|
|
|
|
mov rcx, GRID_SIZE_X
|
|
sub cl, byte ptr [tetroBufferCurrentWidth]
|
|
cmp bl, cl
|
|
jge return_true
|
|
|
|
mov bl, byte ptr [playerPosY]
|
|
mov rcx, GRID_SIZE_Y
|
|
sub cl, byte ptr [tetroBufferCurrentHeight]
|
|
cmp bl, cl
|
|
jge return_true
|
|
|
|
|
|
jmp return_false
|
|
|
|
return_true:
|
|
mov rax, 1
|
|
return_false:
|
|
pop rbx
|
|
pop rcx
|
|
ret ; todo
|
|
IsPlayerJammedInBounds ENDP
|
|
|
|
IsPlayerJammed PROC
|
|
mov rax, 0
|
|
|
|
call IsPlayerJammedInPlayfield
|
|
test rax, rax
|
|
jnz ret_true
|
|
|
|
call IsPlayerJammedInBounds
|
|
ret
|
|
|
|
ret_true:
|
|
mov rax, 1
|
|
ret_false:
|
|
ret
|
|
IsPlayerJammed ENDP
|
|
|
|
CommitToPlayField PROC
|
|
push rcx
|
|
push rdx
|
|
;xor rcx, rcx ; x = 0
|
|
xor rdx, rdx ; y = 0
|
|
push r8
|
|
push r9
|
|
mov r8b, [tetroBufferCurrentWidth] ; maxX
|
|
mov r9b, [tetroBufferCurrentHeight] ; maxY
|
|
|
|
loopY:
|
|
cmp dl, r9b ; if(y == maxY)
|
|
je loopY_break
|
|
xor rcx, rcx
|
|
loopX:
|
|
cmp cl, r8b ; if(x == maxX)
|
|
je loopX_break
|
|
|
|
call GetTetroState
|
|
test rax, rax
|
|
jz loopX_continue
|
|
; set block
|
|
|
|
add cl, byte ptr [playerPosX]
|
|
add dl, byte ptr [playerPosY]
|
|
push r8
|
|
mov r8, 1
|
|
call SetFieldState
|
|
pop r8
|
|
sub dl, byte ptr [playerPosY]
|
|
sub cl, byte ptr [playerPosX]
|
|
|
|
loopX_continue:
|
|
inc cl
|
|
jmp loopX
|
|
loopX_break:
|
|
loopY_continue:
|
|
inc dl
|
|
jmp loopY
|
|
loopY_break:
|
|
|
|
|
|
pop r9
|
|
pop r8
|
|
pop rdx
|
|
pop rcx
|
|
|
|
call CheckRowClear
|
|
ret
|
|
CommitToPlayField ENDP
|
|
|
|
MovePlayerDown PROC
|
|
inc [playerPosY]
|
|
call IsPlayerJammed
|
|
test rax, rax
|
|
jz cleanup
|
|
; Player is jammed
|
|
; Undo and commit
|
|
dec [playerPosY]
|
|
call CommitToPlayField
|
|
; Reset player position and new tetro
|
|
mov byte ptr [playerPosY], 0
|
|
call LoadRandomTetromino
|
|
|
|
decollide_loop:
|
|
call IsPlayerJammed
|
|
test rax, rax
|
|
jz decollide_break
|
|
dec [playerPosX]
|
|
jmp decollide_loop
|
|
|
|
decollide_break:
|
|
cleanup:
|
|
ret
|
|
MovePlayerDown ENDP
|
|
|
|
TryRotateCCW PROC
|
|
call RotateTetroCCW
|
|
call IsPlayerJammed
|
|
test rax, rax
|
|
jz doReturn
|
|
call RotateTetroCW
|
|
|
|
doReturn:
|
|
ret
|
|
TryRotateCCW ENDP
|
|
|
|
; (in) rcx keycode
|
|
OnKeyDown PROC
|
|
and ecx, 00FFFFFFh ; Remove repeat-count
|
|
|
|
cmp ecx, VK_W
|
|
je rotateCCW
|
|
cmp ecx, VK_S
|
|
je goDown
|
|
cmp ecx, VK_A
|
|
je goLeft
|
|
cmp ecx, VK_D
|
|
je goRight
|
|
|
|
jmp cleanup
|
|
|
|
goLeft:
|
|
dec [playerPosX]
|
|
call IsPlayerJammed
|
|
test rax, rax
|
|
jz goLeft_cleanup
|
|
inc [playerPosX]
|
|
goLeft_cleanup:
|
|
call RequestPaint
|
|
jmp cleanup
|
|
|
|
goRight:
|
|
inc [playerPosX]
|
|
call IsPlayerJammed
|
|
test rax, rax
|
|
jz goRight_cleanup
|
|
dec [playerPosX]
|
|
|
|
goRight_cleanup:
|
|
call RequestPaint
|
|
jmp cleanup
|
|
rotateCCW:
|
|
call TryRotateCCW
|
|
call RequestPaint
|
|
jmp cleanup
|
|
goDown:
|
|
call MovePlayerDown
|
|
mov byte ptr [skipNextGL], 1
|
|
call RequestPaint
|
|
jmp cleanup
|
|
|
|
cleanup:
|
|
ret
|
|
OnKeyDown ENDP
|
|
|
|
; (in) rcx hWnd
|
|
; (in) edx uMsg
|
|
; (in) r9 wParam
|
|
; (in) r10 lParam
|
|
; (out) rax LRESULT
|
|
WndProc PROC
|
|
enter 32, 0
|
|
|
|
cmp edx, WM_CREATE
|
|
je onWmCreate
|
|
|
|
cmp edx, WM_TIMER
|
|
je onWmTimer
|
|
|
|
cmp edx, WM_PAINT
|
|
je onWmPaint
|
|
|
|
cmp edx, WM_KEYDOWN
|
|
je onWmKeyDown
|
|
|
|
cmp edx, WM_ERASEBACKGROUND
|
|
je onWmEraseBackground
|
|
|
|
cmp edx, WM_DESTROY
|
|
je onWmDestroy
|
|
|
|
sub rsp, 32
|
|
call DefWindowProcA
|
|
add rsp, 32
|
|
jmp cleanup
|
|
|
|
onWmCreate:
|
|
; Create a timer for the game loop
|
|
mov rdx, 1
|
|
mov r8, 500
|
|
mov r9, 0
|
|
call SetTimer
|
|
|
|
xor rax, rax
|
|
jmp cleanup
|
|
|
|
onWmTimer:
|
|
mov r12b, byte ptr [skipNextGL]
|
|
test r12b, r12b
|
|
jnz skipGameUpdate
|
|
|
|
call GameUpdate
|
|
mov rcx, [hwndWindow]
|
|
mov rdx, 0
|
|
mov r8, 0
|
|
call InvalidateRect
|
|
skipGameUpdate:
|
|
mov byte ptr [skipNextGL], 0
|
|
|
|
xor rax, rax
|
|
|
|
jmp cleanup
|
|
|
|
onWmPaint:
|
|
call GamePaint
|
|
jmp cleanup
|
|
|
|
onWmKeyDown:
|
|
push rcx
|
|
mov rcx, r9
|
|
call OnKeyDown
|
|
pop rcx
|
|
jmp cleanup
|
|
|
|
onWmEraseBackground:
|
|
mov rax, 1
|
|
jmp cleanup
|
|
|
|
onWmDestroy:
|
|
xor rcx, rcx
|
|
call PostQuitMessage
|
|
xor rax, rax
|
|
|
|
cleanup:
|
|
leave
|
|
ret
|
|
|
|
handle_destroy:
|
|
mov rcx, 0
|
|
call ExitProcess
|
|
ret
|
|
WndProc ENDP
|
|
|
|
end
|