FreeBSD 64bit Assembler (amd64)

For FreeBSD, there are not many 64bit assembler (amd64) examples available on the web and virtually none that are a little bit more complex.

To change this, I ported a DOS Tetris game to 64bit FreeBSD, using SVGALIB to max VGA out with 320x200 and 256 colours - it is 2020 after all!

Screenshot

Jokes aside, this example shows how to set up an assembler program that uses C runtime functions and also links in additional libraries available in ports/packages.

Note: The keyboard handling could be smoother, but that would require changes that go beyond the scope of this small showcase.

Background

The amd64 ABI of FreeBSD follows in most aspects the Linux ABI, so you can frequently work with Linux examples and tutorials.

That means parameters to library functions are passed in this order (exluding floats):

1. rdi
2. rsi
3. rdx
4. rcx
5. r8
6. r9

Note that syscall has different calling conventions though.

There are some Linux tutorials that you probably want to look at as well.

Preparations

The assembler style is nasm, so you need to install the following packages:

$ pkg install nasm
...
$ pkg install svgalib

Building and Debugging

You can build the program with this little shell script that uses the system compiler (LLVM) to link with the needed libraries:

#!/bin/sh
# You need to have nasm and svgalib packages installed
nasm -w-error=label-redef-late -f elf64 -g -F dwarf tetris.asm
export CPATH=/usr/local/include
export LIBRARY_PATH=/usr/local/lib
cc -g -pipe tetris.o -o tetris -lvga -lvgagl -lm

Debugging can simply be done with lldb which comes with the FreeBSD base system.
Above shell script adds debug code so the symbols are available in the debugger.

Note: SVGALIB-programs need to be run as root.

The Code

The assembler code can also be found on github.

;Frame parameters
frameHeight equ 8 * 20
frameWidth equ 8 * 10
frameBorderWidth equ 5
frameBeginningX equ 50
frameBeginningY equ 10

nextPieceFrameHeight equ 8 * 5
nextPieceFrameWidth equ 8 * 5
nextPieceFrameBorderWidth equ 5
nextPieceFrameBeginningX equ 155
nextPieceFrameBeginningY equ 10

boardColor equ 0
pieceSize equ 8

scorePosX equ 160
scorePosY equ 100

gameOverPosX equ 120
gameOverPosY equ 80

; waitFactor*usleepMS should be approx 1sec
; If usleepMS is too long, we will miss keyboard input
; If it is too short, keypresses will be counted several times
waitFactor equ 10
usleepMS equ 100000

section .data
      gameOver: db "GAME OVER",0
      scoreString: db "SCORE",0




section .bss
        boardState: resb 20 * 10

        nextPieceType: resb 1
        pieceType: resb 1
        pieceCol: resb 1
        piecePos: resb 4
        piecePivotPos: resb 1

        temporaryPiecePos: resb 4

        waitTime: resb 1

        score: resw 1
        scoreAsString: resb 6     ; 5 digits+0


extern gl_write,gl_setfont,gl_font8x8,gl_setfontcolors,gl_setwritemode,vga_init,vga_setmode,vga_setcolor,gl_fillbox,gl_setcontextvga,vga_getkey,usleep,rand
extern exit,keyboard_init,keyboard_translatekeys,keyboard_update,keyboard_keypressed,keyboard_close

section .text

global main
main:
        ;Initialization
        sub rsp,8
        call vga_init

        mov rdi, 5              ; SVGALIB 5 = 320x200x256 (see vga.h)
        call vga_setmode
        mov rdi, 5              ; we need to init vgagl with the same mode
        call gl_setcontextvga
        mov rdi,8
        mov rsi,8
        mov rdx, [gl_font8x8]
        call gl_setfont         ; see man gl_write(3)
        mov rdi,2
        call gl_setwritemode
        mov rdi, 0
        mov rsi, 15
        call gl_setfontcolors
        call keyboard_init
        mov rdi, 7

        call drawFrame
        call drawNextPieceFrame
        jmp gameInProgres

endOfGame:
        call delayForLongWhile
        call displayGameOver
        call delayForLongWhile

        ;Return to previous video mode
        call keyboard_close
        mov rdi, 0
        call vga_setmode

        ;Finish program
        ; we can not simply call return because we might be called from within
        ; gameInProgress
        mov rdi, 0
        call exit

