diff --git a/Bugtris.sln b/Bugtris.sln
new file mode 100644
index 0000000..44a87b6
--- /dev/null
+++ b/Bugtris.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35521.163 d17.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Bugtris", "Bugtris\Bugtris.vcxproj", "{22C82B7D-B379-4723-86F5-62DEC4D22BF0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Debug|x64.ActiveCfg = Debug|x64
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Debug|x64.Build.0 = Debug|x64
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Debug|x86.ActiveCfg = Debug|Win32
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Debug|x86.Build.0 = Debug|Win32
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Release|x64.ActiveCfg = Release|x64
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Release|x64.Build.0 = Release|x64
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Release|x86.ActiveCfg = Release|Win32
+ {22C82B7D-B379-4723-86F5-62DEC4D22BF0}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Bugtris/Bugtris.vcxproj b/Bugtris/Bugtris.vcxproj
new file mode 100644
index 0000000..93e8e41
--- /dev/null
+++ b/Bugtris/Bugtris.vcxproj
@@ -0,0 +1,145 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {22c82b7d-b379-4723-86f5-62dec4d22bf0}
+ Bugtris
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ main
+
+
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Bugtris/Bugtris.vcxproj.filters b/Bugtris/Bugtris.vcxproj.filters
new file mode 100644
index 0000000..aed0bfb
--- /dev/null
+++ b/Bugtris/Bugtris.vcxproj.filters
@@ -0,0 +1,27 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Quelldateien
+
+
+
+
+ Quelldateien
+
+
+
\ No newline at end of file
diff --git a/Bugtris/main.asm b/Bugtris/main.asm
new file mode 100644
index 0000000..e69de29
diff --git a/Bugtris/main2.asm b/Bugtris/main2.asm
new file mode 100644
index 0000000..f4ae8bf
--- /dev/null
+++ b/Bugtris/main2.asm
@@ -0,0 +1,1340 @@
+
+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