Compare commits
8 Commits
labs/cours
...
acc1ada69c
| Author | SHA1 | Date | |
|---|---|---|---|
| acc1ada69c | |||
| ca7fbed016 | |||
| 3412167a09 | |||
| 4c423c7acc | |||
| a338ac968e | |||
| 386c7be1a7 | |||
| 34d0c33e87 | |||
| 65d5c3f5c3 |
@ -2,3 +2,119 @@
|
|||||||
|
|
||||||
## Введение в низкоуровневое программирование. Встроенный отладчик. Встроенный Ассемблер
|
## Введение в низкоуровневое программирование. Встроенный отладчик. Встроенный Ассемблер
|
||||||
|
|
||||||
|
## Переписываем шаблон
|
||||||
|
|
||||||
|
Поскольку весь шаблонный текст написан под MS-DOS, мы очевидным образом не можем его использовать для решения задачи под linux.
|
||||||
|
|
||||||
|
Замены требуют следующие функции:
|
||||||
|
|
||||||
|
- getch
|
||||||
|
- delay
|
||||||
|
- inp
|
||||||
|
- bioskey
|
||||||
|
|
||||||
|
### getch
|
||||||
|
|
||||||
|
Наиболее простая замена будет для `getch()`, поскольку единственное ее назначение - ожидать нажатия клавиши. В этом контексте у линукса есть полноценная замена в виде `system("pause")`
|
||||||
|
|
||||||
|
### delay
|
||||||
|
|
||||||
|
Здесь уже несколько посложнее, потому что DOS'овский `delay` использует задержку в миллисекундах, а линуксовый `sleep` - в секундах. Поэтому используем функцию `usleep`. Она принимает время задержки в микросекундах, поэтому для получения миллисекунда нужно просто умножить на 1000. То есть код:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void delay(unsigned ms)
|
||||||
|
{
|
||||||
|
usleep(ms * 1000);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### bioskey
|
||||||
|
|
||||||
|
Из всех пока что самая сложная замена. Если вызвать `bioskey(1)`, то она вытаст 1 если какая либо клавиша была нажата и 0 если не была. при этом проверка происходит в моменте и не блокирует выполнение программы.
|
||||||
|
|
||||||
|
Для иммитации этого на линуксе нам потребуется неканонический режим ввода в терминал, а также сделать так, чтобы все печатаемое не выводилось в курсор. Этого можно добиться 2 способами:
|
||||||
|
|
||||||
|
1. Покурить гигагалактический томик по ассемблеру и узнать про системный вызов ioctl, после чего руками разметить область оперативной памяти, провести все системные вызовы, потом при помощи poll проверять наличие символов в буфере, обрабатывать ошибки и интегрировать функции через прототипы в наш код на C
|
||||||
|
2. Сдаться и выбрать путь языка C
|
||||||
|
|
||||||
|
Я уже сказал, что я из слабых, поэтому писать кусок на ассемблере как-то не горю желанием (хотя может когда-нибудь в будущем по просьбам напишу)
|
||||||
|
|
||||||
|
#### Зависимости
|
||||||
|
|
||||||
|
Язык программирования C имеет определенный уровень абстракции от конкретных системных вызовов и предоставляет нам несколько вещей:
|
||||||
|
|
||||||
|
- `<termios.h>` - структура данных, хранящая информацию о текущем состоянии терминала, а также удобные методы `tcgetattr` и `tcsetattr`
|
||||||
|
- `<unistd.h>` - Библиотека, используемая для унификации дескрипторов, битов и прочих унификаций
|
||||||
|
- `<stdlib.h>` - много чего, но нам для безопасности потребуется `atexit`, чтобы если что-то пошло не так, у нас не наебнулся терминал
|
||||||
|
|
||||||
|
Опционально берется `<stdio.h>` для целей адекватного вывода ошибок. Не обязательно, но предпочтительно
|
||||||
|
|
||||||
|
#### Реализация
|
||||||
|
|
||||||
|
Для начала нам необходимо сохранить свой текущий терминал, чтобы без проблем его восстановить в будущем, для этого заводим в памяти переменную (придется сделать ее глобальной, потому что на инкапсуляцию и защиту нет времени, нервов и желания)
|
||||||
|
|
||||||
|
```C
|
||||||
|
struct termios saved_attributes;
|
||||||
|
```
|
||||||
|
|
||||||
|
Далее сразу напишем функцию для восстановления
|
||||||
|
|
||||||
|
```C
|
||||||
|
void reset_input_mode()
|
||||||
|
{
|
||||||
|
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Здесь `STDIN_FILENO` - это дескриптор потока стандартного ввода (ввод с консоли по простяге). Вообще это число, но в `<unistd.h>` он вынесен в макрос для хоть какой-то унификации, `TCSANOW` - тоже число. В контексте функции `tcsetattr` оно заставляет изменениям в формате терминала вступить в силу немедленно, вне зависимости от того, есть ли еще в буфере текст на вывод. Другими вариантами могут стать:
|
||||||
|
|
||||||
|
- `TCSANOW` - применить изменения сразу при сигнале и продолжать предыдущий вывод с того же места, где он кончился
|
||||||
|
- `TCSADRAIN` - заставит сначала очистить текущий буфер вывода до дна, а только потом сменит режим. То есть сначала все, что было на момент запроса в буфере, будет выведено, а только потом сменится режим терминала
|
||||||
|
- `TCSAFLUSH` - то же, что и `TCSADRAIN`, только еще и сносит весь буффер ввода
|
||||||
|
|
||||||
|
```C
|
||||||
|
void set_input_mode()
|
||||||
|
{
|
||||||
|
struct termios tattr;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
// Убеждаемся, что STDIN - это терминал
|
||||||
|
if (!isatty (STDIN_FILENO))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Not a terminal.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сохраняем параметры текущего терминала
|
||||||
|
//для последующего восстановления
|
||||||
|
tcgetattr (STDIN_FILENO, &saved_attributes);
|
||||||
|
atexit (reset_input_mode);
|
||||||
|
|
||||||
|
// Устанавливаем все режимы, которые
|
||||||
|
// нас в общем-то интересуют
|
||||||
|
tcgetattr (STDIN_FILENO, &tattr);
|
||||||
|
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
|
||||||
|
tattr.c_cc[VMIN] = 0;
|
||||||
|
tattr.c_cc[VTIME] = 0;
|
||||||
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Исправление ассемблерных вставок
|
||||||
|
|
||||||
|
Должен сказать, что я не большой поклонник "inline assembly". На мой субъективный взгляд намного лучше, читаемее и стабильнее добавлять ассемблер на этапе линковки. Это дает несколько приятных бонусов:
|
||||||
|
|
||||||
|
1. Код можно поддерживать на любимом ассемблере
|
||||||
|
2. Код ассемблера можно компилить отдельно
|
||||||
|
3. Код программы на C становится ощутимо чище (*лично на мой взгляд ассемблерные вставки плохо смотрятся в коде*), а также все макросы ассемблера не касаются кода на C
|
||||||
|
4. Меньше потенциальных ошибок из-за того, что вы что-то не так поняли и откомпилировалось все неправильно
|
||||||
|
|
||||||
|
Помимо прочего очень важный момент: я использую gcc для компиляции, а в отличие от clang, он довольно ленивый и наши строки для ассемблера в нетронутом виде отправятся прямо в текст программы, которая затем будет скормлена ассемблеру. Отсюда следует несколько нюансов:
|
||||||
|
|
||||||
|
- Стандартный ассемблер, используемый `gcc` - `as` и по умолчанию он использует синтаксис AT&T. Однако я не очень люблю этот синтаксис, предпочитаю работать с синтаксисом intel. Выхода тут 2:
|
||||||
|
- Дать компилятору флаг -masm=intel, после чего уже собственный ассемблер переключится на intel синтаксис
|
||||||
|
- В начале каждой ассемблерной вставки ставить ".intel_syntax noprefix", а после вставки но перед параметрами ставить ".att_syntax prefix". Это может периодически плохо работать
|
||||||
|
- При написании ассемблера необходимо соблюдать все переносы строк и при этом указывать это явно (поэтому в конце строк у меня и появляются `\n\t` - это поддержание табуляции и переноса строки
|
||||||
|
- Компилятору надо понимать, что будет происходить с переменными и регистрами во время ассемблерной вставки, поэтому и это тоже придется указать отдельно
|
||||||
|
|
||||||
|
Собственно видно, что есть ньансы, которые и заставляют меня сделать выбор в пользу обычного ассемблера и линковки, но раз лаба хочет, чтобы использовался именно inline, то будем использовать inline
|
||||||
|
|||||||
114
01-asm-basics/main.c
Normal file
114
01-asm-basics/main.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/io.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "substitutions.h"
|
||||||
|
|
||||||
|
#define PortCan0 0x40
|
||||||
|
|
||||||
|
void beep(unsigned iTone, unsigned iDlit);
|
||||||
|
|
||||||
|
void delay(unsigned int ms)
|
||||||
|
{
|
||||||
|
usleep(ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
|
||||||
|
long int lCnt = 0;
|
||||||
|
int iA = 0x1234;
|
||||||
|
|
||||||
|
char *pT = (char *)0x46C;
|
||||||
|
printf("\nПечатаем 10 раз значение байта с известным адресом\n");
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
printf(" \n %d ", *pT);
|
||||||
|
}
|
||||||
|
printf("\n Для продолжения нажмите любую клавишу \n");
|
||||||
|
system("pause"); // Ждем нажатия клавиши
|
||||||
|
|
||||||
|
printf("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n");
|
||||||
|
printf("\n Для выхода из цикла - нажмите любую клавишу \n");
|
||||||
|
|
||||||
|
// Линуксу не сильно нравится, что ты насилуешь порты ввода и вывода процессора, поэтому нужно выдать ему на это дело разрешение
|
||||||
|
|
||||||
|
ioperm(PortCan0, 1, 3); // Что означает тройка напишу позже
|
||||||
|
set_input_mode();
|
||||||
|
while (isKeyPressed() == 0) {
|
||||||
|
printf("\n Порт40 = %d", inb(PortCan0));
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
reset_input_mode();
|
||||||
|
|
||||||
|
system("pause");
|
||||||
|
printf("\n Читаем содержимое порта с адресом 40 ассемблером \n");
|
||||||
|
|
||||||
|
set_input_mode();
|
||||||
|
while (isKeyPressed() == 0) {
|
||||||
|
asm {
|
||||||
|
push ax
|
||||||
|
in al,0x40
|
||||||
|
}
|
||||||
|
unsigned char Tmm = _AL;
|
||||||
|
asm pop ax
|
||||||
|
delay(500);
|
||||||
|
printf("\n Порт40 = %d", Tmm);
|
||||||
|
}
|
||||||
|
reset_input_mode();
|
||||||
|
system("pause");
|
||||||
|
printf("\n Для продолжения - нажмите любую клавишу \n");
|
||||||
|
system("pause");
|
||||||
|
|
||||||
|
long *pTime = (long *)0x46C;
|
||||||
|
set_input_mode();
|
||||||
|
while (isKeyPressed() == 0) {
|
||||||
|
printf("\n %ld", *pTime);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
reset_input_mode();
|
||||||
|
system("pause");
|
||||||
|
|
||||||
|
int Time;
|
||||||
|
set_input_mode();
|
||||||
|
while (isKeyPressed() == 0) {
|
||||||
|
asm push ds
|
||||||
|
asm push si
|
||||||
|
asm mov ax, 40h
|
||||||
|
asm mov ds, ax
|
||||||
|
asm mov si, 0x6C
|
||||||
|
asm mov ax, [ds : si]
|
||||||
|
asm mov Time, ax
|
||||||
|
asm pop si
|
||||||
|
asm pop ds
|
||||||
|
|
||||||
|
printf("\n %d", Time);
|
||||||
|
delay(300);
|
||||||
|
}
|
||||||
|
reset_input_mode();
|
||||||
|
|
||||||
|
beep(400, 200);
|
||||||
|
for (lCnt = 0; lCnt < 1000000; lCnt++) {
|
||||||
|
a1:
|
||||||
|
asm {
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
mov ax,iA
|
||||||
|
a2:
|
||||||
|
mov ax,iA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
beep(400, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
//void beep(unsigned iTone, unsigned iDlit) {
|
||||||
|
// sound(iTone);
|
||||||
|
// delay(iDlit);
|
||||||
|
// nosound();
|
||||||
|
//}
|
||||||
61
01-asm-basics/substitutions.c
Normal file
61
01-asm-basics/substitutions.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "substitutions.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
/* Use this variable to remember original terminal attributes. */
|
||||||
|
|
||||||
|
struct termios saved_attributes;
|
||||||
|
|
||||||
|
void reset_input_mode()
|
||||||
|
{
|
||||||
|
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_input_mode()
|
||||||
|
{
|
||||||
|
struct termios tattr;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* Make sure stdin is a terminal. */
|
||||||
|
if (!isatty (STDIN_FILENO))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Not a terminal.\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save the terminal attributes so we can restore them later. */
|
||||||
|
tcgetattr (STDIN_FILENO, &saved_attributes);
|
||||||
|
atexit (reset_input_mode);
|
||||||
|
|
||||||
|
/* Set the funny terminal modes. */
|
||||||
|
tcgetattr (STDIN_FILENO, &tattr);
|
||||||
|
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
|
||||||
|
tattr.c_cc[VMIN] = 1;
|
||||||
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delay(unsigned int ms)
|
||||||
|
{
|
||||||
|
usleep(ms * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
char isKeyPressed()
|
||||||
|
{
|
||||||
|
char key_handler = 0;
|
||||||
|
read(STDIN_FILENO, &key_handler, 1);
|
||||||
|
if (key_handler > 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//int main()
|
||||||
|
//{
|
||||||
|
// set_input_mode();
|
||||||
|
// while (isKeyPressed() == 0) {}
|
||||||
|
// printf("ok");
|
||||||
|
// reset_input_mode();
|
||||||
|
//}
|
||||||
9
01-asm-basics/substitutions.h
Normal file
9
01-asm-basics/substitutions.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef SUBSTITUTIONS_H
|
||||||
|
#define SUBSTITUTIONS_H
|
||||||
|
|
||||||
|
void reset_input_mode();
|
||||||
|
void set_input_mode();
|
||||||
|
void delay(unsigned int ms);
|
||||||
|
char isKeyPressed();
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -2,171 +2,3 @@
|
|||||||
|
|
||||||
## Система команд процессора, ее связь с кодами команд
|
## Система команд процессора, ее связь с кодами команд
|
||||||
|
|
||||||
## Кодирование команд для x86-64 архитектуры
|
|
||||||
|
|
||||||
Преподаватель на ресурсном курсе оставил огромный талмуд Intel (далее именуемый "талмудик" и "талмуд") на тему того, как кодируются команды у их процессоров. И пусть даже наш дорогой препод на лекции дал пояснения по конверсии и прочему, он оставил без ответа вопросы следующего толка: когда какие байты задействованы, где посмотреть опкоды команд и прочие мелочи жизни. Я тот еще программист, поэтому на меня тут не надейтесь, но помогу чем смогу
|
|
||||||
|
|
||||||
Перво-наперво структура команды. Приведена она и у препода и в талмудике Intel, повторяться не хочу, но картинку оставлю
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
На этой же картинке видно, что может быть от разное количество байт на КОП (который я по привычке именую опкодом), на Displacement, на Immediate и прочем. Да и еще проскакивают надписи `(if required)` и `(optional)`. Вопрос назревает сам собой - а где смотреть-то. И ответ у меня к сожалению не утешительный - в том самом великом и ужасном талмудике (по крайней мере я не нашел другого способа понадежнее). Но тут есть одна так сказать проблемка... Таблички по командам от Intel выглядят мягко скажем как-то так...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Хтонь лично на мой взгляд довольно неприятная, но на самом деле она не так страшна, как вы подумали... Она значительно хуже...
|
|
||||||
|
|
||||||
В общем я тут приведу свои пояснения ко всему, что указывают сами Intel, но не в сухую по руководству, а на основе собственного опыта ручного ассемблирования (который, к слову, не очень богат, потому что я еще не успел настолько сойти с ума, чтобы делать работу ассемблера за него)
|
|
||||||
|
|
||||||
### Колонка opcode
|
|
||||||
|
|
||||||
- `REX.W` - По идее этот префикс может означать много вещей, но на практике пока что я сталкивался с ним только в таком разрезе: если он есть в начале, значит в REX-байте нужно поставить единичку в 3 разряде (4-я цифра справа). Также это означает, что данный байт, вообще говоря, обязателен для функционирования этой команды
|
|
||||||
|
|
||||||
- `REX` - Такое встречается, на моей памяти, только рядом с восьмибитными инструкциями и всегда только для того, чтобы к ним тут же приложилось пояснение от intel, что какие-то там проблемы. В общем читайте все это в сносках, потому что сам по себе флаг означает простое наличие REX-байта перед опкодом по всей видимости
|
|
||||||
- `/digit` - можно порой встретить что-то типа `/0` или `/7`. Когда такое видите, это значит, что в ModR/M байте вместо поля reg нужно записать в двоичной системе то число, которое после слеша. То есть от `000` и до `111`. А все остальное адресуете как раньше
|
|
||||||
- `/r` - указывает на то, что в ModR/M байте все поля Mod, Reg и R/M используются в стандартном варианте
|
|
||||||
- `cb`, `cw`, `cd`, `cp`, `co`, `ct` - сам плохо понимаю, что это за покемоны такие. В 64-битных опкодах встречаются редко. Согласно мануалу показывают, сколько битов после опкода следует зарезервировать под смещение для сегмента кода (если вы откроете методичку Милицина, то это тот самый CS или Code Segment). Также согласно талмудику иногда оно может изменить значение сегментному регистру кода. Возможны варианты 1 байт, 2 байта, 4 байта, 6 байт, 8 байт, 10 байт соотвественно.
|
|
||||||
- `ib`, `iw`, `id`, `io` - Показывают, что после опкода, ModR/M байта (если есть) должен идти непосредственный операнд длиной 1, 2, 4, 8 байт соответственно. Встречается он в таблице обычно там же, где в колонке instruction в соответствующем месте производятся какие-то действия с непосредственными операндами. При чем надо понимать, что нельзя просто опустить байты, которые заполнены нулями, даже если очень хочется и мама разрешила. ставим столько, сколько требует спецификация
|
|
||||||
- `+rb`, `+rw`, `+rd`, `+ro` - встречается тогда, когда создатели процессора почему-то решили засунуть регистр прям в опкод операнда. Ну, не нам их за это судить. Фактически нам нужно просто глянуть в таблицу которая приведена самими Intel, чтобы определиться только с тем, какое число от 0 до 7 прибавлять. В целом это число является номером регистра, а идут они всегда в следующем порядке: rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, а также дополнительные регистры r8-r15 работают в том же режиме, то есть начинают нумероваться с нуля. Единственное отличие - бит в REX байте нужно поставить. А вообще табличка должен сказать весьма любопытная, поэтому с ней придется ознакомиться самому. Находится она на 45 странице руководства.
|
|
||||||
- `+i` - используется в операциях с плавающей точкой. Такие операции любят использовать стек сопроцессора (потому что на самом деле вся арифметика с плавающей точкой аппаратно ускоряется и у нее тоже есть собственная память). Так вот, такой стек обозначается ST(i). Где ST(0) - вершина стека. Не берусь утверждать, но по всей видимости в стеке всего 8 ячеек, потому что по мануалу i может принимать значения от 0 до 7. Соотвественно наша задача просто прибавить это число к байту слева от плюса и на этом все. Больше ничего не требуется
|
|
||||||
|
|
||||||
### Колонка instruction
|
|
||||||
|
|
||||||
<!---Тут на самом деле говна еще очень много, но я постараюсь описать как можно проще-->
|
|
||||||
|
|
||||||
*Обозначений в этой колонке кратно больше, но я буду стараться приводить их кратко. поскольку тут есть регистры на любой вкус и цвет, я буду ставить символ # когда на месте # может быть число от 16 до 64*
|
|
||||||
|
|
||||||
- `rel8` - адресация относитльно rip насколько я понимаю. Воспринимается как знаковое число длиной 8 бит, которое прибавится к содержимому rip
|
|
||||||
- `rel#` - адресация внутри одного сегмента кода (когда в ассемблере начинается `section .text` - это оно). Встречается обычно когда надо обратиться к меткам при прыжках туда и сюда, а оттого встретить можно в `j*` и `call`. Разбиратьсят точнее у меня к сожалению нет возможности
|
|
||||||
- `r#` - бозначает регистр размером # битов (от 8-битных до 64-битных)
|
|
||||||
- `m#` - обозначает ячейку памяти размером # бит. периодически можено даже встретить # равное 128, что используется только в SSE и SSE2 инструкциях
|
|
||||||
- `r/m#` - показывает, что операнд или память (например `[r11 + rcx + 2]`), или регистр (например `rcx`). Если относиться как к памяти, то число `#` указывает не на то, как память адресуется, а сколько битов будет из памяти прочитано, А если как к регистру, то нужно выбирать регистр по размеру, иначе процессор будет жаловаться
|
|
||||||
- `imm#` - тут `#` может принимать помимо стандартных еще и значение 8. обозначает, что работа ведется с непосредственным операндом (как например в инструкции `mov rax, 12`)
|
|
||||||
- `moffs#` - сокращение от memory offset - весьма любопытно. Показывает, что программа не использует ModR/M байт. Встречается только в некоторых вариациях `mov`, а адрес задается в качестве смещения относительно базового сегмента (не уверен, что это означает, но думаю относительно сегмента, где сейчас находится rip). Сами интел например на `mov al, moffs8` пишет следующее описание *"Move byte at (seg:offset) to A"*. Также надо понимать, что # задает лишь сколько байтов будет прочитано, а offset задается вполне себе 4 байтами после опкода
|
|
||||||
- `Sreg` - указывает на использование сегментного регистра. Битовые присвоения для сегментных регистров таковы: e ES = 0, CS = 1, SS = 2, DS = 3, FS = 4, и GS = 5
|
|
||||||
|
|
||||||
Остальные инструкции здесь не привожу, поскольку они затрагивают работу с числами с плавающей точкой, векторами и прочими радостями, до которых надеюсь не дойдет
|
|
||||||
|
|
||||||
## Как собрать ~~своего покемона~~ свою команду из ассемблера
|
|
||||||
|
|
||||||
*оно же: "Да как этой б\*\*\*ской таблицей пользоаться вообще*
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Повторно привожу это изображение. Оно показывает общую структуру команды для процессора. Далее в инструкции я буду обращаться к разным участкам этой команды применяя соответствующие обозначения. Для примера команды можете смотреть на табличку для add в качестве примера того, что можно там увидеть
|
|
||||||
|
|
||||||
Самое важное чиселко, которое тут есть - 16-ричное породы "понятно-написанное". Оно - фундамент всего опкода, его мы и берем. А дальше алгоритм следующий:
|
|
||||||
|
|
||||||
1. Смотрим, колонку instruction. В ней ищем глазами базу и венец - понятно написанный опкод (это будет скорее всего от двух и до шести 16-ричных цифр). Дальше смотрим, надо ли к нему непосредственно что-то прибавать, и если надо - прибавляем. Ура - мы получили opcode.
|
|
||||||
2. Как только мы получили опкод, начинаем смотреть налево - если есть приписка REX.W, значит пишем REX байт. Пока что просто ставим заглушку: 0b01001000 или 0x48. Он нам потребуется если мы захотим адресоваться к регистрам с r8 по r15, а до тех пор он будет в заглушечном состоянии. На будущее также отмечу, что выглядит этот байт в общем случае примерно так - 0100 WRXB, где 0100 - обязательная часть, а все к остальным битам я буду адресоваться через точку
|
|
||||||
3. Далее присматриваемся, надо ли что-то прорезервировать (те самые `cb`) и если непосредственно после опкода ничего не требуется начинаем писать ModR/M байт
|
|
||||||
4. Написание ModR/M байта пожалуй самый запутанный процесс, но с ним нам должна помочь табличка от Intel. Находятся они в районе 32-34 страниц. Но расскажу так. Поля у ModR/M следующие - 2 битовый mod, потом 3 битный reg, потом 3 битный r/m.
|
|
||||||
1. mod - указывает на то, как будет адресоваться r/m (третье поле). r/m - сокращение от register/memory. То есть как можно из названия догадаться, только в этом поле процессор может адресовать память компьютера. Это же и есть фундаментальная причина, по которой нельзя заассемблировать команду вроде `mov [addr1], [addr2]`. Возможное содержимое этого поля такого: `00` - Будет адресоваться оперативная память, при чем использоваться будут только регистры (`mov rax, [rdi + rsi*4]`). `01` - будет адресоваться оперативная память и помимо регистра будет еще и смещение, но длиной не больше 1 байта (`add [rbp + rcx - 2], rax`). `10` - то же, что и `01`, но смещение уже занимает 4 байта. `11` - адресоваться будут 2 регистра (`xor rax, rbx`). Если мы поставили что угодно кроме `11` - это надо запомнить, потому что число которое мы записали нужно будет включить в двоичный вид команды использовав взятое нами число байт, а разместить их надо будет либо после ModR/M, либо после SIB байта, если последний будет.
|
|
||||||
2. reg - указывает регистр, если в колонке opcode не стоит что-то из разряда `/4`. Если стоит, то в reg записывается это число в двоичной форме. Все неиспользованные разряды заполняются нулями. Если длина регистра не вмещается, то самую старшую единицу можно поставить в REX.R
|
|
||||||
3. R/M - указывает регистр или участок в памяти, который будет адрессоваться. Можем писать сюда номер любого регистра (если не влезет, то расширять его при помощи X бита в REX байте). Однако особенностью тут обладает значение `100`, потому что оно показывает процессору, что нужен SIB байт. При любом другом значении адрес (если mod != 0b11) будет взят из регистра и если есть смещение, оно будет добавлено к значению этого адреса. Единственное, что если mod взят 0b00, то значение 101 тоже магическое - оно затребует 32-битное смещение и использует его в качестве адреса (это точно сработает в 32 битном режиме, но не уверен, что сработает в 64 битах)
|
|
||||||
5. Если вы взяли такую комбинацию, что вам понадобился SIB байт (а это mod != 11, r/m == 100), то разберемся со структурой байта. Весь этот байт завязан исключительно на адресацию в памяти и состоит из трех побей Scale (SS) в 2 бита, index в 3 бита, base и 3 бита. для разбора полей возьмем следующий пример `[rbx + rcx * 4 + 3]`
|
|
||||||
1. SS - это scale - это то, на что будет умножаться регистр индекса, при чем это двухбитовое число можно считать степенью двойки. То есть получается 2^(SS) - это коэфициент на который мы умножаем и можем получается умножить на 1, 2, 4 и 8.
|
|
||||||
2. index - это регистр, который будет умножаться на 2^(SS). Если вы хотите вписать регистры с r8-r15, то невлезающую единицу можно записать в REX.X. В целом же связка SS и index и обеспечивает наличие в адресе в нашем примере `rcx * 4`
|
|
||||||
3. base - указывает на регистр, значение которого в лоб прибавится к адресу, то есть в нашем примере он отвечает за `rbx`. Если базовый регистр не нужен, на его место ставится `101`
|
|
||||||
|
|
||||||
6. После SIB идет displacement байты (1 или 4 в зависимости от поля mod в ModR/M). Заполняем их согласно выделенному количеству
|
|
||||||
7. После dispacement идет immediate байты - они могут встретиться если в табличке в колонке opcode на этой строке есть что-то похожее на `ib` или другие, которые мы упоминали. заполняем сколько надо
|
|
||||||
|
|
||||||
NOTE: По какой-то причине это не указано в руководстве Intel, но по крайней мере если верить сайту, который я использовал для ассемблирования инструкций, то нужно обязательно учитыать префиксы к опкоду прежде чем начинать кодировать (опять же все по опыту):
|
|
||||||
|
|
||||||
1. `0x66` - ставится, если программа иссользует хотя бы 1 16-битный регистр
|
|
||||||
2. `0x67` - ставится если команда **адресуется** при помощи 32-битных регистров
|
|
||||||
|
|
||||||
Благо для 8-битных операций другие опкоды и хотя бы на них не надо префиксы запоминать)
|
|
||||||
|
|
||||||
## Примеры переводов
|
|
||||||
|
|
||||||
Голая теория никого никогда не радовала, поэтому постараюсь в меру своих сил переконвертировать несколько примеров
|
|
||||||
|
|
||||||
**`mov rax, rbx`** - исходная инструкция
|
|
||||||
|
|
||||||
Для начала в талмудике нам потребуется найти опкод операции. Поэтому ищем табличку для этой операции. В ней нас интересует один из двух покодов: `mov r/m64, r64` и `mov r64, r/m64`. Я возьму второй из этих вариантов, просто чтобы порядок следования регистров совпадал с нашей исходной иструкцией (потом поймете). Для выбранной инструкции приведен следующий opcode: `REX.W + 8B/r`
|
|
||||||
|
|
||||||
Для начала посередине ставим опкод (8b в двоичной):
|
|
||||||
|
|
||||||
[ 1000 1011 ]
|
|
||||||
|
|
||||||
REX байт у нас появляется в любом случае. REX.W - это флаг для REX, который переназначает операнды в размеры 64 бита. Если бы оба наших регистра были бы 32 битные, то этого флага бы не было. Ну а раз REX у нас обязателен, то можно немного определиться, будут ли у нас еще какие-то флаги в этом байте. Еще какие-то флаги могут появиться если вы используете регистры r8-r15, в остальных случаях REX будет выглядеть как [ 0100 1000 ]. У нас эти регистры не используются, поэтому оставим его в таком виде
|
|
||||||
|
|
||||||
Промежуточный результат: [ 0100 1000 ] [ 1000 1011 ]
|
|
||||||
|
|
||||||
Далее ModR/M. Первые 2 бита от него определяют будут ли использовать адреса и смещения. Подробнее смотрите выше. В нашем случае мы пересылаем из регистра в регистр, поэтому нам нужен mod = 11. Далее идет поле рег. Конкретно в случае `mov r64, r/m64` по контексту можно догадаться что reg у нас слева. Тремя битами кодируется номер регистра. У rax номер 000, а у rbx номер 011. Как я уже и сказал, в нашем случае регистр приемника слева, поэтому ModR/M: [ 11 000 011 ]
|
|
||||||
|
|
||||||
Итого: [ 0100 1000 ] [ 1000 1011 ] [ 11 000 011 ]
|
|
||||||
|
|
||||||
*примечание: ответ мог бы получиться немного другой, если бы мы взяли первый попавшийся опкод, но можете использовать его в качестве упражнения (ну а что, составители учебника так могут, а я нет?)*
|
|
||||||
|
|
||||||
**`mov r8, r11`**
|
|
||||||
|
|
||||||
Все действия и рассуждения у нас аналогичны примеру выше. Я хотел лишь пару слов сказать о том, как кодировать регистры r8-r15. Как раз для этих целей нам поможет REX байт. Как я уже писал, REX имеет следующую структуру: 0100 WRXB. Бит R используется для расширения поля reg, а бит B используется для расширения поля r/m. Когда идет обращение к регистру из диапазона r8-r15 в соответствующих дополняющих байтах должна стоять единица. То есть r8 в поле reg будет записываться как REX.R == 1 + 000, а r11 в поле r/m как REX.B == 1 + 011. Так что для данной команды повторится все, кроме REX байта, который в свою очередь примет вид [ 0100 1101 ]
|
|
||||||
|
|
||||||
Итого: [ 0100 1101 ] [ 1000 1011 ] [ 11 000 011 ]
|
|
||||||
|
|
||||||
**`lea rbx, [rbp + r12 * 4 + 33]`**
|
|
||||||
|
|
||||||
Для начала повторяем наш процесс поиска в талмудике опкода. В этот раз у нас разночтений нет, у нас есть только вариант с инструкцией `lea r64, m`. Для нее поле opcode выглядит довольно знакомо: `REX.W 8D /r`. REX байт у нас уже задействован и 100% появится, однако я вижу тут регистр r12, что должно сказать мне, что REX байт не ограничится простым [ 0100 1000 ]. Заполним мы его чуть позже, а пока только опкод. [ 1000 1101 ]
|
|
||||||
|
|
||||||
Далее смотрим ModR/M. в нашем случае мы явно будем использовать адрес из оперативной памяти и число 33 для сдвига, поэтому нам нужен режим mod = 01 (можно и 10, но тогда будет куча бесполезных нулей). поле reg не вызывает вопросов - используется регистр rbx, поэтому в REX.R = 0, а reg = 011, а вот с полем r/m все любопытнее. Число, которое идет с плюсом можно пока мысленно вынести за скобки (но не убирать далеко, оно нам еще пригодится), а вот регистры мы уже не можем проморгать. Как мы помним поле r/m хранит только 3 бита, как же адресовать эту адову хренотень? Оказывается в r/m последовательность 100 зарезервирована под добавление еще одного байта - SIB байта. ModRM = [ 01 011 100 ]
|
|
||||||
|
|
||||||
SIB байт состоит из 2 битов SS, которые представляют собой степень двойки, 2^(ss) я буду называть коэфициентом. В нашем случае хочу умножить на 4, поэтому ss = 10. Далее идет 3 бита на индекс - это регистр, который будет умножен на коэфициент. В нашем случае мы хотим умножить r12 на 4. Index регистр расширяется из REX.X (потому что inde**X**). Поэтому нам нужно записать двоичной системе 12 - 1100. Старший бит отправится в REX.X, а остальное в поле index, то есть REX.X == 1 index == 100. Далее идет 3 бита под регистр базы. Он расширяется из REX.B, в нашем случае хочется использовать регистр rbp, он имеет номер 0101, поэтому используем REX.B == 0, base == 101. SIB = [ 10 100 101 ]
|
|
||||||
|
|
||||||
Уже на этом этапе мы видим, что нужно проставить еще одну единицу в REX.X, в остальных же местах используются обычные регистры, поэтому REX = [ 0100 1010 ].
|
|
||||||
|
|
||||||
Помните, мы запоминали число 33? Ну вот настало его время. Дело в том, что число, которое надо прибавить к итоговому адресу. В нашем случае надо закодировать число 33, это будет 0b00100001, это мы засунем в displecement байт
|
|
||||||
|
|
||||||
Итого: [ 0100 1010 ] [ 1000 1101 ] [ 01 011 100 ] [ 10 100 101 ] [ 0010 0001 ]
|
|
||||||
|
|
||||||
**`inc WORD PTR [2 * rsi + 31]`**
|
|
||||||
|
|
||||||
*Интересный факт, в качестве index не может использоваться rsp*
|
|
||||||
|
|
||||||
Не буду много повторяться. Внутри опокад стоит `WORD PTR`, что значит, что я самолично попросил ассемблер относиться к содержимому скобок, как к указателю на 1 машинное слово. Возможно также отнестись как к указателю на байт `BYTE`, двойное слово `DWORD` и четверное слово `QWORD`. Instruction `inc r/m16`. Opcode `FF /0`. `/0` означает, что в ModR/M в поле reg нужно записать 3 нуля. Остальное адрессуется как обычно, поэтому самое время обсудить вот какую вещь. Если нам необходимо опустить базу, то в SIB байте мы поставим в поле base 101. Однако для этого в mod нужно поставить 00 и автоматически придется записать 4 байта смещения.
|
|
||||||
|
|
||||||
[ 0110 0110 ] [ 1111 1111 ] [ 00 000 100 ] [ 01 110 101 ] [ 0001 1111 ] [ 0000 0000 ] [ 0000 0000 ] [ 0000 0000 ]
|
|
||||||
|
|
||||||
Замечу так же, что поскольку не используется ни REX.W ни один из расширенных регистров, REX байт принимал значение 0100 0000, но в таком случае спецификация Intel позволяет этот байт опускать. А вот что опускать нельзя - это префикс переназначения операнда, потому что используется 16 битный регистр.
|
|
||||||
|
|
||||||
Число 31 у нас записано справа от SIB байта и у многих наверное появился вопрос, а почему оно выглядит имено так? А точнее - почему 3 последних байта заполнены нулями. Отвечаю - бог его знает и на самом деле, это зависит от процессора, и, возможно, от того, использует ли от little endian или big endian. Little endian - это когда число в памяти записывается как мы привыкли, то есть чем ливее циферка, тем она значительнее. Так что 0x1c1b так в память и запишутся - [ 0001 1100 ] [ 0001 1011 ]. Но вот в случае big endian запись идет по байтам, и если внутри байта все стандартно, то вот сами байты идут уже от младшего к старшему, поэтому то же число запишется уже в обратном порядке как [ 0001 1011 ] [ 0001 1100 ]. Думаю тут такая же фигня
|
|
||||||
|
|
||||||
Однако тут есть еще один важный момент Мы видим, что в данном случае есть один интересный момент. Вроде mod == 00, но тут 4 байта dispasement. Дело в том, что если mod == 00 а base == 101, то будет адресация вида index * scale + disp32. Довольно весело. Это я к чему? даже если вы знаете все номера регистры, таблицу смотреть все равно надо
|
|
||||||
|
|
||||||
**Послесловие**
|
|
||||||
|
|
||||||
Это далеко не исчерпывающий набор примеров, но этого хватит для начала.
|
|
||||||
|
|
||||||
## Решение остальных пунктов
|
|
||||||
|
|
||||||
*В целом в этом репозитории лежат файлы, в которых я приложил пока еще не протестированное, но решение для первых нескольких пунктов. Однако в силу того, что память у нас 64-битная, а также я не могу залеть напрямую в видеопамять если не буду собирать модуль ядра. Возможно конечно от нечего делать я сделаю модуль ядра, который позволит выворачивать такие приколы, но это будет точно не на время этого курса.*
|
|
||||||
|
|
||||||
### 01 - простучать команды ассемблеру
|
|
||||||
|
|
||||||
Тут нечего сказать - есть просто колонка с коммандами и просят их использовать. Тут гугл в помощь.
|
|
||||||
|
|
||||||
А вот по поводу полей в команде могу сразу сказать - в 64 битном процессоре все это будет выглядеть немного иначе. Поэтому предлагаю 16-битные регистры заменить в команде на 64 битные и закодировать как для 64 разрядной системы. подробная инструкция как это бы надо бы сделать у меня приведена выше, поэтому тут не буду на этом останаваливаться.
|
|
||||||
|
|
||||||
### 02 - Пересылка массива при помощи loop и lea
|
|
||||||
|
|
||||||
`lea` - это сокращение от "load effective adress". Она использует использует классическую операцию обращения к памяти, но саму память не затрагивает, а просто записывает высчитанный адрес в переменную. `loop` в свою очередь прыгает на определенную метку пока в rcx не окажется 0 и при каждом прыжке уменьшает значение в rcx на 1.
|
|
||||||
|
|
||||||
### 03 - Пересылка данных через LODS, MOVS, STOS
|
|
||||||
|
|
||||||
LODS и STOS - парные команды. Первая читает из памяти в rax (или его часть), STOS наоборот - пишет в память содержимое rax (или его часть). `movs` перемещает содержимое из [rsi] в [rdi], после чего увеличивает адрес на размер элемента. Это очень хорошо сочетается с префиксом rep, который заставляет повторяет команду пока в rcx не будет 0, а после каждого повторения уменьшает rcx на 1
|
|
||||||
|
|
||||||
### 05 - Запись в произвольную память
|
|
||||||
|
|
||||||
В линуксе вся память виртуальная, а если попытаться в лоб попробовать написать что-то в рандомный адрес, ядро выдаст segfault. Чтобы этого не произошло необходимо промапать память. Для этого используется системный вызов mmap, про его особенности написано внутри файла. Здесь хотелось бы пояснить вот какой момент: этот системный вызов использует кучу флагов, которые изначально не особенно нам известны. Так вот. Самый быстрый способ найти их - обратиться к include вашего компилятора. Для mmap все лежит в файле <sys/mmap.h>. Эти значения я решил занести в define, чтобы код был чуть читаемее
|
|
||||||
|
|
||||||
У mmap есть и другая особенность - он мапает виртуальную память, а не физическую, поэтому то, что в оригинальной методичке мы на самом деле использовали видеобуфер, для нас не имеет реального значения. Я также использовал анонимный приватный маппинг, чтобы не портить жизнь другим процессам и не грузить ничего из файла, поэтому даже попортить жизнь другим процессам у меня не получится
|
|
||||||
|
|
||||||
<!--- Пока что я думаю эта инфа лишняя, может потом верну и раскомментирую
|
|
||||||
### Чутка про префикс REX
|
|
||||||
|
|
||||||
Судя по всему, префикс REX стал почти обязателен при переходе на 64 разряда. Что ж, это не удивительно, так как в 64-разрядных системах прибавилось регистров, а их номера нужно где-то и как-то хранить, поэтому это вот такой вот "костылик". На самом деле в талмудике преведены схемы всех подключений, которые я пока не привожу, потому что это не самое главное, но может потом добавлю
|
|
||||||
|
|
||||||
Вот что они пишут про префик REX во второй главе своего талмуда: "Префикс REX указывается не всегда в 64-разрядном режиме. Он необходим только тогда, когда инструкция адресуется к одному из рассширенных регистров или использует 64-разрядные операнды". То есть условно говоря если работам с 64 разрядами, то RAX нужен, а если нет - его может и не быть, если явно не сказано иное. Сами же REX - это 16 опкодов, которые берут пространство от 0x40 до 0x4F. В режиме режиме обратной совместимости и IA-32 отражают опкоды реальных инструкций, но нас естественно интересует режим 64 разрядов, а в нем они как отдельная инструкция не трактуются и идут только в связке. Также почему-то интел сокрушаются, что из-за этого однобайтовый опкод для инкреммента и декремента перестал существовать в 64 разрядных системах
|
|
||||||
-->
|
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
def hex2bin(hexNum: str) -> None:
|
|
||||||
return bin(int(hexNum, 16))
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Convert hex number to bin right in terminal")
|
|
||||||
parser.add_argument('-s', '--stdin', action='store_true', help="if passed takes input from stdin")
|
|
||||||
parser.add_argument('number', nargs='?')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.stdin:
|
|
||||||
print(hex2bin(input()))
|
|
||||||
elif args.number:
|
|
||||||
print(hex2bin(args.number))
|
|
||||||
else:
|
|
||||||
print("something went wrong")
|
|
||||||
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
section .data
|
|
||||||
|
|
||||||
source: db 1, 2, 3, 4, 5, 6, 7, 8
|
|
||||||
s_size equ $-source
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
|
|
||||||
dest: resb 8
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
_start:
|
|
||||||
|
|
||||||
lea rsi, [source]
|
|
||||||
lea rdi, [dest]
|
|
||||||
mov rcx, s_size
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
|
|
||||||
mov al, [rsi + rcx]
|
|
||||||
mov [rdi + rcx], al
|
|
||||||
|
|
||||||
loop .loop
|
|
||||||
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 0
|
|
||||||
syscall
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
section .data
|
|
||||||
source: db 1, 2, 3, 4, 5, 6, 7, 8
|
|
||||||
s_size equ $-source
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
|
|
||||||
dest: resb 8
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
_start:
|
|
||||||
|
|
||||||
mov rsi, source
|
|
||||||
mov rdi, dest
|
|
||||||
mov rcx, s_size
|
|
||||||
|
|
||||||
rep movsb
|
|
||||||
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 0
|
|
||||||
syscall
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
%define SRC 0xB8000
|
|
||||||
%define DST 0xB9000
|
|
||||||
%define ARR_SIZE 10
|
|
||||||
|
|
||||||
%define PROT_READ 0x1
|
|
||||||
%define PROT_WRITE 0x2
|
|
||||||
%define MAP_PRIVATE 0x02
|
|
||||||
%define MAP_ANONYMOUS 0x20
|
|
||||||
|
|
||||||
_start:
|
|
||||||
|
|
||||||
; Из-за особенностей ядра линукса нужно сначала промапать произвольную память
|
|
||||||
mov rax, 0x9 ; mmap
|
|
||||||
mov rdi, SRC ; где
|
|
||||||
mov rsi, ARR_SIZE ; сколько
|
|
||||||
mov rdx, PROT_READ ; флаги чтения
|
|
||||||
or rdx, PROT_WRITE ; флаги записи
|
|
||||||
mov r10, MAP_PRIVATE ; приватная память
|
|
||||||
or r10, MAP_ANONYMOUS ; не связана с файлом
|
|
||||||
mov r9, 0 ; офсет должен быть 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
|
|
||||||
mov rsi, rax ; ставлю так, так как ядро линукса выделяет ближайшую область памяти, а не точно заказанную - проклятое выравнивание
|
|
||||||
|
|
||||||
; заполню чем-нибудь массив
|
|
||||||
mov rcx, ARR_SIZE
|
|
||||||
mov rbx, 0
|
|
||||||
|
|
||||||
.fill_src_loop:
|
|
||||||
|
|
||||||
mov [rsi + rbx], bl
|
|
||||||
inc rbx
|
|
||||||
|
|
||||||
loop .fill_src_loop
|
|
||||||
|
|
||||||
push rsi
|
|
||||||
|
|
||||||
mov rax, 0x9 ; mmap
|
|
||||||
mov rdi, DST ; где
|
|
||||||
mov rsi, ARR_SIZE ; сколько
|
|
||||||
mov rdx, PROT_WRITE ; флаги чтения
|
|
||||||
; or rdx, PROT_WRITE ; флаги записи
|
|
||||||
mov r10, MAP_PRIVATE ; приватная память
|
|
||||||
or r10, MAP_ANONYMOUS ; не связана с файлом
|
|
||||||
mov r9, 0 ; офсет должен быть 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
mov rdi, rax
|
|
||||||
; заполню чем-нибудь массив
|
|
||||||
|
|
||||||
mov rcx, ARR_SIZE ; сколько байт копируем
|
|
||||||
|
|
||||||
pop rsi
|
|
||||||
rep movsb
|
|
||||||
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
ASM = nasm
|
|
||||||
ASM_FLAGS = -felf64 -g
|
|
||||||
LINK = ld
|
|
||||||
|
|
||||||
%: %.o
|
|
||||||
$(LINK) -o $@ $^
|
|
||||||
|
|
||||||
%.o: %.asm
|
|
||||||
$(ASM) $(ASM_FLAGS) $^ -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o
|
|
||||||
rm -f $(subst .asm, $(empty), $(wildcard *.asm))
|
|
||||||
@ -2,13 +2,3 @@
|
|||||||
|
|
||||||
## Ассемблер и функции BIOS
|
## Ассемблер и функции BIOS
|
||||||
|
|
||||||
В этой работе намного проще посмотреть непосредственно решения и почитать комментарии к коду, чем читать теоретическое приложение к работе. Если вам все же что-то не понятно - кидайте в issues
|
|
||||||
|
|
||||||
Впрочем зная, что основная масса народу не будет делать эту лабу так, как сделал ее я, сюда вряд ли кто-то заглянет)
|
|
||||||
|
|
||||||
### Касаемо Makefile
|
|
||||||
|
|
||||||
Для того чтобы не писать много команд для однотипной и монотонной сборки проекта, был написан простой Makefile. Однако работает он следующим образом: он принимает название цели сборки и ищет файл с именем цели и расширением .asm. Если не находит - не собирает цель.
|
|
||||||
|
|
||||||
Важно заметить, что он не умеет линковать другие файлы в ассемблер, потому что написан был не для этого. Он просто берет голый файл на NASM (обязательно) и выдает 64-битный ELF из этого единственного файла. Если вам необходимо что-то прилинковать к ассемблеру, то увы, придется собирать проект вручную или менять этот makefile
|
|
||||||
|
|
||||||
|
|||||||
@ -1,81 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
%define STDIN 0
|
|
||||||
%define STDOUT 1
|
|
||||||
%define STDERR 2
|
|
||||||
|
|
||||||
section .data
|
|
||||||
src db 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
|
|
||||||
src_size equ $-src
|
|
||||||
|
|
||||||
; резервируем 1 килобайт для буффера ввода и вывода
|
|
||||||
; также в отдельной переменной сохраняем размер этого буфера
|
|
||||||
print_buf: times 1024 db 0
|
|
||||||
buf_size equ $-print_buf
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
%macro DIGIT_TO_ASCII 1 ; макрос, принимающий один регистр
|
|
||||||
add %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
|
|
||||||
|
|
||||||
; Передачу аргументов будем делать при помощи ABI - стандартная практика для linux
|
|
||||||
; Аргументы передаются в следующем порядке: rdi, rsi, rdx, rcx, r8, r9. Все, что не влезло, пушится в стек
|
|
||||||
; У передачи через стек тоже есть особенности, но их мы пока касаться не будем
|
|
||||||
|
|
||||||
print_from_buf: ; word -> void
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
_start:
|
|
||||||
mov rcx, src_size
|
|
||||||
mov rsi, src
|
|
||||||
mov rdi, print_buf
|
|
||||||
|
|
||||||
xor rax, rax ; обнуляем регистр
|
|
||||||
.transfer: ; в цикле передаем данные, попутно конвертируя их в ascii
|
|
||||||
lodsb
|
|
||||||
DIGIT_TO_ASCII rax
|
|
||||||
stosb
|
|
||||||
loop .transfer
|
|
||||||
|
|
||||||
mov [rdi + 1], BYTE `\n` ; Чтобы система не ругалась на отсутствие переноса
|
|
||||||
|
|
||||||
mov rdi, src_size
|
|
||||||
call print_from_buf
|
|
||||||
|
|
||||||
exit:
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
@ -1,219 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 142 KiB |
@ -1,379 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
%define NAME_SIZE 64
|
|
||||||
%define BUFF_SIZE 1024
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
name resb NAME_SIZE
|
|
||||||
buffer resb BUFF_SIZE
|
|
||||||
|
|
||||||
section .data
|
|
||||||
buffer_tail dq buffer
|
|
||||||
iseof db 0
|
|
||||||
|
|
||||||
;strings
|
|
||||||
E_open_file db `Something went wrong while opening file\n`
|
|
||||||
|
|
||||||
section .text
|
|
||||||
read:
|
|
||||||
push rcx
|
|
||||||
push r11
|
|
||||||
mov rax, 0
|
|
||||||
syscall
|
|
||||||
pop r11
|
|
||||||
pop rcx
|
|
||||||
ret
|
|
||||||
|
|
||||||
write:
|
|
||||||
push rcx
|
|
||||||
push r11
|
|
||||||
mov rax, 1
|
|
||||||
syscall
|
|
||||||
pop r11
|
|
||||||
pop rcx
|
|
||||||
ret
|
|
||||||
|
|
||||||
namecmp:
|
|
||||||
.LFB6:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
mov -8[rbp], rdi
|
|
||||||
mov -16[rbp], rsi
|
|
||||||
jmp .L2
|
|
||||||
.L6:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx edx, BYTE [rax]
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
cmp dl, al
|
|
||||||
je .L3
|
|
||||||
mov eax, 0
|
|
||||||
jmp .L4
|
|
||||||
.L3:
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
add QWORD -16[rbp], 1
|
|
||||||
.L2:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
test al, al
|
|
||||||
je .L5
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
test al, al
|
|
||||||
jne .L6
|
|
||||||
.L5:
|
|
||||||
mov eax, 1
|
|
||||||
.L4:
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
openfile:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 32
|
|
||||||
mov -24[rbp], rdi
|
|
||||||
mov rax, -24[rbp]
|
|
||||||
mov esi, 0
|
|
||||||
mov rdi, rax
|
|
||||||
mov eax, 2
|
|
||||||
syscall
|
|
||||||
mov -4[rbp], eax
|
|
||||||
cmp DWORD -4[rbp], 0
|
|
||||||
jns .L8
|
|
||||||
mov rdi, 1
|
|
||||||
mov rsi, E_open_file
|
|
||||||
mov rdx, 40
|
|
||||||
call write
|
|
||||||
mov edi, 1
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 1
|
|
||||||
syscall
|
|
||||||
.L8:
|
|
||||||
mov eax, -4[rbp]
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
|
|
||||||
fill_buffer:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 32
|
|
||||||
mov -20[rbp], edi
|
|
||||||
mov rdx, [buffer_tail]
|
|
||||||
lea rax, [buffer]
|
|
||||||
sub rdx, rax
|
|
||||||
mov eax, 1024
|
|
||||||
sub rax, rdx
|
|
||||||
mov -8[rbp], rax
|
|
||||||
mov rdx, -8[rbp]
|
|
||||||
mov eax, -20[rbp]
|
|
||||||
lea rcx, [buffer]
|
|
||||||
mov rsi, rcx
|
|
||||||
mov edi, eax
|
|
||||||
call read
|
|
||||||
mov -16[rbp], rax
|
|
||||||
cmp QWORD -16[rbp], 1023
|
|
||||||
ja .L11
|
|
||||||
mov BYTE [iseof], 1
|
|
||||||
.L11:
|
|
||||||
lea rdx, [buffer]
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov [buffer_tail], rax
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
find_second_word:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
mov -8[rbp], rdi
|
|
||||||
jmp .L13
|
|
||||||
.L14:
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
.L13:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
cmp al, 32
|
|
||||||
jne .L14
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
find_fullname_end:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
lea rax, [buffer]
|
|
||||||
mov -8[rbp], rax
|
|
||||||
jmp .L17
|
|
||||||
.L20:
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
.L17:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
cmp al, 10
|
|
||||||
jne .L18
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
test al, al
|
|
||||||
jne .L19
|
|
||||||
.L18:
|
|
||||||
lea rax, [buffer+1024]
|
|
||||||
cmp -8[rbp], rax
|
|
||||||
jb .L20
|
|
||||||
.L19:
|
|
||||||
lea rax, [buffer+1024]
|
|
||||||
cmp -8[rbp], rax
|
|
||||||
jne .L21
|
|
||||||
mov eax, 0
|
|
||||||
jmp .L22
|
|
||||||
.L21:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
.L22:
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
pop_fullname:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
sub rsp, 32
|
|
||||||
mov eax, 0
|
|
||||||
call find_fullname_end
|
|
||||||
add rax, 1
|
|
||||||
mov -16[rbp], rax
|
|
||||||
lea rdx, [buffer]
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
sub rax, rdx
|
|
||||||
mov -24[rbp], rax
|
|
||||||
mov QWORD -8[rbp], 0
|
|
||||||
jmp .L24
|
|
||||||
.L25:
|
|
||||||
mov rdx, -16[rbp]
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
lea rcx, [buffer]
|
|
||||||
mov rdx, -8[rbp]
|
|
||||||
add rdx, rcx
|
|
||||||
mov BYTE [rdx], al
|
|
||||||
mov rdx, -16[rbp]
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov BYTE [rax], 0
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
.L24:
|
|
||||||
mov eax, 1024
|
|
||||||
sub rax, -24[rbp]
|
|
||||||
cmp -8[rbp], rax
|
|
||||||
jb .L25
|
|
||||||
mov rax, [buffer_tail]
|
|
||||||
mov rdx, -24[rbp]
|
|
||||||
neg rdx
|
|
||||||
add rax, rdx
|
|
||||||
mov [buffer_tail], rax
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
preprint db `count: `
|
|
||||||
postprint db `\n`
|
|
||||||
|
|
||||||
print_count:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 32
|
|
||||||
mov -24[rbp], rdi
|
|
||||||
mov edx, 8
|
|
||||||
mov rax, preprint
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write
|
|
||||||
jmp .L27
|
|
||||||
.L28:
|
|
||||||
mov rcx, -24[rbp]
|
|
||||||
mov rdx, -3689348814741910323
|
|
||||||
mov rax, rcx
|
|
||||||
mul rdx
|
|
||||||
shr rdx, 3
|
|
||||||
mov rax, rdx
|
|
||||||
sal rax, 2
|
|
||||||
add rax, rdx
|
|
||||||
add rax, rax
|
|
||||||
sub rcx, rax
|
|
||||||
mov rdx, rcx
|
|
||||||
mov eax, edx
|
|
||||||
add eax, 48
|
|
||||||
mov BYTE -1[rbp], al
|
|
||||||
mov rax, -24[rbp]
|
|
||||||
mov rdx, -3689348814741910323
|
|
||||||
mul rdx
|
|
||||||
mov rax, rdx
|
|
||||||
shr rax, 3
|
|
||||||
mov -24[rbp], rax
|
|
||||||
lea rax, -1[rbp]
|
|
||||||
mov edx, 1
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write
|
|
||||||
.L27:
|
|
||||||
cmp QWORD -24[rbp], 0
|
|
||||||
jne .L28
|
|
||||||
mov edx, 1
|
|
||||||
mov rax, postprint
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
get_name:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 40
|
|
||||||
mov -40[rbp], rdi
|
|
||||||
lea rax, [buffer]
|
|
||||||
mov rdi, rax
|
|
||||||
call find_second_word
|
|
||||||
mov -24[rbp], rax
|
|
||||||
mov rax, -24[rbp]
|
|
||||||
mov -8[rbp], rax
|
|
||||||
jmp .L30
|
|
||||||
.L31:
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
.L30:
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
cmp al, 32
|
|
||||||
jne .L31
|
|
||||||
cmp QWORD -8[rbp], 0
|
|
||||||
jne .L32
|
|
||||||
mov eax, -1
|
|
||||||
jmp .L33
|
|
||||||
.L32:
|
|
||||||
mov QWORD -16[rbp], 0
|
|
||||||
jmp .L34
|
|
||||||
.L35:
|
|
||||||
mov rdx, -24[rbp]
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov rcx, -40[rbp]
|
|
||||||
mov rdx, -16[rbp]
|
|
||||||
add rdx, rcx
|
|
||||||
movzx eax, BYTE [rax]
|
|
||||||
mov BYTE [rdx], al
|
|
||||||
add QWORD -16[rbp], 1
|
|
||||||
.L34:
|
|
||||||
mov rdx, -24[rbp]
|
|
||||||
mov rax, -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
cmp rax, -8[rbp]
|
|
||||||
jb .L35
|
|
||||||
mov eax, 0
|
|
||||||
.L33:
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
|
|
||||||
_start:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 96
|
|
||||||
mov -84[rbp], edi
|
|
||||||
mov -96[rbp], rsi
|
|
||||||
mov rax, -96[rbp]
|
|
||||||
add rax, 8
|
|
||||||
mov rax, [rax]
|
|
||||||
mov rdi, rax
|
|
||||||
call openfile
|
|
||||||
mov -12[rbp], eax
|
|
||||||
mov edx, 64
|
|
||||||
lea rax, [name]
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 0
|
|
||||||
call read
|
|
||||||
mov QWORD -8[rbp], 0
|
|
||||||
mov QWORD -80[rbp], 0
|
|
||||||
mov QWORD -72[rbp], 0
|
|
||||||
mov QWORD -64[rbp], 0
|
|
||||||
mov QWORD -56[rbp], 0
|
|
||||||
mov QWORD -48[rbp], 0
|
|
||||||
mov QWORD -40[rbp], 0
|
|
||||||
mov QWORD -32[rbp], 0
|
|
||||||
mov QWORD -24[rbp], 0
|
|
||||||
.L39:
|
|
||||||
mov eax, 0
|
|
||||||
call find_fullname_end
|
|
||||||
test rax, rax
|
|
||||||
jne .L37
|
|
||||||
mov eax, DWORD -12[rbp]
|
|
||||||
mov edi, eax
|
|
||||||
call fill_buffer
|
|
||||||
.L37:
|
|
||||||
lea rax, -80[rbp]
|
|
||||||
mov rdi, rax
|
|
||||||
call get_name
|
|
||||||
lea rax, -80[rbp]
|
|
||||||
lea rdx, [name]
|
|
||||||
mov rsi, rdx
|
|
||||||
mov rdi, rax
|
|
||||||
call namecmp
|
|
||||||
test eax, eax
|
|
||||||
je .L38
|
|
||||||
add QWORD -8[rbp], 1
|
|
||||||
.L38:
|
|
||||||
mov eax, 0
|
|
||||||
call pop_fullname
|
|
||||||
mov rdx, [buffer_tail]
|
|
||||||
lea rax, [buffer]
|
|
||||||
cmp rdx, rax
|
|
||||||
jne .L39
|
|
||||||
mov rax, -8[rbp]
|
|
||||||
mov rdi, rax
|
|
||||||
call print_count
|
|
||||||
mov eax, 0
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#define BUFF_SIZE 1024
|
|
||||||
#define NAME_SIZE 64
|
|
||||||
#define STDIN 0
|
|
||||||
#define STDOUT 1
|
|
||||||
|
|
||||||
typedef unsigned long long u64;
|
|
||||||
|
|
||||||
char name[NAME_SIZE];
|
|
||||||
char buffer[BUFF_SIZE];
|
|
||||||
char* buffer_tail = buffer; // points on first free symbol
|
|
||||||
|
|
||||||
char iseof = 0;
|
|
||||||
|
|
||||||
int namecmp(char* name1, char* name2)
|
|
||||||
{
|
|
||||||
for (; *name1 && *name2; name1++, name2++)
|
|
||||||
{
|
|
||||||
if (*name1 != *name2)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int openfile(char* filename)
|
|
||||||
{
|
|
||||||
int file = open(filename, O_RDONLY);
|
|
||||||
if (file < 0)
|
|
||||||
{
|
|
||||||
puts("Something went wrong while opening file\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_buffer(int filedesc)
|
|
||||||
{
|
|
||||||
const size_t tofill = BUFF_SIZE - (buffer_tail - buffer);
|
|
||||||
u64 bytes_read = read(filedesc, buffer, tofill);
|
|
||||||
if (bytes_read < BUFF_SIZE)
|
|
||||||
{
|
|
||||||
iseof = 1;
|
|
||||||
}
|
|
||||||
buffer_tail = buffer + bytes_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* find_second_word(char* start)
|
|
||||||
{
|
|
||||||
for (; *start != ' '; start++);
|
|
||||||
return ++start;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* find_fullname_end()
|
|
||||||
{
|
|
||||||
char* end = buffer;
|
|
||||||
for (; (*end != '\n' || *end == 0)
|
|
||||||
&& end < buffer + BUFF_SIZE; end++);
|
|
||||||
if (end == buffer + BUFF_SIZE)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return end;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop_fullname() // pops from buffer
|
|
||||||
{
|
|
||||||
char* end = find_fullname_end() + 1;
|
|
||||||
u64 fullname_len = end - buffer;
|
|
||||||
size_t i = 0;
|
|
||||||
for (; i < BUFF_SIZE - fullname_len; i++)
|
|
||||||
{
|
|
||||||
buffer[i] = end[i];
|
|
||||||
end[i] = 0;
|
|
||||||
}
|
|
||||||
buffer_tail -= fullname_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_count(u64 count)
|
|
||||||
{
|
|
||||||
write(STDOUT, "count: ", 8);
|
|
||||||
while (count)
|
|
||||||
{
|
|
||||||
char digit = (count % 10) + '0';
|
|
||||||
count /= 10;
|
|
||||||
write(STDOUT, &digit, 1);
|
|
||||||
}
|
|
||||||
write(STDOUT, "\n", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_name(char* accumulator)
|
|
||||||
{
|
|
||||||
char* start = find_second_word(buffer);
|
|
||||||
char* end = start;
|
|
||||||
for (; *end != ' '; end++);
|
|
||||||
if (!end)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; &start[i] < end; i++)
|
|
||||||
{
|
|
||||||
accumulator[i] = start[i];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
int filedesc = openfile(argv[1]);
|
|
||||||
read(STDIN, name, sizeof(name)/sizeof(name[0]));
|
|
||||||
// fgets(name, sizeof(name)/sizeof(name[0]), stdin);
|
|
||||||
size_t count = 0;
|
|
||||||
char namebuff[NAME_SIZE] = {0, };
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!find_fullname_end())
|
|
||||||
{
|
|
||||||
fill_buffer(filedesc);
|
|
||||||
}
|
|
||||||
get_name(namebuff);
|
|
||||||
if (namecmp(namebuff, name))
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
pop_fullname();
|
|
||||||
} while (buffer_tail != buffer);
|
|
||||||
print_count(count);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,467 +0,0 @@
|
|||||||
.file "main.c"
|
|
||||||
.intel_syntax noprefix
|
|
||||||
.text
|
|
||||||
.globl name
|
|
||||||
.bss
|
|
||||||
.align 32
|
|
||||||
.type name, @object
|
|
||||||
.size name, 64
|
|
||||||
name:
|
|
||||||
.zero 64
|
|
||||||
.globl buffer
|
|
||||||
.align 32
|
|
||||||
.type buffer, @object
|
|
||||||
.size buffer, 1024
|
|
||||||
buffer:
|
|
||||||
.zero 1024
|
|
||||||
.globl buffer_tail
|
|
||||||
.section .data.rel.local,"aw"
|
|
||||||
.align 8
|
|
||||||
.type buffer_tail, @object
|
|
||||||
.size buffer_tail, 8
|
|
||||||
buffer_tail:
|
|
||||||
.quad buffer
|
|
||||||
.globl iseof
|
|
||||||
.bss
|
|
||||||
.type iseof, @object
|
|
||||||
.size iseof, 1
|
|
||||||
iseof:
|
|
||||||
.zero 1
|
|
||||||
.text
|
|
||||||
.globl namecmp
|
|
||||||
.type namecmp, @function
|
|
||||||
namecmp:
|
|
||||||
.LFB6:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
mov QWORD PTR -8[rbp], rdi
|
|
||||||
mov QWORD PTR -16[rbp], rsi
|
|
||||||
jmp .L2
|
|
||||||
.L6:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx edx, BYTE PTR [rax]
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
cmp dl, al
|
|
||||||
je .L3
|
|
||||||
mov eax, 0
|
|
||||||
jmp .L4
|
|
||||||
.L3:
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
add QWORD PTR -16[rbp], 1
|
|
||||||
.L2:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
test al, al
|
|
||||||
je .L5
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
test al, al
|
|
||||||
jne .L6
|
|
||||||
.L5:
|
|
||||||
mov eax, 1
|
|
||||||
.L4:
|
|
||||||
pop rbp
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE6:
|
|
||||||
.size namecmp, .-namecmp
|
|
||||||
.section .rodata
|
|
||||||
.align 8
|
|
||||||
.LC0:
|
|
||||||
.string "Something went wrong while opening file\n"
|
|
||||||
.text
|
|
||||||
.globl openfile
|
|
||||||
.type openfile, @function
|
|
||||||
openfile:
|
|
||||||
.LFB7:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 32
|
|
||||||
mov QWORD PTR -24[rbp], rdi
|
|
||||||
mov rax, QWORD PTR -24[rbp]
|
|
||||||
mov esi, 0
|
|
||||||
mov rdi, rax
|
|
||||||
mov eax, 0
|
|
||||||
call open@PLT
|
|
||||||
mov DWORD PTR -4[rbp], eax
|
|
||||||
cmp DWORD PTR -4[rbp], 0
|
|
||||||
jns .L8
|
|
||||||
lea rax, .LC0[rip]
|
|
||||||
mov rdi, rax
|
|
||||||
call puts@PLT
|
|
||||||
mov edi, 1
|
|
||||||
call exit@PLT
|
|
||||||
.L8:
|
|
||||||
mov eax, DWORD PTR -4[rbp]
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE7:
|
|
||||||
.size openfile, .-openfile
|
|
||||||
.globl fill_buffer
|
|
||||||
.type fill_buffer, @function
|
|
||||||
fill_buffer:
|
|
||||||
.LFB8:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 32
|
|
||||||
mov DWORD PTR -20[rbp], edi
|
|
||||||
mov rdx, QWORD PTR buffer_tail[rip]
|
|
||||||
lea rax, buffer[rip]
|
|
||||||
sub rdx, rax
|
|
||||||
mov eax, 1024
|
|
||||||
sub rax, rdx
|
|
||||||
mov QWORD PTR -8[rbp], rax
|
|
||||||
mov rdx, QWORD PTR -8[rbp]
|
|
||||||
mov eax, DWORD PTR -20[rbp]
|
|
||||||
lea rcx, buffer[rip]
|
|
||||||
mov rsi, rcx
|
|
||||||
mov edi, eax
|
|
||||||
call read@PLT
|
|
||||||
mov QWORD PTR -16[rbp], rax
|
|
||||||
cmp QWORD PTR -16[rbp], 1023
|
|
||||||
ja .L11
|
|
||||||
mov BYTE PTR iseof[rip], 1
|
|
||||||
.L11:
|
|
||||||
lea rdx, buffer[rip]
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov QWORD PTR buffer_tail[rip], rax
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE8:
|
|
||||||
.size fill_buffer, .-fill_buffer
|
|
||||||
.globl find_second_word
|
|
||||||
.type find_second_word, @function
|
|
||||||
find_second_word:
|
|
||||||
.LFB9:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
mov QWORD PTR -8[rbp], rdi
|
|
||||||
jmp .L13
|
|
||||||
.L14:
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
.L13:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
cmp al, 32
|
|
||||||
jne .L14
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
pop rbp
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE9:
|
|
||||||
.size find_second_word, .-find_second_word
|
|
||||||
.globl find_fullname_end
|
|
||||||
.type find_fullname_end, @function
|
|
||||||
find_fullname_end:
|
|
||||||
.LFB10:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
lea rax, buffer[rip]
|
|
||||||
mov QWORD PTR -8[rbp], rax
|
|
||||||
jmp .L17
|
|
||||||
.L20:
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
.L17:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
cmp al, 10
|
|
||||||
jne .L18
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
test al, al
|
|
||||||
jne .L19
|
|
||||||
.L18:
|
|
||||||
lea rax, buffer[rip+1024]
|
|
||||||
cmp QWORD PTR -8[rbp], rax
|
|
||||||
jb .L20
|
|
||||||
.L19:
|
|
||||||
lea rax, buffer[rip+1024]
|
|
||||||
cmp QWORD PTR -8[rbp], rax
|
|
||||||
jne .L21
|
|
||||||
mov eax, 0
|
|
||||||
jmp .L22
|
|
||||||
.L21:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
.L22:
|
|
||||||
pop rbp
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE10:
|
|
||||||
.size find_fullname_end, .-find_fullname_end
|
|
||||||
.globl pop_fullname
|
|
||||||
.type pop_fullname, @function
|
|
||||||
pop_fullname:
|
|
||||||
.LFB11:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 32
|
|
||||||
mov eax, 0
|
|
||||||
call find_fullname_end
|
|
||||||
add rax, 1
|
|
||||||
mov QWORD PTR -16[rbp], rax
|
|
||||||
lea rdx, buffer[rip]
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
sub rax, rdx
|
|
||||||
mov QWORD PTR -24[rbp], rax
|
|
||||||
mov QWORD PTR -8[rbp], 0
|
|
||||||
jmp .L24
|
|
||||||
.L25:
|
|
||||||
mov rdx, QWORD PTR -16[rbp]
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
lea rcx, buffer[rip]
|
|
||||||
mov rdx, QWORD PTR -8[rbp]
|
|
||||||
add rdx, rcx
|
|
||||||
mov BYTE PTR [rdx], al
|
|
||||||
mov rdx, QWORD PTR -16[rbp]
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov BYTE PTR [rax], 0
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
.L24:
|
|
||||||
mov eax, 1024
|
|
||||||
sub rax, QWORD PTR -24[rbp]
|
|
||||||
cmp QWORD PTR -8[rbp], rax
|
|
||||||
jb .L25
|
|
||||||
mov rax, QWORD PTR buffer_tail[rip]
|
|
||||||
mov rdx, QWORD PTR -24[rbp]
|
|
||||||
neg rdx
|
|
||||||
add rax, rdx
|
|
||||||
mov QWORD PTR buffer_tail[rip], rax
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE11:
|
|
||||||
.size pop_fullname, .-pop_fullname
|
|
||||||
.section .rodata
|
|
||||||
.LC1:
|
|
||||||
.string "count: "
|
|
||||||
.LC2:
|
|
||||||
.string "\n"
|
|
||||||
.text
|
|
||||||
.globl print_count
|
|
||||||
.type print_count, @function
|
|
||||||
print_count:
|
|
||||||
.LFB12:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 32
|
|
||||||
mov QWORD PTR -24[rbp], rdi
|
|
||||||
mov edx, 8
|
|
||||||
lea rax, .LC1[rip]
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write@PLT
|
|
||||||
jmp .L27
|
|
||||||
.L28:
|
|
||||||
mov rcx, QWORD PTR -24[rbp]
|
|
||||||
movabs rdx, -3689348814741910323
|
|
||||||
mov rax, rcx
|
|
||||||
mul rdx
|
|
||||||
shr rdx, 3
|
|
||||||
mov rax, rdx
|
|
||||||
sal rax, 2
|
|
||||||
add rax, rdx
|
|
||||||
add rax, rax
|
|
||||||
sub rcx, rax
|
|
||||||
mov rdx, rcx
|
|
||||||
mov eax, edx
|
|
||||||
add eax, 48
|
|
||||||
mov BYTE PTR -1[rbp], al
|
|
||||||
mov rax, QWORD PTR -24[rbp]
|
|
||||||
movabs rdx, -3689348814741910323
|
|
||||||
mul rdx
|
|
||||||
mov rax, rdx
|
|
||||||
shr rax, 3
|
|
||||||
mov QWORD PTR -24[rbp], rax
|
|
||||||
lea rax, -1[rbp]
|
|
||||||
mov edx, 1
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write@PLT
|
|
||||||
.L27:
|
|
||||||
cmp QWORD PTR -24[rbp], 0
|
|
||||||
jne .L28
|
|
||||||
mov edx, 1
|
|
||||||
lea rax, .LC2[rip]
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 1
|
|
||||||
call write@PLT
|
|
||||||
nop
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE12:
|
|
||||||
.size print_count, .-print_count
|
|
||||||
.globl get_name
|
|
||||||
.type get_name, @function
|
|
||||||
get_name:
|
|
||||||
.LFB13:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 40
|
|
||||||
mov QWORD PTR -40[rbp], rdi
|
|
||||||
lea rax, buffer[rip]
|
|
||||||
mov rdi, rax
|
|
||||||
call find_second_word
|
|
||||||
mov QWORD PTR -24[rbp], rax
|
|
||||||
mov rax, QWORD PTR -24[rbp]
|
|
||||||
mov QWORD PTR -8[rbp], rax
|
|
||||||
jmp .L30
|
|
||||||
.L31:
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
.L30:
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
cmp al, 32
|
|
||||||
jne .L31
|
|
||||||
cmp QWORD PTR -8[rbp], 0
|
|
||||||
jne .L32
|
|
||||||
mov eax, -1
|
|
||||||
jmp .L33
|
|
||||||
.L32:
|
|
||||||
mov QWORD PTR -16[rbp], 0
|
|
||||||
jmp .L34
|
|
||||||
.L35:
|
|
||||||
mov rdx, QWORD PTR -24[rbp]
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
mov rcx, QWORD PTR -40[rbp]
|
|
||||||
mov rdx, QWORD PTR -16[rbp]
|
|
||||||
add rdx, rcx
|
|
||||||
movzx eax, BYTE PTR [rax]
|
|
||||||
mov BYTE PTR [rdx], al
|
|
||||||
add QWORD PTR -16[rbp], 1
|
|
||||||
.L34:
|
|
||||||
mov rdx, QWORD PTR -24[rbp]
|
|
||||||
mov rax, QWORD PTR -16[rbp]
|
|
||||||
add rax, rdx
|
|
||||||
cmp rax, QWORD PTR -8[rbp]
|
|
||||||
jb .L35
|
|
||||||
mov eax, 0
|
|
||||||
.L33:
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE13:
|
|
||||||
.size get_name, .-get_name
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
.LFB14:
|
|
||||||
.cfi_startproc
|
|
||||||
push rbp
|
|
||||||
.cfi_def_cfa_offset 16
|
|
||||||
.cfi_offset 6, -16
|
|
||||||
mov rbp, rsp
|
|
||||||
.cfi_def_cfa_register 6
|
|
||||||
sub rsp, 96
|
|
||||||
mov DWORD PTR -84[rbp], edi
|
|
||||||
mov QWORD PTR -96[rbp], rsi
|
|
||||||
mov rax, QWORD PTR -96[rbp]
|
|
||||||
add rax, 8
|
|
||||||
mov rax, QWORD PTR [rax]
|
|
||||||
mov rdi, rax
|
|
||||||
call openfile
|
|
||||||
mov DWORD PTR -12[rbp], eax
|
|
||||||
mov edx, 64
|
|
||||||
lea rax, name[rip]
|
|
||||||
mov rsi, rax
|
|
||||||
mov edi, 0
|
|
||||||
call read@PLT
|
|
||||||
mov QWORD PTR -8[rbp], 0
|
|
||||||
mov QWORD PTR -80[rbp], 0
|
|
||||||
mov QWORD PTR -72[rbp], 0
|
|
||||||
mov QWORD PTR -64[rbp], 0
|
|
||||||
mov QWORD PTR -56[rbp], 0
|
|
||||||
mov QWORD PTR -48[rbp], 0
|
|
||||||
mov QWORD PTR -40[rbp], 0
|
|
||||||
mov QWORD PTR -32[rbp], 0
|
|
||||||
mov QWORD PTR -24[rbp], 0
|
|
||||||
.L39:
|
|
||||||
mov eax, 0
|
|
||||||
call find_fullname_end
|
|
||||||
test rax, rax
|
|
||||||
jne .L37
|
|
||||||
mov eax, DWORD PTR -12[rbp]
|
|
||||||
mov edi, eax
|
|
||||||
call fill_buffer
|
|
||||||
.L37:
|
|
||||||
lea rax, -80[rbp]
|
|
||||||
mov rdi, rax
|
|
||||||
call get_name
|
|
||||||
lea rax, -80[rbp]
|
|
||||||
lea rdx, name[rip]
|
|
||||||
mov rsi, rdx
|
|
||||||
mov rdi, rax
|
|
||||||
call namecmp
|
|
||||||
test eax, eax
|
|
||||||
je .L38
|
|
||||||
add QWORD PTR -8[rbp], 1
|
|
||||||
.L38:
|
|
||||||
mov eax, 0
|
|
||||||
call pop_fullname
|
|
||||||
mov rdx, QWORD PTR buffer_tail[rip]
|
|
||||||
lea rax, buffer[rip]
|
|
||||||
cmp rdx, rax
|
|
||||||
jne .L39
|
|
||||||
mov rax, QWORD PTR -8[rbp]
|
|
||||||
mov rdi, rax
|
|
||||||
call print_count
|
|
||||||
mov eax, 0
|
|
||||||
leave
|
|
||||||
.cfi_def_cfa 7, 8
|
|
||||||
ret
|
|
||||||
.cfi_endproc
|
|
||||||
.LFE14:
|
|
||||||
.size main, .-main
|
|
||||||
.ident "GCC: (Debian 14.2.0-6) 14.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@ -1,292 +0,0 @@
|
|||||||
global _start
|
|
||||||
|
|
||||||
%define STDIN 0
|
|
||||||
%define STDOUT 1
|
|
||||||
%define STDERR 2
|
|
||||||
%define RD_ONLY 00
|
|
||||||
|
|
||||||
%define FILE_BUF_SIZE 1024
|
|
||||||
|
|
||||||
section .data
|
|
||||||
buffer_tail_ptr dq file_buffer ; stores pointer to the end of the buffer for file
|
|
||||||
is_eof db 0 ; indicates if file ended
|
|
||||||
count dq 0 ; сколько имен нашлось
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
name resb 128
|
|
||||||
real_name_len resb 1
|
|
||||||
name_size equ $-name
|
|
||||||
|
|
||||||
file_descriptor resq 1
|
|
||||||
file_buffer resb FILE_BUF_SIZE
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
%macro PRINT_STR 1
|
|
||||||
jmp %%run ; we define data right in code. It's not good, but ok for prototype
|
|
||||||
%%string db %1
|
|
||||||
%%string_len equ $-%%string
|
|
||||||
%%run:
|
|
||||||
mov rax, 1
|
|
||||||
mov rdi, STDOUT
|
|
||||||
mov rsi, %%string
|
|
||||||
mov rdx, %%string_len
|
|
||||||
syscall
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
%macro MPUSH 1-*
|
|
||||||
%rep %0
|
|
||||||
push %1
|
|
||||||
%rotate 1
|
|
||||||
%endrep
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
%macro MPOP_R 1-*
|
|
||||||
%rotate -1
|
|
||||||
%rep %0
|
|
||||||
pop %1
|
|
||||||
%rotate -1
|
|
||||||
%endrep
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
is_alpha: ; dl - symbol in ascii
|
|
||||||
xor al, al
|
|
||||||
cmp dl, 'A'
|
|
||||||
jl .exit
|
|
||||||
cmp dl, 'z'
|
|
||||||
jg .exit
|
|
||||||
; dl < 'Z' or dl > 'a'
|
|
||||||
cmp dl, 'Z'
|
|
||||||
jl .true
|
|
||||||
cmp dl, 'a'
|
|
||||||
jg .true
|
|
||||||
jmp .exit
|
|
||||||
.true:
|
|
||||||
mov al, 1
|
|
||||||
.exit:
|
|
||||||
ret
|
|
||||||
|
|
||||||
fill_buf: ; read bytes from file to buffer
|
|
||||||
MPUSH rdi, rsi, rcx, r11, rdx
|
|
||||||
mov rdi, [buffer_tail_ptr]
|
|
||||||
call buffer_left
|
|
||||||
mov rdx, rax
|
|
||||||
mov rax, 0
|
|
||||||
mov rdi, [file_descriptor]
|
|
||||||
mov rsi, [buffer_tail_ptr]
|
|
||||||
; mov rdx, FILE_BUF_SIZE
|
|
||||||
syscall
|
|
||||||
cmp rax, 0
|
|
||||||
jl file_read_error
|
|
||||||
add [buffer_tail_ptr], rax
|
|
||||||
cmp rax, FILE_BUF_SIZE
|
|
||||||
jg .exit
|
|
||||||
mov byte [is_eof], 1
|
|
||||||
.exit:
|
|
||||||
MPOP_R rdi, rsi, rcx, r11, rdx
|
|
||||||
ret
|
|
||||||
|
|
||||||
read_name: ; ask user for name he is searching for
|
|
||||||
MPUSH rax, rdi, rsi, rdx, rcx
|
|
||||||
mov rax, 0
|
|
||||||
mov rdi, STDIN
|
|
||||||
mov rsi, name
|
|
||||||
mov rdx, name_size
|
|
||||||
syscall ; returns number of read bytes
|
|
||||||
cmp rax, 0
|
|
||||||
jl read_error
|
|
||||||
test rax, rax
|
|
||||||
jz no_name_error ; if no bytes were read
|
|
||||||
mov [real_name_len], al
|
|
||||||
; if user didn't end output with CTRL+D, there will be one extra symbol
|
|
||||||
mov dl, [name + rax - 1]
|
|
||||||
call is_alpha ; check if last symbol is alpha
|
|
||||||
test al, al
|
|
||||||
jnz .exit
|
|
||||||
; if last symbol is \n or something simmilar, erase it
|
|
||||||
xor rdx, rdx
|
|
||||||
mov dl, byte [real_name_len]
|
|
||||||
mov byte [name + rdx - 1], 0 ; clear last char if non alpha
|
|
||||||
dec byte [real_name_len]
|
|
||||||
.exit:
|
|
||||||
MPOP_R rax, rdi, rsi, rdx, rcx
|
|
||||||
ret
|
|
||||||
|
|
||||||
are_names_equal: ; takes first fullname from buff and compares
|
|
||||||
MPUSH rcx, rsi, rdi
|
|
||||||
; jump to second word
|
|
||||||
mov rdi, file_buffer
|
|
||||||
mov rcx, FILE_BUF_SIZE
|
|
||||||
mov al, ' '
|
|
||||||
repne scasb ; Теперь мы указываем на символ второго слова
|
|
||||||
mov r8, rdi ; store for later
|
|
||||||
repne scasb ; Ищем второй пробел, чтобы определить границы второго слова
|
|
||||||
dec rdi ; Нашелся второй пробел. убавляем 1 чтобы указывать именно на него
|
|
||||||
sub rdi, r8 ; len
|
|
||||||
mov rax, rdi
|
|
||||||
cmp al, [real_name_len]
|
|
||||||
jnz .false ; if lenghts are different - already false
|
|
||||||
mov rdi, r8 ; return to pointer on second word
|
|
||||||
mov rsi, name
|
|
||||||
xor rcx, rcx
|
|
||||||
mov cl, [real_name_len]
|
|
||||||
repe cmpsb
|
|
||||||
jnz .false
|
|
||||||
.true:
|
|
||||||
mov rax, 1
|
|
||||||
jmp .exit
|
|
||||||
.false:
|
|
||||||
mov rax, 0
|
|
||||||
.exit:
|
|
||||||
MPOP_R rcx, rsi ,rdi
|
|
||||||
ret
|
|
||||||
|
|
||||||
buffer_left: ; counts free part of buffer
|
|
||||||
push rdi
|
|
||||||
mov rax, FILE_BUF_SIZE
|
|
||||||
sub rdi, file_buffer
|
|
||||||
sub rax, rdi
|
|
||||||
pop rdi
|
|
||||||
ret
|
|
||||||
|
|
||||||
pop_from_addr: ; frees buffer from one name until \n, takes 1 addr
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
MPUSH rdx, rax
|
|
||||||
sub rsp, 24
|
|
||||||
mov [rbp-24], rdi ; char* start
|
|
||||||
call buffer_left
|
|
||||||
mov [rbp-16], rax ; * count
|
|
||||||
mov QWORD [rbp-8], 0 ; i
|
|
||||||
;loop
|
|
||||||
.loop:
|
|
||||||
; read byte from start[i]
|
|
||||||
mov rdx, [rbp-24]
|
|
||||||
mov rax, [rbp-8]
|
|
||||||
add rax, rdx
|
|
||||||
mov al, [rax]
|
|
||||||
; place to buffer[i]
|
|
||||||
mov rdx, [rbp-8]
|
|
||||||
add rdx, file_buffer
|
|
||||||
mov [rdx], al
|
|
||||||
; move zero to read position
|
|
||||||
mov rdx, [rbp-24]
|
|
||||||
mov rax, [rbp-8]
|
|
||||||
add rax, rdx
|
|
||||||
mov byte [rax], 0
|
|
||||||
inc qword [rbp-8]
|
|
||||||
; .cond:
|
|
||||||
mov rax, [rbp-8]
|
|
||||||
cmp rax, [rbp-16]
|
|
||||||
jb .loop
|
|
||||||
; update buffer_tail_ptr
|
|
||||||
mov rax, [rbp-24]
|
|
||||||
sub rax, file_buffer
|
|
||||||
sub [buffer_tail_ptr], rax
|
|
||||||
add rsp, 24
|
|
||||||
MPOP_R rcx, rax
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
free_fullname:
|
|
||||||
MPUSH rcx, rdi, rax
|
|
||||||
mov rcx, FILE_BUF_SIZE
|
|
||||||
mov rdi, file_buffer
|
|
||||||
mov al, `\n`
|
|
||||||
repne scasb
|
|
||||||
call pop_from_addr
|
|
||||||
MPOP_R rcx, rdi, rax
|
|
||||||
ret
|
|
||||||
|
|
||||||
_start:
|
|
||||||
; --parse command_line arguments
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
; --check if there is any args
|
|
||||||
mov rax, [rbp + 8]
|
|
||||||
cmp rax, 2
|
|
||||||
jl no_filepath
|
|
||||||
; --if argument specified, try to read path
|
|
||||||
mov rdi, [rbp + 8*3]
|
|
||||||
mov rax, 0x2 ; open file
|
|
||||||
mov rsi, RD_ONLY
|
|
||||||
xor rdx, rdx
|
|
||||||
syscall ; filepath already should be in rdi
|
|
||||||
mov [file_descriptor], rax
|
|
||||||
cmp rax, 0
|
|
||||||
jl file_open_error
|
|
||||||
; --asking user what name we are looking for
|
|
||||||
PRINT_STR `Input name you are looking for: `
|
|
||||||
call read_name
|
|
||||||
; -- find number of occurenses. HARDEST PART
|
|
||||||
.fill_buff_prefix:
|
|
||||||
call fill_buf
|
|
||||||
.name_cmp_loop:
|
|
||||||
mov rdi, name
|
|
||||||
call are_names_equal
|
|
||||||
test rax, rax
|
|
||||||
jz .no_increment
|
|
||||||
inc qword [count]
|
|
||||||
.no_increment:
|
|
||||||
call free_fullname
|
|
||||||
; iseof && buffer_tail_ptr == file_buffer
|
|
||||||
mov rax, [buffer_tail_ptr]
|
|
||||||
cmp rax, file_buffer
|
|
||||||
jne .name_cmp_loop
|
|
||||||
mov al, [is_eof]
|
|
||||||
test al, al
|
|
||||||
jz .fill_buff_prefix
|
|
||||||
;formatted output
|
|
||||||
mov rax, 1 ; write
|
|
||||||
mov rdi, STDOUT
|
|
||||||
mov rsi, name
|
|
||||||
mov rdx, [real_name_len]
|
|
||||||
syscall
|
|
||||||
PRINT_STR " ............... "
|
|
||||||
; printing number
|
|
||||||
mov rax, [count]
|
|
||||||
mov rbx, 10
|
|
||||||
xor rcx, rcx
|
|
||||||
.number_converting_loop:
|
|
||||||
xor rdx, rdx
|
|
||||||
div rbx
|
|
||||||
; push 1 byte to stack
|
|
||||||
add dl, '0' ; convert to ascii
|
|
||||||
dec rsp
|
|
||||||
mov [rsp], dl
|
|
||||||
inc rcx
|
|
||||||
test rax, rax
|
|
||||||
jnz .number_converting_loop
|
|
||||||
mov rax, 1
|
|
||||||
mov rdi, STDOUT
|
|
||||||
mov rsi, rsp
|
|
||||||
mov rdx, rcx
|
|
||||||
syscall
|
|
||||||
PRINT_STR `\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
read_error:
|
|
||||||
PRINT_STR `Error while reading the name\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
no_name_error:
|
|
||||||
PRINT_STR `No propper name given\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
file_open_error:
|
|
||||||
PRINT_STR `Failed to open the file\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
file_read_error:
|
|
||||||
PRINT_STR `Error while reading the file\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
no_filepath:
|
|
||||||
PRINT_STR `You haven't specified a file to read from\n`
|
|
||||||
jmp exit
|
|
||||||
|
|
||||||
exit:
|
|
||||||
mov rax, 60
|
|
||||||
mov rdi, 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user