;/////Drawing FUNCTIONS////
;////////////////////////////////////////////////////////////
;Draw Rectangle, rax - begin point, rcx - Height, rbx - Width, rdi - color
drawRect:
    xor rdx, rdx                ; clear dx for ax division
    push rdi                    ; Save colour
    Push rcx                    ; Save height for later
    mov rcx, 320                ; divisor in cx

    div cx                      ; divide begin point/320 so we get y in ax and x in dx (remainder)

    mov rdi, rdx                ; x1 (param 1)
    mov rsi, rax                ; y1 (param 2)
    mov rdx, rbx                ; width (param 3)
    pop rcx                     ; height (param 4, fetch again from stack)
    pop r8                      ; Pass colour
    call gl_fillbox
    mov rdi, r8                 ; ...and restore colour
    ret

;////////////////////////////////////////////////////////////
drawFrame:
    mov rdi, 7                    ; colour

    ;Gora
    mov rax, frameBeginningY * 320 + frameBeginningX
    mov rcx, frameBorderWidth
    mov rbx, frameWidth + 2 * frameBorderWidth
    call drawRect

    ;Lewa
    mov rax, (frameBeginningY + frameBorderWidth) * 320 + frameBeginningX
    mov rcx, frameHeight
    mov rbx, frameBorderWidth
    call drawRect

    ;Prawa
    mov rax, (frameBeginningY + frameBorderWidth) * 320 + frameBeginningX + frameWidth + frameBorderWidth
    mov rcx, frameHeight
    mov rbx, frameBorderWidth
    call drawRect

    ;Dol
    mov rax, (frameBeginningY + frameHeight + frameBorderWidth) * 320 + frameBeginningX
    mov rcx, frameBorderWidth
    mov rbx, frameWidth + 2 * frameBorderWidth
    call drawRect

  ret

;////////////////////////////////////////////////////////////
drawNextPieceFrame:
    mov rdi, 7

    ;Gora
    mov rax, nextPieceFrameBeginningY * 320 + nextPieceFrameBeginningX
    mov rcx, nextPieceFrameBorderWidth
    mov rbx, nextPieceFrameWidth + 2 * nextPieceFrameBorderWidth
    call drawRect

    ;Lewa
    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth) * 320 + nextPieceFrameBeginningX
    mov rcx, nextPieceFrameHeight
    mov rbx, nextPieceFrameBorderWidth
    call drawRect

    ;Prawa
    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth) * 320 + nextPieceFrameBeginningX + nextPieceFrameWidth + nextPieceFrameBorderWidth
    mov rcx, nextPieceFrameHeight
    mov rbx, nextPieceFrameBorderWidth
    call drawRect

    ;Dol
    mov rax, (nextPieceFrameBeginningY + nextPieceFrameHeight + nextPieceFrameBorderWidth) * 320 + nextPieceFrameBeginningX
    mov rcx, nextPieceFrameBorderWidth
    mov rbx, nextPieceFrameWidth + 2 * nextPieceFrameBorderWidth
    call drawRect

    ret

;////////////////////////////////////////////////////////////
drawBoardState:
    mov rbx, 200

drawBoardStateLoop1:
        dec rbx
        push rbx

        movzx rdi, byte[boardState + rbx]
        mov rax, rbx

        call drawOneSquare

        pop rbx
        cmp rbx, 0
    jne drawBoardStateLoop1

    ret

;////////////////////////////////////////////////////////////
clearBoard:
  mov rax, (frameBeginningY + frameBorderWidth) * 320 + frameBeginningX + frameBorderWidth
    mov rcx, frameHeight
    mov rbx, frameWidth
    mov rdi, boardColor
    call drawRect

    ret

;////////////////////////////////////////////////////////////
drawTetromino:
    movzx rax, byte[piecePos]
    movzx rdi, byte[pieceCol]
    call drawOneSquare

    movzx rax, byte[piecePos + 1]
    movzx rdi, byte[pieceCol]
    call drawOneSquare

    movzx rax, byte[piecePos + 2]
    movzx rdi, byte[pieceCol]
    call drawOneSquare

    movzx rax, byte[piecePos + 3]
    movzx rdi, byte[pieceCol]
    call drawOneSquare

    ret

