; Write the copyDiagonal() procedure.

global _start

segment .data

EXIT  equ 1
READ  equ 3
WRITE equ 4

STDIN  equ 0
STDOUT equ 1

DIM_X equ 10
DIM_Y equ 10
BSIZE equ 40

grid db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'
     db '..........'

eol db 0xA, 0xA

segment .bss

    line_buffer resb BSIZE

    ; ASCII Display
    display_ascii  resb 10 ; 10 ASCII digits
    display_length equ $ - display_ascii

segment .text

_start:

    call printGrid
    call inputCells
    call printGrid

    push grid
    call copyDiagonal

    call printGrid

.exit:
    xor eax, eax
    inc eax
    xor ebx, ebx
    int 0x80

; ----------------------------------

copyDiagonal:

	; Fix the offset within the stack
	push ebp
	mov  ebp, esp

	; Preserve registers for caller we use
	push esi
	push edi
	push ecx

	; Offset from the grid's base on the main diagonal
	xor  esi, esi

	; Retrieve address of the grid from stack
	mov  ebx, [ebp + 8]

	; For every row of the grid...
	mov  ecx, DIM_Y

	; Offset on the secondary diagonal, initially 9
	mov  edi, ecx
	dec  edi

	; Main loop
.line:

	; Only copy live cells
	cmp  [ebx + esi], byte 'O'
	jne  .over

	; Match, copy to the secondary diagonal

	mov  [ebx + edi], byte 'O'

.over:
	; Adjust column indices on our diagonals for
	; the next row of the grid
	add  esi, DIM_X + 1
    add  edi, DIM_X - 1

	loop .line

	; Done, restore the context
	pop  ecx
	pop  edi
	pop  esi
	pop  ebp

	; The procedure cleans up the stack after itself
    ret  4

; ---------------------------------

printGrid:

    ; pusha saves all resgistrs

    push eax
    push ebx
    push ecx
    push edx
    push esi

;------------------------

    mov ecx, DIM_Y
    xor esi, esi

.print_lines:

    push ecx

        mov eax, WRITE
        mov ebx, STDOUT
        mov ecx, grid
        add ecx, esi
        mov edx, DIM_X
        int 0x80

        mov eax, WRITE
        mov ebx, STDOUT
        mov ecx, eol
        mov edx, 1
        int 0x80

    pop ecx

    add esi, DIM_X

    loop .print_lines

; ----------------------

    mov eax, WRITE
    mov ebx, STDOUT
    mov ecx, eol
    mov edx, 2
    int 0x80

    pop esi
    pop edx
    pop ecx
    pop ebx
    pop eax

    ret


inputCells:

    pusha

.loop_forever:

    ; read line
    mov eax, READ
    mov ebx, STDIN
    mov ecx, line_buffer ; line buffer
    mov edx, BSIZE  ; length buffer
    int 0x80

    ; parse the input
    ; edx - input length
    call parse

    ; -> bl = r, bh = c
    ;    al = 'g'
    ;    al = -1 (error)

    cmp al, 'g'
    je .end_input

    cmp al, -1
    ; jump over initialization
    je .loop_forever

    ; convert r,c to d
    ; bl = r, bh = c
    call r_c_to_address
    ; -> esi = d

    ; store 'O' at [grid + d]
    mov [grid + esi], byte 'O'

    jmp .loop_forever

.end_input:

    popa

    ret


parse:

    xor esi, esi
    xor edi, edi

    mov ecx, edx

.again:
    ; take one char at time
    ; until EOL or 2nd digit encountered

    cmp edi, 2
    je  .ret

    mov  al, [line_buffer + esi]

    cmp  al, 'g'
    je  .ret

    ; if it is a blank or a non-digit, skip

    ; if it is a first digit, convert to numeric
    ; and store in bl

    and  al, 0x0F
    cmp  al, 9

    jg   .next

    or   edi, edi

    jnz  .digitcount

    mov  bl, al

.digitcount:
    mov  bh, al
    inc  edi

    ; if it is a second digit, convert to numeric
    ; and store in bh

    ; if it is a 'g' ret it in al

    ; if one or both digits missing at EOL, return -1 in al

.next:
    inc  esi
    loop .again

.over:
    cmp  edi, 2
    je   .ret

    xor  al, al
    dec  al

.ret:
    ret

; bl = r, bh = c
; Output: esi = d = DIM_X * r + c
r_c_to_address:
    push ebx
    push eax

    mov al, bl
    mov bl, DIM_X

    mul    bl
    
    ; add bh to the result above
    ; ax = bl * al

    add al, bh

    mov esi, eax
    and esi, 0x0000FFFF

    pop eax
    pop ebx

    ret


to_ascii:
    mov  eax, ecx

    xor  ecx, ecx ; counter of how many ASCII digits we will have received
    xor  ebx, ebx
    xor  edi, edi

.again:
    or   eax, eax   ; eax = 0? if yes, stop converting else divide

    jnz  .div

    inc  ecx
    jmp  .ret

.div:
    mov  ebx, 10
    xor  edx, edx
    div  ebx

    or   dl, 0x30

    ; Put the current ASCII character into the the end of the display buffer
    mov  [display_ascii + display_length - 1 + edi], dl
    dec  edi
    inc  ecx

    jmp  .again

.ret:

    ; Shift all ASCII digits towards the beginning of the buffer
    mov  edx, ecx ; return value

    mov  ebx, display_ascii
    add  ebx, display_length + 1
    sub  ebx, ecx

    xor  edi, edi

.shift:
    mov  al, [ebx + edi]
    mov  [display_ascii + edi], al
    mov  byte [ebx + edi], ' '
    inc  edi
    loop .shift

    ret

