В этой работе намного проще посмотреть непосредственно решения и почитать комментарии к коду, чем читать теоретическое приложение к работе. Если вам все же что-то не понятно - кидайте в issues
Впрочем зная, что основная масса народу не будет делать эту лабу так, как сделал ее я, сюда вряд ли кто-то заглянет)
### Касаемо Makefile
Для того чтобы не писать много команд для однотипной и монотонной сборки проекта, был написан простой Makefile. Однако работает он следующим образом: он принимает название цели сборки и ищет файл с именем цели и расширением .asm. Если не находит - не собирает цель.
Важно заметить, что он не умеет линковать другие файлы в ассемблер, потому что написан был не для этого. Он просто берет голый файл на NASM (обязательно) и выдает 64-битный ELF из этого единственного файла. Если вам необходимо что-то прилинковать к ассемблеру, то увы, придется собирать проект вручную или менять этот makefile
print_from_buf:; qword -> void; пытается вывести данные из буфера. аргумент не может быть больше 1024
PUSH_Mrax,rsi,rdx,rdi; сохраним регистры, которые точно попортим
PUSH_Mrax,rsi,rdx,rdi; сохраним регистры, которые точно попортим
movrdx,rdi; сколько выводить, в rdi содержится единственный аргумент
movrdx,rdi; сколько выводить, в rdi содержится единственный аргумент
movrsi,print_buf; откуда выводить. Адрес буфера
movrsi,print_buf; откуда выводить. Адрес буфера
movrdi,STDOUT; куда выводить. Дескриптор файла. В нашем случае стандартного вывода
movrdi,STDOUT; куда выводить. Дескриптор файла. В нашем случае стандартного вывода
movrax,1
movrax,1
RPOP_Mrax,rsi,rdx,rdi
pushrcx
syscall
syscall
poprcx
RPOP_Mrax,rsi,rdx,rdi; вернем значения регистров
RPOP_Mrax,rsi,rdx,rdi; вернем значения регистров
ret
ret
read_buf:; none -> void. Пытается заполнить буфер из стандартного ввода
read_to_buf:; none -> void. Пытается заполнить буфер из стандартного ввода
PUSH_Mrax, rdi,rsi
PUSH_Mrdi,rsi,rdx
movrdi,STDIN; откуда читать (дескриптор файла)
movrdi,STDIN; откуда читать (дескриптор файла)
movrsi,input_buf; куда читать
movrsi,input_buf; куда читать
movrdx,input_size; Сколько пытаемся читать
movrdx,input_size; Сколько пытаемся читать
movrax,0; системный вызов чтения
movrax,0; системный вызов чтения
RPOP_Mrax,rdi,rsi
syscall
RPOP_Mrdi,rsi,rdx; rax содержит количество прочитанных байт, а это важно
ret
ret
;poll_stdin:
poll_stdin:
; PUSH_M rdi, rsi, rdx
PUSH_M rdi,rsi, rdx
; mov rsi, 1 ; следим только за одним потоком
mov rsi,1 ; следим только за одним потоком
;
movrax,7; poll syscall
; RPOP_M rdi, rsi, rdx
movrdi,input_pollfd
; ret
movrsi,1; одна структура данных (изначально просто вызов принимает кучу таких)
movrdx,0; не ждать
syscall
RPOP_Mrdi,rsi,rdx
ret
print_number:; qword (rdi) -> void
; наша задача - сформировать массив символов.
; Нуа раз мы не знаем точно сколько их будет, формировать его будем прямо в стеке. нам повезло, что он растет вниз
; Нам очень повезло, что он растет вниз
; создадим 2 локальные переменные - одну для размера массива, другую для делителя
pushrbp
PUSH_Mrdx,rdi,rsi; сохранять регистры обязательно надо до того, как писать в стек символы
; создаем базу для адресации. Тогда первая будет на rbp - 8 - делитель, а вторая на rbp - 16 - количество
movrbp,rsp
; [WARNING] тут надо будет сохранить регистры
pushrsp; сохраню, потому что после всей вакханалии я концов не сыщу
subrsp,16; выделяем место под 3 переменные
movqword[rbp-16],10; пусть и жирно, но операнд обязан быть 64 разрядным для корректного деления
movqword[rbp-24],0; счетчик
movrax,rdi
pushbyte0; при выводе он ориентируется на это как на конец строки
.division_loop:
xorrdx,rdx; обнулим найденый остаток. (он просто еще и при делении принимает участие)
divqword[rbp-16]
DIGIT_TO_ASCIIdl
PUSHR8dl; поскольку в процессор не завезли возможность закинуть в стек 8 битный регистр, я им немного помог макросами
incqword[rbp-24]; увеличиваем счетчик на единицу
testrax,rax; делает and поразрядное с самим собой. Меня интересует, лежит ли в rax ноль
jnz.division_loop; если в rax не ноль, то продолжаем цикл
; выводим число
movrax,1
movrdi,STDOUT
movrsi,rsp
movrdx,[rbp-24]; уже не надо очищать, потому что в конце я просто восстановлю как было
pushrcx
syscall
poprcx
movrsp,[rbp-8]
RPOP_Mrdx,rdi,rsi
poprbp
ret
_start:
_start:
movrcx,src_size
movrbp,rsp
movrsi,src
; Создадим 2 локальные переменные для аккумулятора размером 8 байт и для математических нужд 8 байт.
movrdi,print_buf
; аккумулятор будет по адресу rbp - 8, а временная по rbp - 16
subrsp,16
; потом я не удержался и завел еще одну переменную - сколько мы успели написать в массив
subrsp,2; массив все равно размером всего 512, делать переменную больше нет смысла. rbp - 18
xorrax,rax; обнуляем регистр
movrsi,input_buf
.transfer:; в цикле передаем данные, попутно конвертируя их в ascii
movrdi,array
.read_loop:
callread_to_buf; системный вызов read вернет количество прочитаных байтов
movrcx,rax; сколько байтов прочиталось, столько и обработаем
; обработаем информацию
xorrax,rax; обнулим на всякий пожарный
jmp.read_byte
.separator_occured:
decrcx
movrax,[rbp-8]
stosq
xorrax,rax
incword[rbp-18]
movqword[rbp-8],0
testrcx,rcx
jz.check_buf
.read_byte:; цикл чтения
lodsb
lodsb
DIGIT_TO_ASCIIrax
; проверим, цифра ли это. Если нет, то записываем в память то, что хранилось в локальной переменной
stosb
cmpal,'0'
loop.transfer
jl.separator_occured
cmpal,'9'
jg.separator_occured
mov[rdi+1],BYTE`\n`; Чтобы система не ругалась на отсутствие переноса
ASCII_TO_DIGITal; Если цифра, то конвертируем ее из ascii
; Поскольку умножение и деление можно сделать только через регистр, придется извратиться
PUSH_Mrax,rdx
movrax,[rbp-8]
movqword[rbp-16],10
mulqword[rbp-16]
mov[rbp-8],rax
RPOP_Mrax,rdx
add[rbp-8],rax; результат деления запишем в локальную переменную
loop.read_byte; читаем буфер ввода до конца
movrdi,src_size
.check_buf:
callpoll_stdin
testdword[revents],POLLIN
jnz.read_loop
; Теперь выведем прочитанный массив на экран
xorrcx,rcx
movcx,[rbp-18]
movrsi,array
callclean_print_buf
.output_loop:
lodsq
movrdi,rax
callprint_number
movbyte[print_buf],' '
movrdi,1
callprint_from_buf; печатаем ровно 1 пробел
loop.output_loop
movbyte[print_buf],`\n`
movrdi,1
callprint_from_buf
callprint_from_buf
exit:
exit:
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.