;////////////////////////////////////////////////////////////
;rax - PieceNumber, rdi - color
drawOneSquare:

    mov BL, 10
    div BL

    ;AH - X, AL - Y
    mov CX, AX

    ;Calculate Y offset
    mov AL, CL
    xor AH, AH
    mov BX, 320 * pieceSize
    mul BX

    push rax
    ;Calculate X offset
    mov AL, CH
    xor AH, AH
    mov BX, pieceSize
    mul BX

    pop rdx

    ;Move to fit frame
    add AX, DX
    add AX, (frameBeginningY + frameBorderWidth) * 320 + frameBeginningX + frameBorderWidth

    mov BX, pieceSize
    mov CX, pieceSize

    jmp drawRect

;/////GAME FUNCTIONS////
;////////////////////////////////////////////////////////////
gameInProgres: ;-TO DO
    mov byte[waitTime], waitFactor
    call generateNextPieceNumber
placeNext:
    call updateBoard

    call generateNextPiece
    call generateNextPieceNumber
    call setNewDelay
    call writeScore

    jmp checkIfNotEnd

pieceInProgress:
        call clearBoard
        call drawBoardState
        call drawTetromino

        call scoreToString

        call getPlayerInput
        cmp AX, 0x0F0F

        ;AX = FFFF - Place Next Piece
        cmp AX, 0xFFFF
        je placeNext

        call moveOneDown
        cmp AX, 0xFFFF
        je placeNext

    jmp pieceInProgress

;---------
checkIfNotEnd:
    movzx rbx, byte[piecePos]
    mov AL, [boardState + rbx]
    cmp AL, boardColor
    jne endOfGame

    movzx rbx, byte[piecePos + 1]
    movzx rax, byte[boardState + rbx]
    cmp AL, boardColor
    jne endOfGame

    movzx rbx, byte[piecePos + 2]
    movzx rax, byte[boardState + rbx]
    cmp AL, boardColor
    jne endOfGame

    movzx rbx, byte[piecePos + 3]
    movzx rax, byte[boardState + rbx]
    cmp AL, boardColor
    jne endOfGame

    jmp pieceInProgress

;////////////////////////////////////////////////////////////
getPlayerInput:
    movzx rcx, byte[waitTime]
waitForKey:
        dec CX
        cmp CX, 0
        je noInput
        ; We need to run this in a loop with frequent polling.
        ; If we just check every second or so if a keyboard has been pressed,
        ; we miss out on key events with keyboard_update from svgalib

        push rcx                ; we need to save the loop counter across the svgalib calls
        ; the various push/pops are not super-elegant, yes, I know...
        call delayForWhile
        ; we need to ensure that we don't call this too frequently
        ; otherwise the same keypress is returned again
        call keyboard_update
        pop rcx

        mov rdi, 75             ;SCANCODE_CURSORLEFT
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne moveOneLeft

        mov rdi, 97             ;SCANCODE_CURSORBLOCKLEFT
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne moveOneLeft

        mov rdi, 80             ;SCANCODE_CURSORSDOWN
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne downKey

        mov rdi, 100            ;SCANCODE_CURSORBLOCKDOWN
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne downKey

        mov rdi, 77             ;SCANCODE_CURSORRIGHT
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne moveOneRight

        mov rdi, 98             ;SCANCODE_CURSORBLOCKRIGHT
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne moveOneRight

        mov rdi, 72             ;SCANCODE_CURSORUP
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne rotateCounterClockwise

        mov rdi, 95             ;SCANCODE_CURSORBLOCKUP
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne rotateCounterClockwise

        mov rdi, 76             ;SCANCODE_KEYPAD5
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne rotateClockwise

        mov rdi, 1              ;SCANCODE_ESCAPE
        push rcx
        call keyboard_keypressed
        pop rcx
        cmp rax, 0
        jne endOfGame

        jmp waitForKey

noInput:
    xor rax, rax
    ret

downKey:
    inc word[score]
    call moveOneDown
    xor rax, rax
ret

;////////////////////////////////////////////////////////////
generateNextPieceNumber:
    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth
    mov rcx, nextPieceFrameHeight
    mov rbx, nextPieceFrameWidth
    mov rdi, boardColor
    call drawRect

    ; Random for next piece
    call rand
    xor DX, DX
    mov CX, 7
    div CX
    mov byte[nextPieceType], DL

    mov AH, DL                  ; DOS Code expects next piece type in AH...


