From 65d5c3f5c3a459a85441aa08690d04aa6926632e Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Tue, 10 Sep 2024 20:13:01 +0300 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=D1=81=D0=BC=D0=BE=D0=B3=20=D0=BE?= =?UTF-8?q?=D1=87=D0=B8=D1=81=D1=82=D0=B8=D1=82=D1=8C=20=D0=BE=D1=82=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B5=D0=B2=20=D0=B8=20=D0=BE=D1=82=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/Labv1.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 01-asm-basics/Labv1.cpp diff --git a/01-asm-basics/Labv1.cpp b/01-asm-basics/Labv1.cpp new file mode 100644 index 0000000..de3204b --- /dev/null +++ b/01-asm-basics/Labv1.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#define PortCan0 0x40 + +void beep(unsigned iTone, unsigned iDlit); + +int main(void) { + + long int lCnt = 0; + int iA = 0x1234; + + char *pT = (char *)0x46C; + printf("\n\n"); + for (int i = 0; i < 10; i++) + printf(" \n %d ", *pT); + printf("\n\n"); + getch(); + + printf("\n ⠥ ᮤন ᮬ 40 㭪樨 \n"); + printf("\n 室 横 - \n"); + + while (bioskey(1) == 0) { + printf(" \n 40 = %d ", inp(PortCan0)); + delay(500); + } + getch(); + printf("\n ⠥ ᮤন ᮬ 40 ᥬ஬ \n"); + + while (bioskey(1) == 0) { + asm { + push ax + in al,0x40 + } + unsigned char Tmm = _AL; + asm pop ax delay(500); + printf(" \n 40 = %d ", Tmm); + } + getch(); + printf("\n த - \n "); + getch(); + + long far *pTime = (long *)0x46C; + while (bioskey(1) == 0) { + printf("\n %ld", *pTime); + delay(1000); + } + getch(); + + int Time; + while (bioskey(1) == 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); + } + + 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(); +} -- 2.49.0 From 34d0c33e87ef02ffda2b78d77b68841044307bfd Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Tue, 10 Sep 2024 21:15:57 +0300 Subject: [PATCH 02/13] =?UTF-8?q?chore:=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=20=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B5=D0=BD=20=D0=BD?= =?UTF-8?q?=D0=B0=2030%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Заменены все поломанные символы на нормальный текст, сделан проход форматером, заменен delay --- 01-asm-basics/Labv1.cpp | 59 ++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/01-asm-basics/Labv1.cpp b/01-asm-basics/Labv1.cpp index de3204b..7e59ea6 100644 --- a/01-asm-basics/Labv1.cpp +++ b/01-asm-basics/Labv1.cpp @@ -1,33 +1,36 @@ -#include -#include -#include #include +#include +#include #define PortCan0 0x40 void beep(unsigned iTone, unsigned iDlit); -int main(void) { +void delay(unsigned int ms) +{ + usleep(ms * 1000); +} +int main(void) { long int lCnt = 0; int iA = 0x1234; char *pT = (char *)0x46C; - printf("\n\n"); + printf("\nПечатаем 10 раз значение байта с известным адресом\n"); for (int i = 0; i < 10; i++) printf(" \n %d ", *pT); - printf("\n\n"); - getch(); + printf("\n Для продолжения нажмите любую клавишу \n"); + system("pause"); // Ждем нажатия клавиши - printf("\n ⠥ ᮤন ᮬ 40 㭪樨 \n"); - printf("\n 室 横 - \n"); + printf("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n"); + printf("\n Для выхода из цикла - нажмите любую клавишу \n"); while (bioskey(1) == 0) { - printf(" \n 40 = %d ", inp(PortCan0)); + printf("\n Порт40 = %d", inp(PortCan0)); delay(500); } - getch(); - printf("\n ⠥ ᮤন ᮬ 40 ᥬ஬ \n"); + system("pause"); + printf("\n Читаем содержимое порта с адресом 40 ассемблером \n"); while (bioskey(1) == 0) { asm { @@ -35,27 +38,32 @@ int main(void) { in al,0x40 } unsigned char Tmm = _AL; - asm pop ax delay(500); - printf(" \n 40 = %d ", Tmm); + asm pop ax + delay(500); + printf("\n Порт40 = %d", Tmm); } - getch(); - printf("\n த - \n "); - getch(); + system("pause"); + printf("\n Для продолжения - нажмите любую клавишу \n"); + system("pause"); - long far *pTime = (long *)0x46C; + long *pTime = (long *)0x46C; while (bioskey(1) == 0) { printf("\n %ld", *pTime); delay(1000); } - getch(); + system("pause"); int Time; while (bioskey(1) == 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 + 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); @@ -64,7 +72,8 @@ int main(void) { beep(400, 200); for (lCnt = 0; lCnt < 1000000; lCnt++) { a1: - asm { mov ax,iA + asm { + mov ax,iA mov ax,iA mov ax,iA mov ax,iA -- 2.49.0 From 386c7be1a7a1d510f5bab384c98adc31c22ff290 Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Tue, 10 Sep 2024 23:43:12 +0300 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BB=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D1=8B=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Для повторения bioskey было написано несколько новых функций, переводящих теорминал в неканонический режим и читающие нажатия на клавиши. Также все наработки были вынесены в отдельный файл, чтобы не засорять основной файл --- 01-asm-basics/substitutions.c | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 01-asm-basics/substitutions.c diff --git a/01-asm-basics/substitutions.c b/01-asm-basics/substitutions.c new file mode 100644 index 0000000..c59edcd --- /dev/null +++ b/01-asm-basics/substitutions.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +/* 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(); +//} -- 2.49.0 From a338ac968ecf1304c8a69546f3f3339afa0287e1 Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Tue, 10 Sep 2024 23:44:30 +0300 Subject: [PATCH 04/13] =?UTF-8?q?chore:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BB=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0,=20=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=20=D0=BA=D0=B0=D0=BA=20=D1=81=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/{Labv1.cpp => main.c} | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) rename 01-asm-basics/{Labv1.cpp => main.c} (87%) diff --git a/01-asm-basics/Labv1.cpp b/01-asm-basics/main.c similarity index 87% rename from 01-asm-basics/Labv1.cpp rename to 01-asm-basics/main.c index 7e59ea6..9a8d417 100644 --- a/01-asm-basics/Labv1.cpp +++ b/01-asm-basics/main.c @@ -18,7 +18,9 @@ int main(void) { char *pT = (char *)0x46C; printf("\nПечатаем 10 раз значение байта с известным адресом\n"); for (int i = 0; i < 10; i++) + { printf(" \n %d ", *pT); + } printf("\n Для продолжения нажмите любую клавишу \n"); system("pause"); // Ждем нажатия клавиши @@ -29,6 +31,7 @@ int main(void) { printf("\n Порт40 = %d", inp(PortCan0)); delay(500); } + system("pause"); printf("\n Читаем содержимое порта с адресом 40 ассемблером \n"); @@ -73,18 +76,18 @@ int main(void) { 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 - } + 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); } -- 2.49.0 From 4c423c7acc9cac34998de1c21729dbf3c322b0ba Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Tue, 10 Sep 2024 23:53:08 +0300 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20?= =?UTF-8?q?=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B1=D1=8B=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=88=D0=B8=D1=82=D1=8B=20=D0=BA=20?= =?UTF-8?q?=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D0=BE=D0=BC=D1=83=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/main.c | 18 ++++++++++++++---- 01-asm-basics/substitutions.c | 1 + 01-asm-basics/substitutions.h | 9 +++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 01-asm-basics/substitutions.h diff --git a/01-asm-basics/main.c b/01-asm-basics/main.c index 9a8d417..167ca2e 100644 --- a/01-asm-basics/main.c +++ b/01-asm-basics/main.c @@ -2,6 +2,8 @@ #include #include +#include "substitutions.h" + #define PortCan0 0x40 void beep(unsigned iTone, unsigned iDlit); @@ -27,15 +29,18 @@ int main(void) { printf("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n"); printf("\n Для выхода из цикла - нажмите любую клавишу \n"); - while (bioskey(1) == 0) { + set_input_mode(); + while (isKeyPressed() == 0) { printf("\n Порт40 = %d", inp(PortCan0)); delay(500); } + reset_input_mode(); system("pause"); printf("\n Читаем содержимое порта с адресом 40 ассемблером \n"); - while (bioskey(1) == 0) { + set_input_mode(); + while (isKeyPressed() == 0) { asm { push ax in al,0x40 @@ -45,19 +50,23 @@ int main(void) { delay(500); printf("\n Порт40 = %d", Tmm); } + reset_input_mode(); system("pause"); printf("\n Для продолжения - нажмите любую клавишу \n"); system("pause"); long *pTime = (long *)0x46C; - while (bioskey(1) == 0) { + set_input_mode(); + while (isKeyPressed() == 0) { printf("\n %ld", *pTime); delay(1000); } + reset_input_mode(); system("pause"); int Time; - while (bioskey(1) == 0) { + set_input_mode(); + while (isKeyPressed() == 0) { asm push ds asm push si asm mov ax, 40h @@ -71,6 +80,7 @@ int main(void) { printf("\n %d", Time); delay(300); } + reset_input_mode(); beep(400, 200); for (lCnt = 0; lCnt < 1000000; lCnt++) { diff --git a/01-asm-basics/substitutions.c b/01-asm-basics/substitutions.c index c59edcd..d6cb262 100644 --- a/01-asm-basics/substitutions.c +++ b/01-asm-basics/substitutions.c @@ -1,3 +1,4 @@ +#include "substitutions.h" #include #include #include diff --git a/01-asm-basics/substitutions.h b/01-asm-basics/substitutions.h new file mode 100644 index 0000000..8bf31e0 --- /dev/null +++ b/01-asm-basics/substitutions.h @@ -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.49.0 From 3412167a090c8b1cee72b9579fe13280515a0f18 Mon Sep 17 00:00:00 2001 From: ElectronixTM Date: Wed, 11 Sep 2024 18:25:36 +0300 Subject: [PATCH 06/13] =?UTF-8?q?docs:=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BC=D0=B0=D0=BD=D0=B8=D0=BF=D1=83=D0=BB=D1=8F?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D1=81=20=D1=83=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=BE=D0=B9=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D1=80=D0=BE=D0=B2=20=D1=82=D0=B5=D1=80=D0=BC=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/README.md | 96 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/01-asm-basics/README.md b/01-asm-basics/README.md index 35947fd..b0cd612 100644 --- a/01-asm-basics/README.md +++ b/01-asm-basics/README.md @@ -2,3 +2,99 @@ ## Введение в низкоуровневое программирование. Встроенный отладчик. Встроенный Ассемблер +## Переписываем шаблон + +Поскольку весь шаблонный текст написан под 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 имеет определенный уровень абстракции от конкретных системных вызовов и предоставляет нам несколько вещей: + +- `` - структура данных, хранящая информацию о текущем состоянии терминала, а также удобные методы `tcgetattr` и `tcsetattr` +- `` - Библиотека, используемая для унификации дескрипторов, битов и прочих унификаций +- `` - много чего, но нам для безопасности потребуется `atexit`, чтобы если что-то пошло не так, у нас не наебнулся терминал + +Опционально берется `` для целей адекватного вывода ошибок. Не обязательно, но предпочтительно + +#### Реализация + +Для начала нам необходимо сохранить свой текущий терминал, чтобы без проблем его восстановить в будущем, для этого заводим в памяти переменную (придется сделать ее глобальной, потому что на инкапсуляцию и защиту нет времени, нервов и желания) + +```C +struct termios saved_attributes; +``` + +Далее сразу напишем функцию для восстановления + +```C +void reset_input_mode() +{ + tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); +} +``` + +Здесь `STDIN_FILENO` - это дескриптор потока стандартного ввода (ввод с консоли по простяге). Вообще это число, но в `` он вынесен в макрос для хоть какой-то унификации, `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] = 1; + tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr); +} +``` -- 2.49.0 From ca7fbed016aeaa0a70e48f0558714c2d0cd16e47 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 12 Sep 2024 16:25:41 +0300 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=BE=20=D0=B2=D1=81=D0=B5,=20=D1=87?= =?UTF-8?q?=D1=82=D0=BE=20=D0=BD=D0=B5=20=D0=B0=D1=81=D1=81=D0=B5=D0=BC?= =?UTF-8?q?=D0=B1=D0=BB=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/main.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/01-asm-basics/main.c b/01-asm-basics/main.c index 167ca2e..6abbced 100644 --- a/01-asm-basics/main.c +++ b/01-asm-basics/main.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -14,6 +15,7 @@ void delay(unsigned int ms) } int main(void) { + long int lCnt = 0; int iA = 0x1234; @@ -29,9 +31,12 @@ int main(void) { printf("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n"); printf("\n Для выхода из цикла - нажмите любую клавишу \n"); + // Линуксу не сильно нравится, что ты насилуешь порты ввода и вывода процессора, поэтому нужно выдать ему на это дело разрешение + + ioperm(PortCan0, 1, 3); // Что означает тройка напишу позже set_input_mode(); while (isKeyPressed() == 0) { - printf("\n Порт40 = %d", inp(PortCan0)); + printf("\n Порт40 = %d", inb(PortCan0)); delay(500); } reset_input_mode(); @@ -102,8 +107,8 @@ int main(void) { beep(400, 200); } -void beep(unsigned iTone, unsigned iDlit) { - sound(iTone); - delay(iDlit); - nosound(); -} +//void beep(unsigned iTone, unsigned iDlit) { +// sound(iTone); +// delay(iDlit); +// nosound(); +//} -- 2.49.0 From acc1ada69cc3a8b0dfc4a5b730e758bfb8b62c86 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 12 Sep 2024 20:14:32 +0300 Subject: [PATCH 08/13] =?UTF-8?q?docs:=20=D0=B2=20README=201=20=D0=BB?= =?UTF-8?q?=D0=B0=D0=B1=D1=8B=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D0=B5=D0=BA=D1=86=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=20=D0=B0=D1=81=D1=81=D0=B5=D0=BC=D0=B1=D0=BB=D0=B5?= =?UTF-8?q?=D1=80=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/01-asm-basics/README.md b/01-asm-basics/README.md index b0cd612..92b703e 100644 --- a/01-asm-basics/README.md +++ b/01-asm-basics/README.md @@ -94,7 +94,27 @@ void set_input_mode() // нас в общем-то интересуют tcgetattr (STDIN_FILENO, &tattr); tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ - tattr.c_cc[VMIN] = 1; + 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 -- 2.49.0 From a5a36086b0f2810691cceb40c9fd178825ed948d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 13 Sep 2024 00:07:13 +0300 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=B3=D0=BE=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=B4=20=D0=B7=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=80=D1=8B=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/time.asm | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 01-asm-basics/time.asm diff --git a/01-asm-basics/time.asm b/01-asm-basics/time.asm new file mode 100644 index 0000000..458ce24 --- /dev/null +++ b/01-asm-basics/time.asm @@ -0,0 +1,67 @@ +global main + +extern printf + +%define CLOCK_REALTIME 0 + +; struct timespec { time_t tv_sec; long tv_nsec; } +struc timespec + .tv_sec: resq 1 + .tv_nsec: resq 1 +endstruc + +section .note.GNU-stack ; чтобы не жаловался линкер + +section .bss + +start: ; uses timespec model + times 2 resq 1 + +finish: + times 2 resq 1 + +section .data + + fstring db "Operations took %ul seconds and %ul milliseconds", 10, 0 + flen equ $-fstring + +section .text + +main: ; лично в моей системе time_t представляет из себя long int + mov rax, 228 ; Системный вызов получения времени + mov rdi, CLOCK_REALTIME + mov rsi, start + syscall + + ; insert your code here + mov rcx, 20000 + + looper: + mov rax, start + loop looper + + mov rax, 228 + mov rdi, CLOCK_REALTIME + mov rsi, finish + syscall + + ; считаем время для секунда и миллисекунд + ; секунды + mov rsi, [finish + timespec.tv_sec] + sub rsi, [start + timespec.tv_sec] + + ; миллисекунды + mov rdx, [finish + timespec.tv_nsec] + sub rdx, [start + timespec.tv_nsec] + + mov rdi, fstring + mov rax, 0 + sub rsp, 8 + call printf + add rsp, 8 + +exit: + mov rax, 60 + mov rdi, 0 + syscall + -- 2.49.0 From 6c9d810c6e305f671a0d16be44360207e2623789 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 13 Sep 2024 00:08:49 +0300 Subject: [PATCH 10/13] =?UTF-8?q?chore:=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=B4=D0=B5=20?= =?UTF-8?q?=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/main.c | 111 ++++++++++++++++++++-------------- 01-asm-basics/substitutions.c | 18 +++--- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/01-asm-basics/main.c b/01-asm-basics/main.c index 6abbced..a9c8a66 100644 --- a/01-asm-basics/main.c +++ b/01-asm-basics/main.c @@ -46,12 +46,21 @@ int main(void) { set_input_mode(); while (isKeyPressed() == 0) { - asm { - push ax - in al,0x40 - } - unsigned char Tmm = _AL; - asm pop ax +// asm { +// push ax +// in al,0x40 +// } + + unsigned char Tmm = 0; + asm ( + "push rax\n\t" + "in al, 0x40" + "mov %0, al" + "pop rax" + :"=r"(Tmm) + : + :"rax" + ); delay(500); printf("\n Порт40 = %d", Tmm); } @@ -69,46 +78,58 @@ int main(void) { 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 + // Данная секция закомментирована, поскльку линукс не дает обратиться к + // не промапанной и не аллоцированной памяти. Но даже если ее аллоцировать + // mmem'ом, все равно эта память будет виртуальная, поэтому смысла делать + // это не имеет. Вариант просмотра содержимого условной ячейки памяти на nasm + // приведен в файле time.asm. Объяснить тот код, который я вижу + // на базовом уровне я в состоянии - printf("\n %d", Time); - delay(300); - } - reset_input_mode(); +// int Time; +// set_input_mode(); +// while (isKeyPressed() == 0) { +// Здесь происходит операция получения времени суток при +// помощи обращения к специально размеченой области памяти +// Однако можно ли такой фокус сделать в linux это еще надо узнать +// 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 +// asm( +// "mov " +// ); +// +// 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); - 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); + // здесь секция для выполнения замеров времени. Поскольку доступ к звуку + // Я иметь не могу, если не буду использовать pulseaudio, замерим старыми дедовскими методами + // При помощи clock_gettime } -//void beep(unsigned iTone, unsigned iDlit) { -// sound(iTone); -// delay(iDlit); -// nosound(); -//} diff --git a/01-asm-basics/substitutions.c b/01-asm-basics/substitutions.c index d6cb262..6f137f1 100644 --- a/01-asm-basics/substitutions.c +++ b/01-asm-basics/substitutions.c @@ -3,6 +3,7 @@ #include #include #include +#include /* Use this variable to remember original terminal attributes. */ @@ -32,7 +33,8 @@ void set_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; + tattr.c_cc[VMIN] = 0; + tattr.c_cc[VTIME] = 0; tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr); } @@ -52,10 +54,10 @@ char isKeyPressed() return 0; } -//int main() -//{ -// set_input_mode(); -// while (isKeyPressed() == 0) {} -// printf("ok"); -// reset_input_mode(); -//} +int main() +{ + set_input_mode(); + while (isKeyPressed() == 0) {printf("hell\n");} + printf("ok\n"); + reset_input_mode(); +} -- 2.49.0 From c42d755e80497dc54563b47fc07de28f99d8b5f1 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 13 Sep 2024 00:09:30 +0300 Subject: [PATCH 11/13] =?UTF-8?q?docs(README):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D0=B5=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=20=D0=B2=D0=B8=D1=80=D1=82=D1=83?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B0=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/01-asm-basics/README.md b/01-asm-basics/README.md index 92b703e..a5b025f 100644 --- a/01-asm-basics/README.md +++ b/01-asm-basics/README.md @@ -118,3 +118,5 @@ void set_input_mode() - Компилятору надо понимать, что будет происходить с переменными и регистрами во время ассемблерной вставки, поэтому и это тоже придется указать отдельно Собственно видно, что есть ньансы, которые и заставляют меня сделать выбор в пользу обычного ассемблера и линковки, но раз лаба хочет, чтобы использовался именно inline, то будем использовать inline + +UPD 12.09.24 22:00: в самый последний момент преподаватель решил в своей методички пингануть адрес в памяти, который в ms-dos отведен для хранени данных BIOS, а конкретнее ту часть, которая отведена под системные часы насколько я понимаю. В случае DOS это вполне себе реальная память, которая вполне себе реально существует более того, в досе процессор находится в режиме реальных адресов. Linux в свою очередь относится к приколам с обращением к произвольному участку памяти как к уязвимостям, поэтому не дает просто почитать или пописать в непромапаную память. Но это пол беды на самом-то деле, ведь вся память у любой программы виртуальная и уже на уровне операционной системы и процессора перегонятся в виртуальную, поэтому даже если я воспользуюсь `mmap` и промапаю соответствующий адрес в памяти, в нем будет просто лежать мусор и не более. Поэтому последнюю часть работы, где достается время из памяти BIOS я пропускаю за невозможностью ее выполнить на машине на базе Linux -- 2.49.0 From 55c5096d49e503382a66727e19141e664760ef78 Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Sat, 14 Sep 2024 15:39:28 +0300 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=8C=D1=88=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=B8=D0=B3=D1=80=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20=D1=81=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 01-asm-basics/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/01-asm-basics/README.md b/01-asm-basics/README.md index a5b025f..7541b52 100644 --- a/01-asm-basics/README.md +++ b/01-asm-basics/README.md @@ -100,6 +100,24 @@ void set_input_mode() } ``` +**Разберем некоторые строки подробнее** + +`isatty(STDIN_FILENO)` - в целом `isatty` просто проверяет, является ли дескриптор файла консолью в общем смысле этого слова. Вообще в линуксе `tty` означает teletype - консоли, которые могут использоваться для выполнения команд, восстановления системы и прочего. В некоторых дистрибутивах между ними можно даже переключаться, но так как я на wsl, мне эта роскошь не доступна + +`fprintf` - функция, позволяющая делать "форматированный вывод" в поток дескриптора. То есть это как `prinf`, только еще и дескрипторы принимает + +`EXIT_FAILURE` и `EXIT_SUCCESS` - обозначают 1 и 0 соответственно. Используются чтобы избежать неявной договоренности между пользователями линукс, что при возвращении нуля из функции - это успех, а другого - ошибка + +Очевидным образом если `tcsetattr` устанавливал параметры терминала, то `tcgetattr` должен их получать. В качестве второго параметра принимает указатель на структуру данных, где должен их сохранить. В нашем случае ей выступает упоминавшаяся выше глобальная переменная + +После получения параметров мы начинаем c ними играться в флагах. В данном случае я вырубаю `ICANON` тем самым говоря, что вводить `enter` при вводе команд не обязательно, а также рублю флаг `ECHO`, из-за чего набираемые с клавиатуры символы не отображаются + +`VMIN` говорит о том, что одного символа в буфере достаточно, чтобы считать, что пользователь ввел все, что хотел. Есть еще параметр `VTIME`, который говорит, что если буфер не меняется какое-то время, то пользователь закончил + +Остальное нам вроде бы знакомо =) + + + ## Исправление ассемблерных вставок Должен сказать, что я не большой поклонник "inline assembly". На мой субъективный взгляд намного лучше, читаемее и стабильнее добавлять ассемблер на этапе линковки. Это дает несколько приятных бонусов: @@ -120,3 +138,4 @@ void set_input_mode() Собственно видно, что есть ньансы, которые и заставляют меня сделать выбор в пользу обычного ассемблера и линковки, но раз лаба хочет, чтобы использовался именно inline, то будем использовать inline UPD 12.09.24 22:00: в самый последний момент преподаватель решил в своей методички пингануть адрес в памяти, который в ms-dos отведен для хранени данных BIOS, а конкретнее ту часть, которая отведена под системные часы насколько я понимаю. В случае DOS это вполне себе реальная память, которая вполне себе реально существует более того, в досе процессор находится в режиме реальных адресов. Linux в свою очередь относится к приколам с обращением к произвольному участку памяти как к уязвимостям, поэтому не дает просто почитать или пописать в непромапаную память. Но это пол беды на самом-то деле, ведь вся память у любой программы виртуальная и уже на уровне операционной системы и процессора перегонятся в виртуальную, поэтому даже если я воспользуюсь `mmap` и промапаю соответствующий адрес в памяти, в нем будет просто лежать мусор и не более. Поэтому последнюю часть работы, где достается время из памяти BIOS я пропускаю за невозможностью ее выполнить на машине на базе Linux + -- 2.49.0 From e219503f22aa3cde7aebf2ecbd2ce195f86cb36b Mon Sep 17 00:00:00 2001 From: Miheev Egor Date: Sat, 14 Sep 2024 16:12:58 +0300 Subject: [PATCH 13/13] =?UTF-8?q?docs:=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B0=D1=81=D1=81=D0=B5=D0=BC=D0=B1=D0=BB=D0=B5=D1=80?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D1=87=D0=B0=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавил комментарии в time.asm - Добавил секцию про ассемблер в README --- 01-asm-basics/README.md | 5 ++++ 01-asm-basics/time.asm | 54 ++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/01-asm-basics/README.md b/01-asm-basics/README.md index 7541b52..a8fcceb 100644 --- a/01-asm-basics/README.md +++ b/01-asm-basics/README.md @@ -139,3 +139,8 @@ void set_input_mode() UPD 12.09.24 22:00: в самый последний момент преподаватель решил в своей методички пингануть адрес в памяти, который в ms-dos отведен для хранени данных BIOS, а конкретнее ту часть, которая отведена под системные часы насколько я понимаю. В случае DOS это вполне себе реальная память, которая вполне себе реально существует более того, в досе процессор находится в режиме реальных адресов. Linux в свою очередь относится к приколам с обращением к произвольному участку памяти как к уязвимостям, поэтому не дает просто почитать или пописать в непромапаную память. Но это пол беды на самом-то деле, ведь вся память у любой программы виртуальная и уже на уровне операционной системы и процессора перегонятся в виртуальную, поэтому даже если я воспользуюсь `mmap` и промапаю соответствующий адрес в памяти, в нем будет просто лежать мусор и не более. Поэтому последнюю часть работы, где достается время из памяти BIOS я пропускаю за невозможностью ее выполнить на машине на базе Linux +## Замеры времени ассемблерной команды + +Мне лабораторную работу зачли и без этой части, но если кто-то будет сдавать ему лабу так же как и я, такое возможно не проканает, поэтому для решения задачи замера приложен файлик time.asm (по крайней мере должен быть, если я не забыл). В нем в комментариях я постарался пояснить все этапы замера времени. Но для базового понимания придется сделать некоторые пояснения относительно организационных решений. В методичке преподаватели предпочли обратиться к чтению из промапаной области памяти BIOS, так называемой BIOS data area. Неплохой вариант, если есть прямой доступ к памяти устройства, поскольку BIOS хранит в памяти довольно много полезных данных. Однако из пользовательских программ простучать эту память не получается, потому что linux будет выдавать ошибку даже если такие программы запускать под рутом. + +Однако как же тогда получать время и делать другие манипуляции? Довольно просто на самом деле - системными вызовами. Системный вызов - это программное прерывание, которое просит операционную систему в режиме ядра выполнить какую-то работу: получить время, установить время, поменять разрешения на порты, сменить режимы терминала и очень многое другое вплоть до создания X-server'а. Найти системные вызовы можно прогуглив `linux syscalls table`. От себя порекомендую этот [сайт](https://syscalls.mebeim.net/?table=x86/64/x64/latest). Они вытаскивают системные вызовы из каждой версии ядра. Также возможный, пусть и требующий значительно больше знаний вариант - посмотреть стандартную библиотеку вашего компилятора C. Дело в том, что сам язык C довольно мал и весь его огромный функционал завязан на не таком уж и большом количестве зарезервированных слов и конструкций. В целом сопоставление между C и ассемблером, если компилировать без оптимизаций компилятора (`-O0`), выходит довольно однозначное. И эта же особенность заставляет постоянно переписывать те библиотеки в C, которые зависят от системы или архитектуры. **Не мудрено, что и системные вызовы тоже хранятся где-то в заголовках**. Однако я тут ничего не подскажу, так как так и не понял, где эти номера нормально записаны. Если вы знаете - пишите. diff --git a/01-asm-basics/time.asm b/01-asm-basics/time.asm index 458ce24..f5eae33 100644 --- a/01-asm-basics/time.asm +++ b/01-asm-basics/time.asm @@ -1,10 +1,21 @@ +; Эта директива делает функцию видимой. +; По умолчанию в ассемблере используется _start, +; но поскольку для вывода на экран я пользуюсь +; С'шной функцией prinf, для корректного подключения библиотек на этапе линковки global main +; Объявляю, что буду ссылаться на метку printf, которой нет внутри кода программы +; extern вообще обзначает, что метка объявлена где-то еще extern printf +; тут объявлен макрос CLOCK_REALTIME, который на этапе ассемблирования заменится на число 0 +; Использован он тут, так как является clock_id, о котором будет сказано позже. И я не уверен +; что на всех системах это число будет одинаково. Свое я посмотрел в файлах компилятора. %define CLOCK_REALTIME 0 -; struct timespec { time_t tv_sec; long tv_nsec; } +; так в ассемблере задаются структуры. Существуют они лишь на уровне препроцессора +; да и применение их весьма специфично. Но подробнее лучше погуглите +; struct timespec { time_t tv_sec; long tv_nsec; } - это шаблон из C struc timespec .tv_sec: resq 1 .tv_nsec: resq 1 @@ -12,18 +23,23 @@ endstruc section .note.GNU-stack ; чтобы не жаловался линкер +; Секция с данными, ее особенность в том, что нужно указать лишь сколько нужно зарезервировать section .bss - -start: ; uses timespec model - times 2 resq 1 +; вообще можно было бы использовать istruc и создать эти 2 структуры в .data, но я решил, +; что не хочу тратить время на инициализацию того, что и так будет перезаписано +; обе эти инструкции просто нужны чтобы застолбить по 16 памяти на каждый замер времени +; потому что time_t и long имеют размер 8 байт, а поля 2 +start: + resq 2 finish: - times 2 resq 1 + resq 2 +; Секция с данными, которые заранее заполняются чем-то section .data - fstring db "Operations took %ul seconds and %ul milliseconds", 10, 0 - flen equ $-fstring + fstring db "Operations took %ul seconds and %ul nanoseconds", 10, 0 ; строки стиля C должны оканчиваться нулем + flen equ $-fstring ; длина строки. $ - это текущий адрес. Подробнее не буду рассказывать - мне лень section .text @@ -33,13 +49,15 @@ main: ; лично в моей системе time_t представляет и mov rsi, start syscall - ; insert your code here - mov rcx, 20000 + ; здесь место для кода под замер времени + mov rcx, 20000 ; сколько раз нужно прогнать цикл + ; цикл looper: mov rax, start - loop looper + loop looper ; про это чуть позже узнаете + ; замеряем время второй раз mov rax, 228 mov rdi, CLOCK_REALTIME mov rsi, finish @@ -50,18 +68,30 @@ main: ; лично в моей системе time_t представляет и mov rsi, [finish + timespec.tv_sec] sub rsi, [start + timespec.tv_sec] - ; миллисекунды + ; наносекунды mov rdx, [finish + timespec.tv_nsec] sub rdx, [start + timespec.tv_nsec] + ; вызываем функцию printf. Согласно соглашению о вызовах fastcall + ; при вызове функций для передачи аргументов используются регистры по порядку следования аргументов + ; rdi, rsi, rdx, rcx, r8, r9, а остальные пушатся в ассемблер. + ; Свои заморочки там с числами с плавающей точкой, но об этом не сейчас mov rdi, fstring mov rax, 0 + ; Вот тут все во имя выравнивания стека. Об этом я сейчас рассказывать не буду, только если попросят в readme чиркану sub rsp, 8 + ; собственно вызов функции. На самом деле это обычный jmp, который предварительно пушит в стек адрес возврата. + ; в будущем будьте аккуратнее с этими приколами, потому что при встрече ключевого слова ret ассемблер всегда. + ; подчеркиваю ВСЕГДА прочитает 8 байт со стека и передаст туда управление. И как бы что там будет - одному богу ведомо + ; Так что в ваших же интересах следить за тем, чтобы в стеке лежали правильные байты call printf + ; поскольку выравнивание больше не нужно, возвращаем стек в исходное состояние add rsp, 8 exit: + ; Тут происходит системный вызов выхода из приложения. Если его не увидит + ; linux, то он решит, что программа завершилась аварийно mov rax, 60 - mov rdi, 0 + mov rdi, 0 ; код ошибки. если вернется 0 - считается, что ошибок не произошло syscall -- 2.49.0