global _start %define STDIN 0 %define STDOUT 1 %define STDERR 2 section .data ; резервируем 1 килобайт для буффера ввода и вывода ; также в отдельной переменной сохраняем размер этого буфера print_buf: times 1024 db 0 buf_size equ $-print_buf input_buf: times 1024 db 0 ; буфер, в который будут читаться символы со стандартного ввода input_size equ $-input_buf array: times 512 dq 0 ; молимся, чтобы никому не пришло в голову писать так много arr_size equ $-array ; Для poll %define POLLIN 0x001 ; Есть ли что почитать с буфера ввода. Понадобится для продолжения ввода input_pollfd: dd STDIN dw POLLIN revents: dw 0 ; возвращаемые события section .text %macro DIGIT_TO_ASCII 1 ; макрос, принимающий один аргумент (регистр или память) add %1, '0' %endmacro %macro ASCII_TO_DIGIT 1 ; макрос, принимающий один аргумент (регистр или память) sub %1, '0' %endmacro %macro PUSH_M 1-* ; push many; пушит в порядке следования %rep %0 push %1 %rotate 1 %endrep %endmacro %macro POP_M 1-* ; pop many. читает в порядке следования %rep %0 pop %1 %rotate 1 %endrep %endmacro %macro RPOP_M 1-* ; pop many. читает в обратном порядке %rotate -1 %rep %0 pop %1 %rotate -1 %endrep %endmacro %macro PUSHR8 1; закинуть восьмибитный регистр в стек dec rsp mov [rsp], %1 %endmacro ; Передачу аргументов будем делать при помощи ABI - стандартная практика для linux ; Аргументы передаются в следующем порядке: rdi, rsi, rdx, rcx, r8, r9. Все, что не влезло, пушится в стек ; У передачи через стек тоже есть особенности, но их мы пока касаться не будем clean_print_buf: ; none -> void PUSH_M rax, rcx, rdi mov rcx, buf_size mov rdi, print_buf xor rax, rax ; будем заносить нули во всю память rep stosb RPOP_M rax, rcx, rdi ret print_from_buf: ; qword -> void; пытается вывести данные из буфера. аргумент не может быть больше 1024 PUSH_M rax, rsi, rdx, rdi ; сохраним регистры, которые точно попортим mov rdx, rdi ; сколько выводить, в rdi содержится единственный аргумент mov rsi, print_buf ; откуда выводить. Адрес буфера mov rdi, STDOUT; куда выводить. Дескриптор файла. В нашем случае стандартного вывода mov rax, 1 push rcx syscall pop rcx RPOP_M rax, rsi, rdx, rdi ; вернем значения регистров ret read_to_buf: ; none -> void. Пытается заполнить буфер из стандартного ввода PUSH_M rdi, rsi, rdx mov rdi, STDIN ; откуда читать (дескриптор файла) mov rsi, input_buf ; куда читать mov rdx, input_size ; Сколько пытаемся читать mov rax, 0 ; системный вызов чтения syscall RPOP_M rdi, rsi, rdx ; rax содержит количество прочитанных байт, а это важно ret poll_stdin: PUSH_M rdi, rsi, rdx mov rsi, 1 ; следим только за одним потоком mov rax, 7 ; poll syscall mov rdi, input_pollfd mov rsi, 1 ; одна структура данных (изначально просто вызов принимает кучу таких) mov rdx, 0 ; не ждать syscall RPOP_M rdi, rsi, rdx ret print_number: ; qword (rdi) -> void ; наша задача - сформировать массив символов. ; Ну а раз мы не знаем точно сколько их будет, формировать его будем прямо в стеке. нам повезло, что он растет вниз ; Нам очень повезло, что он растет вниз ; создадим 2 локальные переменные - одну для размера массива, другую для делителя push rbp PUSH_M rdx, rdi, rsi ; сохранять регистры обязательно надо до того, как писать в стек символы ; создаем базу для адресации. Тогда первая будет на rbp - 8 - делитель, а вторая на rbp - 16 - количество mov rbp, rsp ; [WARNING] тут надо будет сохранить регистры push rsp ; сохраню, потому что после всей вакханалии я концов не сыщу sub rsp, 16 ; выделяем место под 3 переменные mov qword [rbp - 16], 10 ; пусть и жирно, но операнд обязан быть 64 разрядным для корректного деления mov qword [rbp - 24], 0 ; счетчик mov rax, rdi push byte 0 ; при выводе он ориентируется на это как на конец строки .division_loop: xor rdx, rdx ; обнулим найденый остаток. (он просто еще и при делении принимает участие) div qword [rbp - 16] DIGIT_TO_ASCII dl PUSHR8 dl ; поскольку в процессор не завезли возможность закинуть в стек 8 битный регистр, я им немного помог макросами inc qword [rbp - 24] ; увеличиваем счетчик на единицу test rax, rax ; делает and поразрядное с самим собой. Меня интересует, лежит ли в rax ноль jnz .division_loop ; если в rax не ноль, то продолжаем цикл ; выводим число mov rax, 1 mov rdi, STDOUT mov rsi, rsp mov rdx, [rbp-24] ; уже не надо очищать, потому что в конце я просто восстановлю как было push rcx syscall pop rcx mov rsp, [rbp - 8] RPOP_M rdx, rdi, rsi pop rbp ret _start: mov rbp, rsp ; Создадим 2 локальные переменные для аккумулятора размером 8 байт и для математических нужд 8 байт. ; аккумулятор будет по адресу rbp - 8, а временная по rbp - 16 sub rsp, 16 ; потом я не удержался и завел еще одну переменную - сколько мы успели написать в массив sub rsp, 2 ; массив все равно размером всего 512, делать переменную больше нет смысла. rbp - 18 mov rsi, input_buf mov rdi, array .read_loop: call read_to_buf ; системный вызов read вернет количество прочитаных байтов mov rcx, rax ; сколько байтов прочиталось, столько и обработаем ; обработаем информацию xor rax, rax ; обнулим на всякий пожарный jmp .read_byte .separator_occured: dec rcx mov rax, [rbp - 8] stosq xor rax, rax inc word [rbp - 18] mov qword [rbp - 8], 0 test rcx, rcx jz .check_buf .read_byte: ; цикл чтения lodsb ; проверим, цифра ли это. Если нет, то записываем в память то, что хранилось в локальной переменной cmp al, '0' jl .separator_occured cmp al, '9' jg .separator_occured ASCII_TO_DIGIT al ; Если цифра, то конвертируем ее из ascii ; Поскольку умножение и деление можно сделать только через регистр, придется извратиться PUSH_M rax, rdx mov rax, [rbp - 8] mov qword [rbp - 16], 10 mul qword [rbp - 16] mov [rbp - 8], rax RPOP_M rax, rdx add [rbp - 8], rax ; результат деления запишем в локальную переменную loop .read_byte ; читаем буфер ввода до конца .check_buf: call poll_stdin test dword [revents], POLLIN jnz .read_loop ; Теперь выведем прочитанный массив на экран xor rcx, rcx mov cx, [rbp - 18] mov rsi, array call clean_print_buf .output_loop: lodsq mov rdi, rax call print_number mov byte [print_buf], ' ' mov rdi, 1 call print_from_buf ; печатаем ровно 1 пробел loop .output_loop mov byte [print_buf], `\n` mov rdi, 1 call print_from_buf exit: mov rax, 60 mov rdi, 0 syscall