genFirstPiece:  ;I
    cmp AH, 0
    jne genSecondPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + 4
    mov rcx, pieceSize
    mov rbx, 4 * pieceSize
    mov rdi, 52
    call drawRect

    ret
genSecondPiece: ;J
    cmp AH, 1
    jne genThirdPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, 3 * pieceSize
    mov rdi, 32
    call drawRect

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + 3 * pieceSize
    mov rcx, pieceSize
    mov rbx, pieceSize
    mov rdi, 32
    call drawRect

    ret
genThirdPiece:  ;L
    cmp AH, 2
    jne genForthPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, 3 * pieceSize
    mov rdi, 43
    call drawRect

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, pieceSize
    mov rdi, 43
    call drawRect

    ret
genForthPiece:  ;O
    cmp AH, 3
    jne genFifthPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize + 4
    mov rcx, 2 * pieceSize
    mov rbx, 2 * pieceSize
    mov rdi, 45
    call drawRect

    ret
genFifthPiece:  ;S
    cmp AH, 4
    jne genSixthPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + 2 * pieceSize
    mov rcx, pieceSize
    mov rbx, 2 * pieceSize
    mov rdi, 48
    call drawRect

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, 2 * pieceSize
    mov rdi, 48
    call drawRect

    ret
genSixthPiece:  ;T
    cmp AH, 5
    jne genSeventhPiece

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, 3 * pieceSize
    mov rdi, 34
    call drawRect

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + 2 * pieceSize
    mov rcx, pieceSize
    mov rbx, pieceSize
    mov rdi, 34
    call drawRect

    ret
genSeventhPiece: ;Z

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + pieceSize
    mov rcx, pieceSize
    mov rbx, 2 * pieceSize
    mov rdi, 40
    call drawRect

    mov rax, (nextPieceFrameBeginningY + nextPieceFrameBorderWidth + 2 * pieceSize + 4) * 320 + nextPieceFrameBeginningX + nextPieceFrameBorderWidth + 2 * pieceSize
    mov rcx, pieceSize
    mov rbx, 2 * pieceSize
    mov rdi, 40
    call drawRect

    ret
;---------------------
generateNextPiece:
    mov AH, byte[nextPieceType]
    mov byte[pieceType], AH
    mov byte[piecePivotPos], 3


firstPiece: ;I
    cmp AH, 0
    jne secondPiece

    mov byte[piecePos], 13
    mov byte[piecePos + 1], 14
    mov byte[piecePos + 2], 15
    mov byte[piecePos + 3], 16
    mov byte[pieceCol], 52

    ret
secondPiece:    ;J
    cmp AH, 1
    jne thirdPiece

    mov byte[piecePos], 13
    mov byte[piecePos + 1], 14
    mov byte[piecePos + 2], 15
    mov byte[piecePos + 3], 25
    mov byte[pieceCol], 32

    ret
thirdPiece: ;L
    cmp AH, 2
    jne forthPiece

    mov byte[piecePos], 13
    mov byte[piecePos + 1], 14
    mov byte[piecePos + 2], 15
    mov byte[piecePos + 3], 23
    mov byte[pieceCol], 43

    ret
forthPiece: ;O
    cmp AH, 3
    jne fifthPiece

    mov byte[piecePos], 14
    mov byte[piecePos + 1], 15
    mov byte[piecePos + 2], 24
    mov byte[piecePos + 3], 25
    mov byte[pieceCol], 45

    ret
fifthPiece: ;S
    cmp AH, 4
    jne sixthPiece

    mov byte[piecePos], 14
    mov byte[piecePos + 1], 15
    mov byte[piecePos + 2], 23
    mov byte[piecePos + 3], 24
    mov byte[pieceCol], 48

    ret
sixthPiece: ;T
    cmp AH, 5
    jne seventhPiece

    mov byte[piecePos], 13
    mov byte[piecePos + 1], 14
    mov byte[piecePos + 2], 15
    mov byte[piecePos + 3], 24
    mov byte[pieceCol], 34

    ret
