Files
solutions/03-asm-bios/task4.asm
Miheev Egor e1aeb3f2df feat: сделан первый прототип решения
Прототип еще не тестировался
2024-09-23 20:45:46 +03:00

209 lines
8.7 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
syscall
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] тут надо будет сохранить регистры
sub rsp, 16 ; выделяем место под 2 переменные
mov qword [rbp - 8], 10 ; пусть и жирно, но операнд обязан быть 64 разрядным для корректного деления
mov qword [rbp - 16], 0 ; счетчик
mov rax, rdi
.division_loop:
xor rdx, rdx ; обнулим найденый остаток. (он просто еще и при делении принимает участие)
div qword [rbp - 8]
DIGIT_TO_ASCII dl
PUSHR8 dl ; поскольку в процессор не завезли возможность закинуть в стек 8 битный регистр, я им немного помог макросами
inc qword [rbp - 16] ; увеличиваем счетчик на единицу
test rax, rax ; делает and поразрядное с самим собой. Меня интересует, лежит ли в rax ноль
jnz .division_loop ; если в rax не ноль, то продолжаем цикл
; выводим число
mov rax, 1
mov rdi, STDOUT
mov rsi, rsp
mov rdx, [rbp-16]
add rsp, 16 ; освобождаем память
RPOP_M rdx, rdi, rsi
pop rbp
_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
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 ax, rdx
mov rax, [rbp - 8]
mov qword [rbp - 16], 10
mul qword [rbp - 16]
mov [rbp - 8], rax
RPOP_M ax, rdx
add [rbp - 8], rax ; результат деления запишем в локальную переменную
loop .read_byte ; читаем буфер ввода до конца
.check_buf:
call poll_stdin
test BYTE [revents], POLLIN
jnz .read_loop
; Теперь выведем прочитанный массив на экран
xor rcx, rcx
mov cx, [rbp - 18]
.output_loop:
mov rsi, array
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