diff --git a/asm/mul.asm b/asm/mul.asm new file mode 100644 index 00000000..f2fed70a --- /dev/null +++ b/asm/mul.asm @@ -0,0 +1,375 @@ + section .text + + global _start +_start: + + sub rsp, 2 * 128 * 8 + lea rdi, [rsp + 128 * 8] + mov rcx, 128 + call read_long + mov rdi, rsp + call read_long + lea rsi, [rsp + 128 * 8] + + + + sub rsp, 2 * 128 * 8 ; выделяем место под ответ + mov r10, rsp + + push rdi + mov rcx, 256 + mov rdi, r10 + call set_zero ; нужно занулить место под ответ + mov rcx, 128 + pop rdi + + call mul_long_long + + + mov rcx, 256 ; подготовим регистры и выведем ответ + mov rdi, r10 + call write_long + + mov al, 0x0a + call write_char + + jmp exit + +; multiplies two long number +; rdi -- address of term #1 (long number) +; rsi -- address of term #2 (long number) +; rcx -- length of long numbers in qwords +; result: +; result is written to r10 +mul_long_long: + push rdi + push rsi + push rcx + + push rax + push rbx + push rbp + push r8 + push r9 + +; далее просто перебираем разряды обоих чисел, умножаем и прибавляем к результату + + xor r8, r8 +loop_outer: + + + xor r9, r9 +loop_inner: + + ; + ; [r10 + 8 * (r8 + r9)] += [rdi + 8 * r8] * [rsi + 8 * r9] грубо говоря + ; + + lea rax, [rdi + 8 * r8] + mov rax, [rax] ; грубо превратили индекс в значение, ну да ладно + lea rbx, [rsi + 8 * r9] + mov rbx, [rbx] + + + ; high:low + mul rbx ; rdx:rax = rax * rbx + + mov rbp, r8 + add rbp, r9 ; rbp = r8 + r9 + lea rbp, [r10 + 8 * rbp] ; rbp - индекс, куда надо прибавлять + + + add [rbp], rax ; прибавляем и ставим CF + lea rbp, [rbp + 8] + adc [rbp], rdx ; снова керри флаг +while_carry: + jnc end_of_while ; если CF = 0 то конец + lea rbp, [rbp + 8] + adc QWORD [rbp], 0 + jmp while_carry +end_of_while: + + + inc r9 + cmp rcx, r9 + ja loop_inner + + + inc r8 + cmp rcx, r8 + ja loop_outer + + + pop r9 + pop r8 + pop rbp + pop rbx + pop rax + + pop rcx + pop rsi + pop rdi + ret + +; adds 64-bit number to long number +; rdi -- address of summand #1 (long number) +; rax -- summand #2 (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; sum is written to rdi +add_long_short: + push rdi + push rcx + push rdx + + xor rdx,rdx +.loop: + add [rdi], rax + adc rdx, 0 + mov rax, rdx + xor rdx, rdx + add rdi, 8 + dec rcx + jnz .loop + + pop rdx + pop rcx + pop rdi + ret + +; multiplies long number by a short +; rdi -- address of multiplier #1 (long number) +; rbx -- multiplier #2 (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; product is written to rdi +mul_long_short: + push rax + push rdi + push rcx + + xor rsi, rsi +.loop: + mov rax, [rdi] + mul rbx + add rax, rsi + adc rdx, 0 + mov [rdi], rax + add rdi, 8 + mov rsi, rdx + dec rcx + jnz .loop + + pop rcx + pop rdi + pop rax + ret + +; divides long number by a short +; rdi -- address of dividend (long number) +; rbx -- divisor (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; quotient is written to rdi +; rdx -- remainder +div_long_short: + push rdi + push rax + push rcx + + lea rdi, [rdi + 8 * rcx - 8] + xor rdx, rdx + +.loop: + mov rax, [rdi] + div rbx + mov [rdi], rax + sub rdi, 8 + dec rcx + jnz .loop + + pop rcx + pop rax + pop rdi + ret + +; assigns a zero to long number +; rdi -- argument (long number) +; rcx -- length of long number in qwords +set_zero: + push rax + push rdi + push rcx + + xor rax, rax + rep stosq + + pop rcx + pop rdi + pop rax + ret + +; checks if a long number is a zero +; rdi -- argument (long number) +; rcx -- length of long number in qwords +; result: +; ZF=1 if zero +is_zero: + push rax + push rdi + push rcx + + xor rax, rax + rep scasq + + pop rcx + pop rdi + pop rax + ret + +; read long number from stdin +; rdi -- location for output (long number) +; rcx -- length of long number in qwords +read_long: + push rcx + push rdi + + call set_zero +.loop: + call read_char + or rax, rax + js exit + cmp rax, 0x0a + je .done + cmp rax, '0' + jb .invalid_char + cmp rax, '9' + ja .invalid_char + + sub rax, '0' + mov rbx, 10 + call mul_long_short + call add_long_short + jmp .loop + +.done: + pop rdi + pop rcx + ret + +.invalid_char: + mov rsi, invalid_char_msg + mov rdx, invalid_char_msg_size + call print_string + call write_char + mov al, 0x0a + call write_char + +.skip_loop: + call read_char + or rax, rax + js exit + cmp rax, 0x0a + je exit + jmp .skip_loop + +; write long number to stdout +; rdi -- argument (long number) +; rcx -- length of long number in qwords +write_long: + push rax + push rcx + + mov rax, 20 + mul rcx + mov rbp, rsp + sub rsp, rax + + mov rsi, rbp + +.loop: + mov rbx, 10 + call div_long_short + add rdx, '0' + dec rsi + mov [rsi], dl + call is_zero + jnz .loop + + mov rdx, rbp + sub rdx, rsi + call print_string + + mov rsp, rbp + pop rcx + pop rax + ret + +; read one char from stdin +; result: +; rax == -1 if error occurs +; rax \in [0; 255] if OK +read_char: + push rcx + push rdi + + sub rsp, 1 + xor rax, rax + xor rdi, rdi + mov rsi, rsp + mov rdx, 1 + syscall + + cmp rax, 1 + jne .error + xor rax, rax + mov al, [rsp] + add rsp, 1 + + pop rdi + pop rcx + ret +.error: + mov rax, -1 + add rsp, 1 + pop rdi + pop rcx + ret + +; write one char to stdout, errors are ignored +; al -- char +write_char: + sub rsp, 1 + mov [rsp], al + + mov rax, 1 + mov rdi, 1 + mov rsi, rsp + mov rdx, 1 + syscall + add rsp, 1 + ret + +exit: + mov rax, 60 + xor rdi, rdi + syscall + +; print string to stdout +; rsi -- string +; rdx -- size +print_string: + push rax + + mov rax, 1 + mov rdi, 1 + syscall + + pop rax + ret + + + section .rodata +invalid_char_msg: + db "Invalid character: " +invalid_char_msg_size: equ $ - invalid_char_msg diff --git a/asm/sub.asm b/asm/sub.asm new file mode 100644 index 00000000..aaa24b38 --- /dev/null +++ b/asm/sub.asm @@ -0,0 +1,309 @@ + section .text + + global _start +_start: + + sub rsp, 2 * 128 * 8 + lea rdi, [rsp + 128 * 8] + mov rcx, 128 + call read_long + mov rdi, rsp + call read_long + lea rsi, [rsp + 128 * 8] + call sub_long_long + + call write_long + + mov al, 0x0a + call write_char + + jmp exit + +; subtracts two long number +; rdi -- address of уменьшитель (long number) +; rsi -- address of уменьшаемое (long number) +; rcx -- length of long numbers in qwords +; result: +; res is written to rdi +sub_long_long: + push rdi + push rsi + push rcx + + clc +.loop: + mov rax, [rsi] + lea rsi, [rsi + 8] + sbb rax, [rdi] + mov [rdi], rax + lea rdi, [rdi + 8] + dec rcx + jnz .loop + + pop rcx + pop rsi + pop rdi + ret + +; adds 64-bit number to long number +; rdi -- address of summand #1 (long number) +; rax -- summand #2 (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; sum is written to rdi +add_long_short: + push rdi + push rcx + push rdx + + xor rdx,rdx +.loop: + add [rdi], rax + adc rdx, 0 + mov rax, rdx + xor rdx, rdx + add rdi, 8 + dec rcx + jnz .loop + + pop rdx + pop rcx + pop rdi + ret + +; multiplies long number by a short +; rdi -- address of multiplier #1 (long number) +; rbx -- multiplier #2 (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; product is written to rdi +mul_long_short: + push rax + push rdi + push rcx + + xor rsi, rsi +.loop: + mov rax, [rdi] + mul rbx + add rax, rsi + adc rdx, 0 + mov [rdi], rax + add rdi, 8 + mov rsi, rdx + dec rcx + jnz .loop + + pop rcx + pop rdi + pop rax + ret + +; divides long number by a short +; rdi -- address of dividend (long number) +; rbx -- divisor (64-bit unsigned) +; rcx -- length of long number in qwords +; result: +; quotient is written to rdi +; rdx -- remainder +div_long_short: + push rdi + push rax + push rcx + + lea rdi, [rdi + 8 * rcx - 8] + xor rdx, rdx + +.loop: + mov rax, [rdi] + div rbx + mov [rdi], rax + sub rdi, 8 + dec rcx + jnz .loop + + pop rcx + pop rax + pop rdi + ret + +; assigns a zero to long number +; rdi -- argument (long number) +; rcx -- length of long number in qwords +set_zero: + push rax + push rdi + push rcx + + xor rax, rax + rep stosq + + pop rcx + pop rdi + pop rax + ret + +; checks if a long number is a zero +; rdi -- argument (long number) +; rcx -- length of long number in qwords +; result: +; ZF=1 if zero +is_zero: + push rax + push rdi + push rcx + + xor rax, rax + rep scasq + + pop rcx + pop rdi + pop rax + ret + +; read long number from stdin +; rdi -- location for output (long number) +; rcx -- length of long number in qwords +read_long: + push rcx + push rdi + + call set_zero +.loop: + call read_char + or rax, rax + js exit + cmp rax, 0x0a + je .done + cmp rax, '0' + jb .invalid_char + cmp rax, '9' + ja .invalid_char + + sub rax, '0' + mov rbx, 10 + call mul_long_short + call add_long_short + jmp .loop + +.done: + pop rdi + pop rcx + ret + +.invalid_char: + mov rsi, invalid_char_msg + mov rdx, invalid_char_msg_size + call print_string + call write_char + mov al, 0x0a + call write_char + +.skip_loop: + call read_char + or rax, rax + js exit + cmp rax, 0x0a + je exit + jmp .skip_loop + +; write long number to stdout +; rdi -- argument (long number) +; rcx -- length of long number in qwords +write_long: + push rax + push rcx + + mov rax, 20 + mul rcx + mov rbp, rsp + sub rsp, rax + + mov rsi, rbp + +.loop: + mov rbx, 10 + call div_long_short + add rdx, '0' + dec rsi + mov [rsi], dl + call is_zero + jnz .loop + + mov rdx, rbp + sub rdx, rsi + call print_string + + mov rsp, rbp + pop rcx + pop rax + ret + +; read one char from stdin +; result: +; rax == -1 if error occurs +; rax \in [0; 255] if OK +read_char: + push rcx + push rdi + + sub rsp, 1 + xor rax, rax + xor rdi, rdi + mov rsi, rsp + mov rdx, 1 + syscall + + cmp rax, 1 + jne .error + xor rax, rax + mov al, [rsp] + add rsp, 1 + + pop rdi + pop rcx + ret +.error: + mov rax, -1 + add rsp, 1 + pop rdi + pop rcx + ret + +; write one char to stdout, errors are ignored +; al -- char +write_char: + sub rsp, 1 + mov [rsp], al + + mov rax, 1 + mov rdi, 1 + mov rsi, rsp + mov rdx, 1 + syscall + add rsp, 1 + ret + +exit: + mov rax, 60 + xor rdi, rdi + syscall + +; print string to stdout +; rsi -- string +; rdx -- size +print_string: + push rax + + mov rax, 1 + mov rdi, 1 + syscall + + pop rax + ret + + + section .rodata +invalid_char_msg: + db "Invalid character: " +invalid_char_msg_size: equ $ - invalid_char_msg