seventhPiece:   ;Z
    mov byte[piecePos], 13
    mov byte[piecePos + 1], 14
    mov byte[piecePos + 2], 24
    mov byte[piecePos + 3], 25
    mov byte[pieceCol], 40

    ret
;///////////////////////////////////////////////////////////
solidifyPiece:
    movzx rbx, byte[piecePos]
    mov AL, byte[pieceCol]
    mov byte[boardState + rbx], AL

    movzx rbx, byte[piecePos + 1]
    mov byte[boardState + rbx], AL

    movzx rbx, byte[piecePos + 2]
    mov byte[boardState + rbx], AL

    movzx rbx, byte[piecePos + 3]
    mov byte[boardState + rbx], AL

    ret
;///////////////// - TO DO
updateBoard:
    mov DL, 20
updateBoardLoop:
    dec DL
    call clearOneRow
    cmp DL, 0
    jne updateBoardLoop

    ret
clearOneRow:
;DL - row To clear
    mov BL, 10
    mov AL, DL
    mul BL

    xor rbx, rbx
    mov BX, AX

    mov CX, 10
clearOneRowLoop1:
    cmp byte[boardState + rbx], boardColor
    je notclearOneRow
    inc BX

    loop clearOneRowLoop1

    add word[score], 100

    cmp DL, 0
    je notclearOneRow

    push rdx
clearOneRowLoop2:
    dec DL
    call moveRowDown
    cmp DL, 1
    jne clearOneRowLoop2

    pop rdx
    jmp clearOneRow
notclearOneRow:
    ret

moveRowDown:
;DL - beginRow
    mov BL, 10
    mov AL, DL
    mul BL

    xor rbx, rbx
    mov BX, AX

    mov CX, 10
moveRowDownLoop:
    mov AL, byte[boardState + rbx]
    mov byte[boardState + rbx + 10], AL
    mov byte[boardState + rbx], boardColor

    inc BX
    loop moveRowDownLoop

    ret
;///////////////// - TO DO
moveOneDown:
    xor rax, rax
    mov rbx, 10
    xor DL, DL

    ;Check Frame Collision
    cmp byte[piecePos], 19 * 10
    jae cantMoveOneDown
    cmp byte[piecePos + 1], 19 * 10
    jae cantMoveOneDown
    cmp byte[piecePos + 2], 19 * 10
    jae cantMoveOneDown
    cmp byte[piecePos + 3], 19 * 10
    jae cantMoveOneDown

    ;Check Space Collsion
    xor rbx, rbx
    mov BL, byte[piecePos]
    add BL, 10
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneDown
    mov BL, byte[piecePos + 1]
    add BL, 10
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneDown
    mov BL, byte[piecePos + 2]
    add BL, 10
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneDown
    mov BL, byte[piecePos + 3]
    add BL, 10
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneDown

    add byte[piecePos], 10
    add byte[piecePos + 1], 10
    add byte[piecePos + 2], 10
    add byte[piecePos + 3], 10

    add byte[piecePivotPos], 10

    xor rax, rax
    ret
cantMoveOneDown:
    call solidifyPiece
    mov rax, 0xFFFF
    ret
;-----------------------------------
moveOneLeft:
    mov BL, 10

    ;Check Frame Collision
    xor AX, AX
    mov AL, byte[piecePos]
    div BL
    cmp AH, 0
    je cantMoveOneLeft
    xor AX, AX
    mov AL, byte[piecePos + 1]
    div BL
    cmp AH, 0
    je cantMoveOneLeft
    xor AX, AX
    mov AL, byte[piecePos + 2]
    div BL
    cmp AH, 0
    je cantMoveOneLeft
    xor AX, AX
    mov AL, byte[piecePos + 3]
    div BL
    cmp AH, 0
    je cantMoveOneLeft

    ;Check Space Collsion
    xor rbx, rbx
    mov BL, byte[piecePos]
    dec BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneLeft
    mov BL, byte[piecePos + 1]
    dec BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneLeft
    mov BL, byte[piecePos + 2]
    dec BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneLeft
    mov BL, byte[piecePos + 3]
    dec BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneLeft

    dec byte[piecePos]
    dec byte[piecePos + 1]
    dec byte[piecePos + 2]
    dec byte[piecePos + 3]

    dec byte[piecePivotPos]

cantMoveOneLeft:
    xor AX, AX
    ret
;-----------------------------------
moveOneRight:
    mov BL, 10

    ;Check Frame Collision
    xor AX, AX
    mov AL, byte[piecePos]
    div BL
    cmp AH, 9
    je cantMoveOneRight
    xor AX, AX
    mov AL, byte[piecePos + 1]
    div BL
    cmp AH, 9
    je cantMoveOneRight
    xor AX, AX
    mov AL, byte[piecePos + 2]
    div BL
    cmp AH, 9
    je cantMoveOneRight
    xor AX, AX
    mov AL, byte[piecePos + 3]
    div BL
    cmp AH, 9
    je cantMoveOneRight

    ;Check Space Collsion
    xor rbx, rbx
    mov BL, byte[piecePos]
    inc BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneRight
    mov BL, byte[piecePos + 1]
    inc BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneRight
    mov BL, byte[piecePos + 2]
    inc BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneRight
    mov BL, byte[piecePos + 3]
    inc BL
    cmp byte[boardState + rbx], boardColor
    jne cantMoveOneRight

    inc byte[piecePos]
    inc byte[piecePos + 1]
    inc byte[piecePos + 2]
    inc byte[piecePos + 3]

    inc byte[piecePivotPos]

cantMoveOneRight:
    ret

rotateClockwise:
    ;CheckBoard
    mov rbx, 4

rotateClockwiseLoop1:
        dec BX
        push rbx

        xor AX, AX
        mov BL, 10
        mov AL, byte[piecePivotPos]
        div BL

        pop rbx
        cmp AH, 0
        jb cantRotateClockwise
        cmp AH, 6
        ja cantRotateClockwise
        cmp AL, 16
        ja cantRotateClockwise

        mov CX, AX
        xor AX, AX

        mov AL, byte[piecePos + rbx]
        push rbx

        mov BL, 10
        sub AL, byte[piecePivotPos]
        div BL
        mov DX, AX

        ;AH - X, AL - Y
        mov AH, DL
        cmp byte[pieceType], 0
        je rotateClockwiseSpc
        mov AL, 3
rotateClockwiseSpcBack:
        sub AL, DH
        mov DX, AX

        mov AL, DL
        mov BL, 10
        mul BL
        add AL, DH

        pop rbx
        add AL, byte[piecePivotPos]
        mov byte[temporaryPiecePos + rbx], AL

        cmp BX, 0
    jne rotateClockwiseLoop1

    xor rbx, rbx
    mov BL, byte[temporaryPiecePos]
    cmp byte[boardState + rbx], boardColor
    jne cantRotateClockwise
    mov BL, byte[temporaryPiecePos + 1]
    cmp byte[boardState + rbx], boardColor
    jne cantRotateClockwise
    mov BL, byte[temporaryPiecePos + 2]
    cmp byte[boardState + rbx], boardColor
    jne cantRotateClockwise
    mov BL, byte[temporaryPiecePos + 3]
    cmp byte[boardState + rbx], boardColor
    jne cantRotateClockwise

    mov AL, byte[temporaryPiecePos]
    mov byte[piecePos], AL
    mov AL, byte[temporaryPiecePos + 1]
    mov byte[piecePos + 1], AL
    mov AL, byte[temporaryPiecePos + 2]
    mov byte[piecePos + 2], AL
    mov AL, byte[temporaryPiecePos + 3]
    mov byte[piecePos + 3], AL

cantRotateClockwise:
    xor AX, AX
    ret

rotateClockwiseSpc:
    mov AL, 4
    jmp rotateClockwiseSpcBack
;/////////////////
rotateCounterClockwise:
    ;CheckBoard
    mov rbx, 4

rotateCounterClockwiseLoop1:
        dec BX
        push rbx

        xor AX, AX
        mov BL, 10
        mov AL, byte[piecePivotPos]
        div BL

        pop rbx
        cmp AH, 0
        jb cantRotateCounterClockwise
        cmp AH, 6
        ja cantRotateCounterClockwise
        cmp AL, 16
        ja cantRotateCounterClockwise

        mov CX, AX
        xor AX, AX

        mov AL, byte[piecePos + rbx]
        push rbx

        mov BL, 10
        sub AL, byte[piecePivotPos]
        div BL
        mov DX, AX

        ;AH - X, AL - Y
        mov AL, DH
        cmp byte[pieceType], 0
        je rotateCounterClockwiseSpc
        mov AH, 3
rotateCounterClockwiseSpcBack:
        sub AH, DL
        mov DX, AX

        mov AL, DL
        mov BL, 10
        mul BL
        add AL, DH

        pop rbx
        add AL, byte[piecePivotPos]
        mov byte[temporaryPiecePos + rbx], AL

        cmp BX, 0
    jne rotateCounterClockwiseLoop1

    ;xor BX, BX
    movzx r8, byte[temporaryPiecePos]
    cmp byte[boardState + r8], boardColor
    jne cantRotateCounterClockwise
    movzx r8, byte[temporaryPiecePos + 1]
    cmp byte[boardState + r8], boardColor
    jne cantRotateCounterClockwise
    movzx r8, byte[temporaryPiecePos + 2]
    cmp byte[boardState + r8], boardColor
    jne cantRotateCounterClockwise
    movzx r8, byte[temporaryPiecePos + 3]
    cmp byte[boardState + r8], boardColor
    jne cantRotateCounterClockwise

    mov AL, byte[temporaryPiecePos]
    mov byte[piecePos], AL
    mov AL, byte[temporaryPiecePos + 1]
    mov byte[piecePos + 1], AL
    mov AL, byte[temporaryPiecePos + 2]
    mov byte[piecePos + 2], AL
    mov AL, byte[temporaryPiecePos + 3]
    mov byte[piecePos + 3], AL

cantRotateCounterClockwise:
    xor AX, AX
    ret

rotateCounterClockwiseSpc:
    mov AH, 4
    jmp rotateCounterClockwiseSpcBack
;/////////////////
;Delay for one/10 second (loop in input is 10 times)
delayForWhile:
    mov rdi, usleepMS
    call usleep

    ret
;--------------------
delayForLongWhile:
    mov rdi, 0x000F8480
    call usleep

    ret
;/////////////////
setNewDelay:
    cmp byte[waitTime], 1
    jb noSetNewDelat

    mov AX, word[score]

    xor DX, DX
    mov BX, 1000
    div BX

    mov DX, AX
    mov AX, 10

    cmp AX, DX
    jl set1Delay
    sub AX, DX

    mov byte[waitTime], AL
noSetNewDelat:
    ret

set1Delay:
    mov byte[waitTime], 1
    ret
;/////////////////
scoreToString:
    xor DX, DX
    mov AX, word[score]
    mov BX, 10000
    div BX
    add AX, 48
    mov byte[scoreAsString], AL

    mov AX, DX
    xor DX, DX
    mov BX, 1000
    div BX
    add AX, 48
    mov byte[scoreAsString + 1], AL

    mov AX, DX
    xor DX, DX
    mov BX, 100
    div BX
    add AX, 48
    mov byte[scoreAsString + 2], AL

    mov AX, DX
    xor DX, DX
    mov BX, 10
    div BX
    add AX, 48
    mov byte[scoreAsString + 3], AL
    add DX, 48
    mov byte[scoreAsString + 4], DL
    mov byte[scoreAsString + 5], 0 ; 0 for C string end

    mov BX, 5
    mov DL, 24

    mov rdi, scorePosX
    mov rsi, scorePosY-15
    mov rdx, scoreString
    call gl_write

    mov rdi, scorePosX                 ; x of position
    mov rsi, scorePosY                 ; y of position
    mov rdx, scoreAsString      ; position of string (=1 character)
    call gl_write

  ret
;////////////////////////
writeScore:

    mov rdi, scorePosX
    mov rsi, scorePosY-15
    mov rdx, scoreString
    call gl_write

    mov rdi, scorePosX                 ; x of position
    mov rsi, scorePosY                 ; y of position
    mov rdx, scoreAsString      ; position of string (=1 character)
    call gl_write

    ret

;----------------
displayGameOver:
    xor rax, rax
    mov rcx, 200
    mov rbx, 320
    mov rdi, boardColor
    call drawRect

    mov rdi, gameOverPosX                 ; x of position
    mov rsi, gameOverPosY                 ; y of position
    mov rdx, gameOver           ; position of string (=1 character)
    call gl_write

ret