Compare commits
43 Commits
a6d997f61d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 92c69f3e57 | |||
| 62be1dc854 | |||
| d21460b8e4 | |||
| 51a0a6a2eb | |||
| d04c04f028 | |||
| 33285bbf1c | |||
| 83a6640b2e | |||
| 7de923d0b6 | |||
| f48bc757ea | |||
| 494686a203 | |||
| 58eb3695f1 | |||
| 2c383b20e9 | |||
| 92f4632803 | |||
| 5ac43bb236 | |||
| 743b8336a3 | |||
| 8dd2aa19ad | |||
| de18bd8252 | |||
| c21df94757 | |||
| b8ebf31762 | |||
| bc5bf92fee | |||
| 908d539abe | |||
| e0ac79bae1 | |||
| 0d3efeda49 | |||
| 6880efafab | |||
| eeb632ed0e | |||
| 1f700295ff | |||
| b11a56ad06 | |||
| 9cdc529918 | |||
|
|
2332be6293 | ||
|
|
1698b43da6 | ||
|
|
17096efe0f | ||
|
|
0716753b1f | ||
|
|
4e111c6148 | ||
|
|
885f62867e | ||
|
|
1f40ef4467 | ||
|
|
184eeb8525 | ||
|
|
6a90ff02c8 | ||
|
|
00273bf803 | ||
|
|
01c7a248ad | ||
|
|
8d019a59d7 | ||
|
|
444a2da5a8 | ||
|
|
6ef051900a | ||
|
|
62bcc7f9cd |
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
**/build/
|
||||
**/build/
|
||||
**/out/
|
||||
|
||||
21
casm/.vscode/launch.json
vendored
@@ -11,6 +11,27 @@
|
||||
"env": {
|
||||
"PATH": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [
|
||||
{
|
||||
"name": "PATH",
|
||||
"value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
|
||||
},
|
||||
],
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,4 +7,4 @@ enable_language(ASM_NASM)
|
||||
set(CMAKE_ASM_NASM_FLAGS "-f elf64")
|
||||
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-gdwarf")
|
||||
|
||||
add_executable(casm asm.asm c.c cpp.cpp)
|
||||
add_executable(casm asm.asm c.c)
|
||||
17
casm/asm.asm
@@ -1,9 +1,10 @@
|
||||
global main
|
||||
extern sum, print, scan, test_vector, a
|
||||
extern sum, print, scan
|
||||
extern a
|
||||
|
||||
section .text
|
||||
main:
|
||||
push rbx ; сохранить rbx
|
||||
push rbx
|
||||
call scan ; прочитать первое число
|
||||
mov rbx, rax ; сохранить первое число
|
||||
call scan ; прочитать второе число
|
||||
@@ -11,12 +12,12 @@ main:
|
||||
mov rsi, rax ; b
|
||||
call sum ; сумма
|
||||
mov rdi, rax ; результат
|
||||
push rax
|
||||
call print ; напечатать
|
||||
call test_vector
|
||||
pop rbx ; восстановить rbx
|
||||
pop rax
|
||||
pop rbx
|
||||
|
||||
mov dword [rel a], 42 ; записать значение 42 в переменную a
|
||||
mov dword [rel a], eax ; записать значение 42 в переменную a
|
||||
|
||||
mov rdi, 0
|
||||
mov rax, 60
|
||||
syscall
|
||||
mov rax, 0
|
||||
ret
|
||||
11
casm/cpp.cpp
@@ -1,11 +0,0 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
extern "C" void test_vector() {
|
||||
std::vector<int> vec = std::vector<int>();
|
||||
for(auto i = 0; i < 10; i++)
|
||||
vec.push_back(i);
|
||||
for(auto num : vec)
|
||||
std::cout << num << ", ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
2
casm/input.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
10
|
||||
20
|
||||
BIN
docs/Intel manual.pdf
Normal file
36
docs/cdecl.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## System V AMD64 ABI (cdecl): шпаргалка
|
||||
|
||||
Кратко о соглашении вызова для x86-64 в Unix-подобных ОС (Linux, macOS, BSD) по ABI System V.
|
||||
|
||||
### Быстрые правила
|
||||
- Аргументы (целые/указатели): RDI, RSI, RDX, RCX, R8, R9; остальные — на стек (справа налево).
|
||||
- Аргументы с плавающей точкой/векторы: XMM0–XMM7; остальные — на стек.
|
||||
- Возврат значения: целые/указатели — RAX (до 128 бит: RAX/RDX); float/double — XMM0 (до двух — XMM0/XMM1).
|
||||
- Перед call: RSP должен быть выровнен по 16 байт. Внутри функции на входе RSP % 16 == 8 (из-за return address).
|
||||
- Red zone: 128 байт под RSP доступны leaf-функциям; не трогается ОС и обработчиками сигналов.
|
||||
- Нет «shadow space» (он есть только в Windows x64 ABI).
|
||||
|
||||
### Сохранность регистров
|
||||
- Caller-saved (clobbered): RAX, RCX, RDX, RSI, RDI, R8–R11, XMM0–XMM15, флаги RFLAGS.
|
||||
- Callee-saved (надо сохранять и восстанавливать в функции): RBX, RSP, RBP, R12–R15.
|
||||
|
||||
### Порядок передачи аргументов
|
||||
#### Для call
|
||||
- 1–6-й целочисленные/указательные: RDI, RSI, RDX, RCX, R8, R9.
|
||||
- Вещественные/векторные: XMM0–XMM7 (независимо от целочисленных).
|
||||
- 7-й и далее, а также переполнение по типам — через стек. Компиляторы обычно размещают их с выравниванием по 8/16.
|
||||
|
||||
### Вариативные функции (printf и т.п.)
|
||||
- Перед вызовом variadic-функции младший байт RAX (AL) содержит число использованных XMM-регистров для передачи аргументов с плавающей точкой (0, если их нет).
|
||||
|
||||
### Структуры и большие значения
|
||||
- Небольшие структуры (до 16 байт) могут возвращаться через регистры (классы INTEGER/SSE распределяются по RAX/RDX или XMM0/XMM1).
|
||||
- Большие структуры возвращаются через скрытый указатель: вызывающий передаёт адрес буфера (sret), а функция заполняет его.
|
||||
|
||||
### Памятка при написании кода
|
||||
- До call: выровняй стек (RSP%16==0), подготовь регистры аргументов, AL для varargs.
|
||||
- После call: результат смотри в RAX/XMM0; считай, что caller-saved испорчены — сохрани важное заранее.
|
||||
- В своей функции сохраняй/восстанавливай RBX, RSP, RBP, R12–R15.
|
||||
|
||||
|
||||
|
||||
BIN
docs/nasmdoc.pdf
Normal file
142
docs/syscall.md
Normal file
@@ -0,0 +1,142 @@
|
||||
## Linux x86-64 syscall: шпаргалка
|
||||
|
||||
Кратко о низкоуровневых системных вызовах в Linux на x86-64 через инструкцию `syscall` (без libc/glibc-обёрток).
|
||||
|
||||
### Быстрые правила
|
||||
- Номер системного вызова: в RAX.
|
||||
- Аргументы (целые/указатели): RDI, RSI, RDX, R10, R8, R9
|
||||
- Возврат: RAX (неотрицательно при успехе; при ошибке — отрицательное значение `-errno` в диапазоне [-4095..-1]).
|
||||
- Порченные регистры инструкцией `syscall`: RCX, R11 (всегда). RAX меняется на код возврата.
|
||||
- Остальные GPR обычно возвращаются неизменёнными ядром, но в пользовательском коде всё равно соблюдай ABI: после syscalls/функций не полагайся на caller-saved (RAX, RCX, RDX, RSI, RDI, R8–R11).
|
||||
- Выравнивание стека: сама инструкция `syscall` не требует 16-байтового выравнивания RSP, но если ты в функции, которая также вызывает обычные функции по ABI System V, держи RSP кратным 16 перед `call`.
|
||||
- Red zone (128 байт под RSP) по ABI доступна и не трогается ядром/обработчиками сигналов — её можно использовать в leaf-коде. Если есть сомнения/нестандартное окружение — не полагайся на неё.
|
||||
|
||||
### Возврат и обработка ошибок
|
||||
- При успехе RAX содержит неотрицательный результат (часто количество байт, файловый дескриптор и т.п.).
|
||||
- При ошибке RAX содержит отрицательное число `-errno` (в диапазоне [-4095..-1]). Никаких флагов (CF/ZF) для ошибки не используется — проверяй знак RAX.
|
||||
- Обёртки libc (например, `write(2)`) конвертируют `-errno` в `-1` и устанавливают `errno`. В чистом `syscall` этого нет — обрабатывай сам.
|
||||
|
||||
### Где взять номера системных вызовов
|
||||
- Заголовки ядра: `/usr/include/x86_64-linux-gnu/asm/unistd_64.h` (или `/usr/include/asm/unistd_64.h` в зависимости от дистрибутива).
|
||||
- `man 2 syscall`, `man 2 <имя>` (например, `man 2 write`). Часто используется `openat` вместо устаревающего `open`.
|
||||
|
||||
### Примеры (NASM)
|
||||
|
||||
#### write(1, msg, len)
|
||||
```nasm
|
||||
; ssize_t write(int fd, const void *buf, size_t count)
|
||||
; x86-64: rax=1 (SYS_write), rdi=fd, rsi=buf, rdx=count
|
||||
mov rax, 1 ; SYS_write
|
||||
mov rdi, 1 ; fd = stdout
|
||||
lea rsi, [rel msg] ; buf
|
||||
mov rdx, msg_end - msg ; count
|
||||
syscall
|
||||
; при успехе: rax = число записанных байт (>=0)
|
||||
; при ошибке: rax < 0 ( = -errno )
|
||||
|
||||
msg: db "Hello, syscall!\n", 0x0A
|
||||
msg_end:
|
||||
```
|
||||
|
||||
#### exit(status)
|
||||
```nasm
|
||||
; void _exit(int status) — завершает текущий поток/процесс (exit_group завершит все потоки процесса)
|
||||
mov rax, 60 ; SYS_exit
|
||||
xor rdi, rdi ; status = 0
|
||||
syscall ; не возвращается
|
||||
```
|
||||
|
||||
#### openat(AT_FDCWD, path, flags, mode)
|
||||
```nasm
|
||||
; long openat(int dirfd, const char *pathname, int flags, mode_t mode)
|
||||
; x86-64: rax=257 (SYS_openat), rdi=dirfd, rsi=pathname, rdx=flags, r10=mode
|
||||
mov rax, 257 ; SYS_openat
|
||||
mov rdi, -100 ; AT_FDCWD
|
||||
lea rsi, [rel path]
|
||||
mov rdx, 0x0002 ; O_RDWR (пример)
|
||||
mov r10, 0o644 ; mode
|
||||
syscall
|
||||
; rax >= 0 => fd, rax < 0 => -errno
|
||||
|
||||
path: db "./file.txt", 0
|
||||
```
|
||||
|
||||
#### mmap(addr, length, prot, flags, fd, offset) — пример с 6 аргументами
|
||||
```nasm
|
||||
; void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
; x86-64: rax=9, rdi=addr, rsi=length, rdx=prot, r10=flags, r8=fd, r9=offset
|
||||
mov rax, 9 ; SYS_mmap
|
||||
xor rdi, rdi ; addr = NULL (hint)
|
||||
mov rsi, 4096 ; length
|
||||
mov rdx, 3 ; PROT_READ|PROT_WRITE
|
||||
mov r10, 0x22 ; MAP_PRIVATE|MAP_ANONYMOUS (пример)
|
||||
mov r8, -1 ; fd = -1 (анонимно)
|
||||
xor r9, r9 ; offset = 0
|
||||
syscall
|
||||
; при успехе: rax = адрес (неотрицательный с точки зрения знака)
|
||||
; при ошибке: rax < 0 => -errno
|
||||
```
|
||||
|
||||
### Часто используемые номера (x86-64)
|
||||
- 0 — read
|
||||
- 1 — write
|
||||
- 2 — open (лучше использовать 257 — openat)
|
||||
- 3 — close
|
||||
- 9 — mmap
|
||||
- 11 — munmap
|
||||
- 12 — brk
|
||||
- 60 — exit
|
||||
- 61 — wait4
|
||||
- 62 — kill
|
||||
- 63 — uname
|
||||
- 78 — gettimeofday (устар.; лучше clock_gettime: 228)
|
||||
- 202 — futex
|
||||
- 231 — exit_group (завершает все потоки процесса)
|
||||
- 257 — openat
|
||||
|
||||
Подробный список — в заголовках ядра и `man 2`.
|
||||
|
||||
### Замечания и подводные камни
|
||||
- Не путай ABI функций и ABI `syscall`. У `syscall` 4-й аргумент — R10 (не RCX), и `syscall` портит RCX/R11.
|
||||
- Не используй `int 0x80` на x86-64: это 32-битный интерфейс, он не совместим и может вести к неверной работе.
|
||||
- Проверка ошибок только по знаку RAX, флаги процессора неинформативны.
|
||||
- Если смешиваешь `syscall` и обычные вызовы функций, следи за выравниванием стека (RSP%16==0 перед `call`).
|
||||
- В многопоточных программах предпочитай `exit_group(231)` для завершения целого процесса; `exit(60)` завершает только текущий поток.
|
||||
|
||||
### Мини-макрос для NASM (опционально)
|
||||
```nasm
|
||||
; Использование: SYS SYS_write, fd, buf, len
|
||||
%define SYS_read 0
|
||||
%define SYS_write 1
|
||||
%define SYS_openat 257
|
||||
%define SYS_exit 60
|
||||
|
||||
%macro SYS 1-7
|
||||
mov rax, %1
|
||||
%if %0 > 1
|
||||
mov rdi, %2
|
||||
%endif
|
||||
%if %0 > 2
|
||||
mov rsi, %3
|
||||
%endif
|
||||
%if %0 > 3
|
||||
mov rdx, %4
|
||||
%endif
|
||||
%if %0 > 4
|
||||
mov r10, %5
|
||||
%endif
|
||||
%if %0 > 5
|
||||
mov r8, %6
|
||||
%endif
|
||||
%if %0 > 6
|
||||
mov r9, %7
|
||||
%endif
|
||||
syscall
|
||||
%endmacro
|
||||
```
|
||||
|
||||
### Памятка при написании кода
|
||||
- Положи номер вызова в RAX, аргументы — в RDI, RSI, RDX, R10, R8, R9.
|
||||
- Вызови `syscall`; помни, что RCX и R11 будут испорчены.
|
||||
- Проверяй RAX: `rax < 0` — ошибка (`-errno`), `rax >= 0` — успех.
|
||||
- Соблюдай сохранность регистров по ABI System V для своего кода (RBX/RBP/R12–R15 — сохраняй, если меняешь).
|
||||
568
docs/x86-64_asm_sheet.ru.md
Normal file
@@ -0,0 +1,568 @@
|
||||
# Шпаргалка по x86‑64 ASM
|
||||
|
||||
## Адресация
|
||||
|
||||
* Без сегментации (кроме `fs` и `gs` для специальных целей, например, потоков)
|
||||
|
||||
* Относительно базового регистра
|
||||
* используется для **данных в стеке**, **массивов**, **структур** и **членов классов**
|
||||
* [`base` + `index` * `scale` + `immediate_offset`]
|
||||
* `base` обязателен, может быть любым 64‑битным регистром
|
||||
* `index` может быть любым 64‑битным регистром, кроме `rsp`
|
||||
* `scale` может быть 1, 2, 4 или 8
|
||||
* `immediate_offset` (в GAS называется displacement) — смещение относительно базового регистра
|
||||
* Синтаксис GAS: `immediate_offset(base, index, scale)`
|
||||
|
||||
* Относительно RIP (также PC‑relative)
|
||||
* используется для **статических данных**
|
||||
* содержит 32‑битное знаково расширяемое смещение относительно указателя команд
|
||||
* явно задаётся в NASM через `mov eax [rel label]` или директивы `default rel` / `default abs` (в противном случае используется 32‑битная абсолютная адресация)
|
||||
* явно задаётся в GAS через `mov eax label(%rip)`
|
||||
|
||||
* 32‑битная абсолютная адресация
|
||||
* 32‑битный константный адрес, знаково расширяемый до 64 бит
|
||||
* работает для адресов ниже 2^31
|
||||
* не используйте для простых операндов памяти: RIP‑relative короче, быстрее (не нужны релокации) и работает везде
|
||||
* используется для доступа к **статическим массивам с индексным регистром**, например `mov ebx, [intarray + rsi*4]`, однако это не работает для DLL в Windows и Linux, а также для исполняемых файлов и DLL в macOS, потому что адреса выше 2^32 (gcc и clang используют это для Linux‑исполняемых файлов; для Windows‑исполняемых MASM использует адресацию относительно базы образа)
|
||||
* альтернативный, работающий везде вариант: сначала загрузить адрес статического массива в `rbx` через `lea` с RIP‑relative адресом, а затем адресовать относительно этого базового регистра (`lea rbx, [array]`, затем `mov eax, [rbx + rcx*4]`); другие статические массивы затем можно адресовать относительно (`mov [(array2-array1) + rbx + rcx*4], eax`)
|
||||
|
||||
* 64‑битная абсолютная адресация
|
||||
* `mov eax, dword [qword a]`
|
||||
* применима только с `mov` и регистрами `al`, `ax`, `eax` или `rax` (источник или приёмник)
|
||||
* не может содержать сегмент, базовый или индексный регистр
|
||||
|
||||
## Позиционно‑независимый код (PIC)
|
||||
|
||||
* Проще и быстрее, чем 32‑битная техника GOT (Global Offset Table), поскольку RIP‑relative уже позиционно‑независим (заметьте, что описанная выше техника доступа к статическим массивам с индексным регистром также позиционно‑независима)
|
||||
|
||||
## Регистры общего назначения
|
||||
|
||||
bit 0 - 63 | bit 0 - 31 | bit 0 - 15 | bit 8 - 15 | bit 0 - 7
|
||||
:----------:|:----------:|:----------:|:----------:|:---------:
|
||||
`rax` | `eax` | `ax` | `ah` | `al`
|
||||
`rbx` | `ebx` | `bx` | `bh` | `bl`
|
||||
`rcx` | `ecx` | `cx` | `ch` | `cl`
|
||||
`rdx` | `edx` | `dx` | `dh` | `dl`
|
||||
`rsi` | `esi` | `si` | | `sil`
|
||||
`rdi` | `edi` | `di` | | `dil`
|
||||
`rbp` | `ebp` | `bp` | | `bpl`
|
||||
`rsp` | `esp` | `sp` | | `spl`
|
||||
`r8` | `r8d` | `r8w` | | `r8b`
|
||||
`r9` | `r9d` | `r9w` | | `r9b`
|
||||
`r10` | `r10d` | `r10w` | | `r10b`
|
||||
`r11` | `r11d` | `r11w` | | `r11b`
|
||||
`r12` | `r12d` | `r12w` | | `r12b`
|
||||
`r13` | `r13d` | `r13w` | | `r13b`
|
||||
`r14` | `r14d` | `r14w` | | `r14b`
|
||||
`r15` | `r15d` | `r15w` | | `r15b`
|
||||
`rflags` | | `flags` | |
|
||||
`rip` | | | |
|
||||
|
||||
## Регистр `rflags`
|
||||
|
||||
* CF (Carry Flag, бит 0) — устанавливается, если арифметическая операция генерирует перенос или заём из старшего бита результата; очищается иначе. Флаг показывает переполнение для беззнаковой арифметики. Также применяется в многоточечной (многословной) арифметике.
|
||||
* PF (Parity Flag, бит 2) — устанавливается, если младший байт результата содержит чётное число единиц; очищается иначе.
|
||||
* AF (Auxiliary carry Flag, бит 4) — устанавливается, если операция вызывает перенос или заём из бита 3 результата; очищается иначе. Используется в двоично‑десятичной (BCD) арифметике.
|
||||
* ZF (Zero Flag, бит 6) — устанавливается, если результат равен нулю; очищается иначе.
|
||||
* SF (Sign Flag, бит 7) — устанавливается равным старшему биту результата, то есть знаковому биту знакового целого (0 — положительное, 1 — отрицательное).
|
||||
* OF (Overflow Flag, бит 11) — устанавливается, если целочисленный результат слишком велик (без учёта знакового бита), чтобы поместиться в операнд назначения: слишком большой положительный или слишком маленький отрицательный; очищается иначе. Показывает переполнение для знаковой (дополнительный код) арифметики.
|
||||
|
||||
## Режимы насыщения и циклического переполнения (набора инструкций)
|
||||
|
||||
* Циклическая арифметика (wraparound) — истинный выход за диапазон усечётся (перенос/переполнение игнорируется, возвращаются только младшие биты результата). Подходит, когда диапазон операндов контролируется. Иначе возможны большие ошибки. Пример: сложение двух больших знаковых чисел может вызвать положительное переполнение и дать отрицательный результат.
|
||||
* Арифметика со знаковым насыщением — выход за диапазон ограничивается представимым диапазоном знаковых целых для данного размера. Например, при положительном переполнении для знаковых слов результат насыщается до 7FFFH (наибольшее положительное 16‑битное); при отрицательном — до 8000H.
|
||||
* Арифметика с беззнаковым насыщением — выход за диапазон ограничивается представимым диапазоном беззнаковых целых данного размера. Положительное переполнение для беззнаковых байтов даёт FFH, отрицательное — 00H.
|
||||
|
||||
## Стековые кадры
|
||||
|
||||
## Инструкции передачи данных
|
||||
|
||||
* [**MOV**](http://www.felixcloutier.com/x86/MOV.html) — Перемещение данных между регистрами общего назначения; между памятью и регистрами общего назначения или сегментными; загрузка непосредственных значений в регистры общего назначения.
|
||||
* [**CMOVcc**](http://www.felixcloutier.com/x86/CMOVcc.html) — Условное перемещение.
|
||||
* [**XCHG**](http://www.felixcloutier.com/x86/XCHG.html) — Обмен значениями.
|
||||
* [**BSWAP**](http://www.felixcloutier.com/x86/BSWAP.html) — Перестановка байтов.
|
||||
* [**XADD**](http://www.felixcloutier.com/x86/XADD.html) — Обмен с добавлением.
|
||||
* [**CMPXCHG**](http://www.felixcloutier.com/x86/CMPXCHG.html) — Сравнить и обменять.
|
||||
* [**CMPXCHG8B / CMPXCHG16B**](http://www.felixcloutier.com/x86/CMPXCHG8B:CMPXCHG16B.html) — Сравнить и обменять 8/16 байт.
|
||||
* [**PUSH**](http://www.felixcloutier.com/x86/PUSH.html) — Поместить на стек.
|
||||
* [**POP**](http://www.felixcloutier.com/x86/POP.html) — Снять со стека.
|
||||
* [**PUSHA / PUSHAD**](http://www.felixcloutier.com/x86/PUSHA:PUSHAD.html) — Поместить все регистры общего назначения на стек.
|
||||
* [**POPA / POPAD**](http://www.felixcloutier.com/x86/POPA:POPAD.html) — Снять все регистры общего назначения со стека.
|
||||
* [**CWD / CDQ / CQO**](http://www.felixcloutier.com/x86/CWD:CDQ:CQO.html) — Расширить слово до двойного слова / двойное слово до квадрослова.
|
||||
* [**CBW / CWDE / CDQE**](http://www.felixcloutier.com/x86/CBW:CWDE:CDQE.html) — Расширить байт до слова / слово до двойного слова в регистре `rax`.
|
||||
* [**MOVSX / MOVSXD**](http://www.felixcloutier.com/x86/MOVSX:MOVSXD.html) — Перемещение с знаковым расширением.
|
||||
* [**MOVZX**](http://www.felixcloutier.com/x86/MOVZX.html) — Перемещение с нулевым расширением.
|
||||
|
||||
## Инструкции целочисленной арифметики
|
||||
|
||||
* [**ADCX**](http://www.felixcloutier.com/x86/ADCX.html) — Беззнаковое сложение с переносом.
|
||||
* [**ADOX**](http://www.felixcloutier.com/x86/ADOX.html) — Беззнаковое сложение с переполнением.
|
||||
* [**ADD**](http://www.felixcloutier.com/x86/ADD.html) — Сложение целых.
|
||||
* [**ADC**](http://www.felixcloutier.com/x86/ADC.html) — Сложение с переносом.
|
||||
* [**SUB**](http://www.felixcloutier.com/x86/SUB.html) — Вычитание.
|
||||
* [**SBB**](http://www.felixcloutier.com/x86/SBB.html) — Вычитание с заёмом.
|
||||
* [**IMUL**](http://www.felixcloutier.com/x86/IMUL.html) — Умножение со знаком.
|
||||
* [**MUL**](http://www.felixcloutier.com/x86/MUL.html) — Умножение без знака.
|
||||
* [**IDIV**](http://www.felixcloutier.com/x86/IDIV.html) — Деление со знаком.
|
||||
* [**DIV**](http://www.felixcloutier.com/x86/DIV.html) — Деление без знака.
|
||||
* [**INC**](http://www.felixcloutier.com/x86/INC.html) — Инкремент.
|
||||
* [**DEC**](http://www.felixcloutier.com/x86/DEC.html) — Декремент.
|
||||
* [**NEG**](http://www.felixcloutier.com/x86/NEG.html) — Изменить знак (двойка‑дополнение).
|
||||
* [**CMP**](http://www.felixcloutier.com/x86/CMP.html) — Сравнение.
|
||||
|
||||
## Логические инструкции
|
||||
|
||||
* [**AND**](http://www.felixcloutier.com/x86/AND.html) — Побитовое И.
|
||||
* [**OR**](http://www.felixcloutier.com/x86/OR.html) — Побитовое ИЛИ.
|
||||
* [**XOR**](http://www.felixcloutier.com/x86/XOR.html) — Побитовое исключающее ИЛИ.
|
||||
* [**NOT**](http://www.felixcloutier.com/x86/NOT.html) — Побитовое НЕ.
|
||||
|
||||
## Сдвиги и ротации
|
||||
|
||||
* [**SAL / SAR / SHL / SHR**](http://www.felixcloutier.com/x86/SAL:SAR:SHL:SHR.html) — Арифметический/логический сдвиг влево/вправо.
|
||||
* [**SHLD**](http://www.felixcloutier.com/x86/SHLD.html) — Двойной сдвиг влево.
|
||||
* [**SHRD**](http://www.felixcloutier.com/x86/SHRD.html) — Двойной сдвиг вправо.
|
||||
* [**RCL / RCR / ROL / ROR**](http://www.felixcloutier.com/x86/RCL:RCR:ROL:ROR.html) — Ротация влево/вправо и через перенос.
|
||||
|
||||
## Битовые и байтовые инструкции
|
||||
|
||||
* [**BT**](http://www.felixcloutier.com/x86/BT.html) — Тест бита.
|
||||
* [**BTS**](http://www.felixcloutier.com/x86/BTS.html) — Тест и установка бита.
|
||||
* [**BTR**](http://www.felixcloutier.com/x86/BTR.html) — Тест и сброс бита.
|
||||
* [**BTC**](http://www.felixcloutier.com/x86/BTC.html) — Тест и инверсия бита.
|
||||
* [**BSF**](http://www.felixcloutier.com/x86/BSF.html) — Поиск первого (младшего) установленного бита.
|
||||
* [**BSR**](http://www.felixcloutier.com/x86/BSR.html) — Поиск последнего (старшего) установленного бита.
|
||||
* [**SETcc**](http://www.felixcloutier.com/x86/SETcc.html) — Установить байт по условию.
|
||||
* [**TEST**](http://www.felixcloutier.com/x86/TEST.html) — Логическое сравнение (AND, флаги).
|
||||
* [**CRC32**](http://www.felixcloutier.com/x86/CRC32.html) — Аппаратное ускорение вычисления CRC для быстрого контроля целостности данных.
|
||||
* [**POPCNT**](http://www.felixcloutier.com/x86/POPCNT.html) — Подсчёт количества единичных битов во втором операнде с возвратом счётчика в первый (регистр назначения).
|
||||
|
||||
## Инструкции передачи управления
|
||||
|
||||
* [**JMP**](http://www.felixcloutier.com/x86/JMP.html) — Безусловный переход.
|
||||
* [**Jcc**](http://www.felixcloutier.com/x86/Jcc.html) — Переход при выполнении условия (операнд RIP‑relative).
|
||||
* [**LOOP / LOOPcc**](http://www.felixcloutier.com/x86/LOOP:LOOPcc.html) — Цикл с счётчиком в `rcx`.
|
||||
* [**CALL**](http://www.felixcloutier.com/x86/CALL.html) — Вызов процедуры.
|
||||
* [**RET**](http://www.felixcloutier.com/x86/RET.html) — Возврат из процедуры.
|
||||
* [**IRET / IRETD / IRETQ**](http://www.felixcloutier.com/x86/IRET:IRETD.html) — Возврат из прерывания.
|
||||
* [**INT n / INTO / INT 3**](http://www.felixcloutier.com/x86/INTn:INTO:INT3.html) — Вызов обработчика прерывания.
|
||||
* [**ENTER**](http://www.felixcloutier.com/x86/ENTER.html) — Высокоуровневый вход в процедуру.
|
||||
* [**LEAVE**](http://www.felixcloutier.com/x86/LEAVE.html) — Высокоуровневый выход из процедуры.
|
||||
|
||||
## Строковые инструкции
|
||||
|
||||
* [**MOVS / MOVSB / MOVSW / MOVSD / MOVSQ**](http://www.felixcloutier.com/x86/MOVS:MOVSB:MOVSW:MOVSD:MOVSQ.html) — Копирование данных из строки в строку.
|
||||
* [**CMPS / CMPSB / CMPSW / CMPSD / CMPSQ**](http://www.felixcloutier.com/x86/CMPS:CMPSB:CMPSW:CMPSD:CMPSQ.html) — Сравнение строковых операндов.
|
||||
* [**SCAS / SCASB / SCASW / SCASD**](http://www.felixcloutier.com/x86/SCAS:SCASB:SCASW:SCASD.html) — Сканирование строки.
|
||||
* [**LODS / LODSB / LODSW / LODSD / LODSQ**](http://www.felixcloutier.com/x86/LODS:LODSB:LODSW:LODSD:LODSQ.html) — Загрузка из строки.
|
||||
* [**STOS / STOSB / STOSW / STOSD / STOSQ**](http://www.felixcloutier.com/x86/STOS:STOSB:STOSW:STOSD:STOSQ.html) — Запись в строку.
|
||||
* [**REP / REPE / REPZ / REPNE / REPNZ**](http://www.felixcloutier.com/x86/REP:REPE:REPZ:REPNE:REPNZ.html) — Префиксы повторения строковых операций.
|
||||
|
||||
## Инструкции управления `rflags`
|
||||
|
||||
* [**STC**](http://www.felixcloutier.com/x86/STC.html) — Установить флаг переноса.
|
||||
* [**CLC**](http://www.felixcloutier.com/x86/CLC.html) — Очистить флаг переноса.
|
||||
* [**CMC**](http://www.felixcloutier.com/x86/CMC.html) — Инвертировать флаг переноса.
|
||||
* [**CLD**](http://www.felixcloutier.com/x86/CLD.html) — Очистить флаг направления.
|
||||
* [**STD**](http://www.felixcloutier.com/x86/STD.html) — Установить флаг направления.
|
||||
* [**LAHF**](http://www.felixcloutier.com/x86/LAHF.html) — Загрузить флаги в регистр `ah`.
|
||||
* [**SAHF**](http://www.felixcloutier.com/x86/SAHF.html) — Сохранить регистр `ah` в флаги.
|
||||
* [**PUSHF / PUSHFQ**](http://www.felixcloutier.com/x86/PUSHF:PUSHFD:PUSHFQ.html) — Поместить `rflags` на стек.
|
||||
* [**POPF / POPFQ**](http://www.felixcloutier.com/x86/POPF:POPFD:POPFQ.html) — Снять `rflags` со стека.
|
||||
* [**STI**](http://www.felixcloutier.com/x86/STI.html) — Установить флаг прерываний.
|
||||
* [**CLI**](http://www.felixcloutier.com/x86/CLI.html) — Очистить флаг прерываний.
|
||||
|
||||
## Прочие инструкции
|
||||
|
||||
* [**LEA**](http://www.felixcloutier.com/x86/LEA.html) — Загрузка эффективного адреса.
|
||||
* [**NOP**](http://www.felixcloutier.com/x86/NOP.html) — Пустая операция.
|
||||
* [**UD**](http://www.felixcloutier.com/x86/UD.html) — Неопределённая инструкция.
|
||||
* [**XLAT / XLATB**](http://www.felixcloutier.com/x86/XLAT:XLATB.html) — Табличное преобразование байта.
|
||||
* [**CPUID**](http://www.felixcloutier.com/x86/CPUID.html) — Идентификация процессора.
|
||||
* [**MOVBE**](http://www.felixcloutier.com/x86/MOVBE.html) — Перемещение данных с перестановкой байтов.
|
||||
* [**PREFETCHW**](http://www.felixcloutier.com/x86/PREFETCHW.html) — Предвыборка данных в кэш с ожиданием записи.
|
||||
* [**CLFLUSH**](http://www.felixcloutier.com/x86/CLFLUSH.html) — Сброс и инвалидация операнда памяти и связанной линии кэша на всех уровнях иерархии кэшей процессора.
|
||||
* [**CLFLUSHOPT**](http://www.felixcloutier.com/x86/CLFLUSHOPT.html) — То же, с оптимизацией пропускной способности памяти.
|
||||
* [**RDRAND**](http://www.felixcloutier.com/x86/RDRAND.html) — Получение случайного числа, сгенерированного аппаратно.
|
||||
* [**RDSEED**](http://www.felixcloutier.com/x86/RDSEED.html) — Получение зерна (seed) для ГСЧ из аппаратного источника.
|
||||
|
||||
## Сохранение/восстановление расширенных состояний в пользовательском режиме
|
||||
|
||||
* [**XSAVE**](http://www.felixcloutier.com/x86/XSAVE.html) — Сохранить расширенные состояния процессора в память.
|
||||
* [**XSAVEC**](http://www.felixcloutier.com/x86/XSAVEC.html) — Сохранить расширенные состояния с уплотнением.
|
||||
* [**XSAVEOPT**](http://www.felixcloutier.com/x86/XSAVEOPT.html) — Оптимизированное сохранение расширенных состояний.
|
||||
* [**XRSTOR**](http://www.felixcloutier.com/x86/XRSTOR.html) — Восстановить расширенные состояния из памяти.
|
||||
* [**XGETBV**](http://www.felixcloutier.com/x86/XGETBV.html) — Прочитать состояние расширенного управляющего регистра.
|
||||
|
||||
## Манипуляции битами (BMI1, BMI2)
|
||||
|
||||
* [**ANDN**](http://www.felixcloutier.com/x86/ANDN.html) — Побитовое AND первого операнда с инвертированным вторым.
|
||||
* [**BEXTR**](http://www.felixcloutier.com/x86/BEXTR.html) — Извлечение непрерывного диапазона битов.
|
||||
* [**BLSI**](http://www.felixcloutier.com/x86/BLSI.html) — Извлечь младший установленный бит.
|
||||
* [**BLSMSK**](http://www.felixcloutier.com/x86/BLSMSK.html) — Установить в 1 все младшие биты ниже первого установленного.
|
||||
* [**BLSR**](http://www.felixcloutier.com/x86/BLSR.html) — Сбросить младший установленный бит.
|
||||
* [**BZHI**](http://www.felixcloutier.com/x86/BZHI.html) — Обнулить старшие биты, начиная с указанной позиции.
|
||||
* [**LZCNT**](http://www.felixcloutier.com/x86/LZCNT.html) — Подсчёт ведущих нулей.
|
||||
* [**MULX**](http://www.felixcloutier.com/x86/MULX.html) — Беззнаковое умножение без изменения флагов.
|
||||
* [**PDEP**](http://www.felixcloutier.com/x86/PDEP.html) — Параллельная «загрузка» битов по маске.
|
||||
* [**PEXT**](http://www.felixcloutier.com/x86/PEXT.html) — Параллельное «извлечение» битов по маске.
|
||||
* [**RORX**](http://www.felixcloutier.com/x86/RORX.html) — Ротация вправо без изменения флагов.
|
||||
* [**SARX / SHLX / SHRX**](http://www.felixcloutier.com/x86/SARX:SHLX:SHRX.html) — Арифметический/логический сдвиг без изменения флагов.
|
||||
* [**TZCNT**](http://www.felixcloutier.com/x86/TZCNT.html) — Подсчёт замыкающих нулей (с конца).
|
||||
|
||||
## Обзор x87 FPU
|
||||
|
||||
* Состояние x87 FPU отображается на состояние MMX; при переходах к MMX‑инструкциям нужно соблюдать осторожность, чтобы избежать неконсистентности результатов.
|
||||
|
||||
## Инструкции передачи данных x87 FPU
|
||||
|
||||
* [**FLD**](http://www.felixcloutier.com/x86/FLD.html) — Загрузка числа с плавающей точкой.
|
||||
* [**FST / FSTP**](http://www.felixcloutier.com/x86/FST:FSTP.html) — Сохранить число с/без извлечения из стека FPU.
|
||||
* [**FILD**](http://www.felixcloutier.com/x86/FILD.html) — Загрузка целого.
|
||||
* [**FIST / FISTP**](http://www.felixcloutier.com/x86/FIST:FISTP.html) — Сохранить целое с/без извлечения.
|
||||
* [**FBLD**](http://www.felixcloutier.com/x86/FBLD.html) — Загрузка BCD.
|
||||
* [**FBSTP**](http://www.felixcloutier.com/x86/FBSTP.html) — Сохранение BCD и извлечение.
|
||||
* [**FXCH**](http://www.felixcloutier.com/x86/FXCH.html) — Обмен регистров.
|
||||
* [**FCMOVcc**](http://www.felixcloutier.com/x86/FCMOVcc.html) — Условное перемещение (FPU).
|
||||
|
||||
## Базовая арифметика x87 FPU
|
||||
|
||||
* [**FADD / FADDP / FIADD**](http://www.felixcloutier.com/x86/FADD:FADDP:FIADD.html) — Сложение с плавающей точкой.
|
||||
* [**FSUB / FSUBP / FISUB**](http://www.felixcloutier.com/x86/FSUB:FSUBP:FISUB.html) — Вычитание с плавающей точкой.
|
||||
* [**FSUBR / FSUBRP / FISUBR**](http://www.felixcloutier.com/x86/FSUBR:FSUBRP:FISUBR.html) — Вычитание в обратном порядке.
|
||||
* [**FMUL / FMULP / FIMUL**](http://www.felixcloutier.com/x86/FMUL:FMULP:FIMUL.html) — Умножение с плавающей точкой.
|
||||
* [**FDIV / FDIVP / FIDIV**](http://www.felixcloutier.com/x86/FDIV:FDIVP:FIDIV.html) — Деление с плавающей точкой.
|
||||
* [**FDIVR / FDIVRP / FIDIVR**](http://www.felixcloutier.com/x86/FDIVR:FDIVRP:FIDIVR.html) — Деление в обратном порядке.
|
||||
* [**FPREM**](http://www.felixcloutier.com/x86/FPREM.html) — Частный остаток.
|
||||
* [**FPREM1**](http://www.felixcloutier.com/x86/FPREM1.html) — Частный остаток (IEEE).
|
||||
* [**FABS**](http://www.felixcloutier.com/x86/FABS.html) — Модуль.
|
||||
* [**FCHS**](http://www.felixcloutier.com/x86/FCHS.html) — Смена знака.
|
||||
* [**FRNDINT**](http://www.felixcloutier.com/x86/FRNDINT.html) — Округление до целого.
|
||||
* [**FSCALE**](http://www.felixcloutier.com/x86/FSCALE.html) — Масштабирование по степени двойки.
|
||||
* [**FSQRT**](http://www.felixcloutier.com/x86/FSQRT.html) — Квадратный корень.
|
||||
* [**FXTRACT**](http://www.felixcloutier.com/x86/FXTRACT.html) — Извлечение показателя и мантиссы.
|
||||
|
||||
## Сравнения x87 FPU
|
||||
|
||||
* [**FCOM / FCOMP / FCOMPP**](http://www.felixcloutier.com/x86/FCOM:FCOMP:FCOMPP.html) — Сравнение чисел с плавающей точкой.
|
||||
* [**FUCOM / FUCOMP / FUCOMPP**](http://www.felixcloutier.com/x86/FUCOM:FUCOMP:FUCOMPP.html) — Неупорядоченное сравнение чисел с плавающей точкой.
|
||||
* [**FICOM / FICOMP**](http://www.felixcloutier.com/x86/FICOM:FICOMP.html) — Сравнение целых.
|
||||
* [**FCOMI / FCOMIP / FUCOMI / FUCOMIP**](http://www.felixcloutier.com/x86/FCOMI:FCOMIP:FUCOMI:FUCOMIP.html) — Сравнение чисел с плавающей точкой с установкой флагов `rflags`.
|
||||
* [**FTST**](http://www.felixcloutier.com/x86/FTST.html) — Тест (сравнение с 0.0).
|
||||
* [**FXAM**](http://www.felixcloutier.com/x86/FXAM.html) — Анализ (exam) числа с плавающей точкой.
|
||||
|
||||
## Трансцендентные функции x87 FPU
|
||||
|
||||
* [**FSIN**](http://www.felixcloutier.com/x86/FSIN.html) — Синус.
|
||||
* [**FCOS**](http://www.felixcloutier.com/x86/FCOS.html) — Косинус.
|
||||
* [**FSINCOS**](http://www.felixcloutier.com/x86/FSINCOS.html) — Синус и косинус.
|
||||
* [**FPTAN**](http://www.felixcloutier.com/x86/FPTAN.html) — Частичная тангенс‑функция.
|
||||
* [**FPATAN**](http://www.felixcloutier.com/x86/FPATAN.html) — Частичная арктангенс‑функция.
|
||||
* [**F2XM1**](http://www.felixcloutier.com/x86/F2XM1.html) — 2^x − 1.
|
||||
* [**FYL2X**](http://www.felixcloutier.com/x86/FYL2X.html) — y ∗ log2(x).
|
||||
* [**FYL2XP1**](http://www.felixcloutier.com/x86/FYL2XP1.html) — y ∗ log2(x + 1).
|
||||
|
||||
## Загрузка констант x87 FPU
|
||||
|
||||
* [**FLD1 / FLDL2T / FLDL2E / FLDPI / FLDLG2 / FLDLN2 / FLDZ**](http://www.felixcloutier.com/x86/FLD1:FLDL2T:FLDL2E:FLDPI:FLDLG2:FLDLN2:FLDZ.html) — Загрузка констант.
|
||||
|
||||
## Управление x87 FPU
|
||||
|
||||
* [**FINCSTP**](http://www.felixcloutier.com/x86/FINCSTP.html) — Инкремент указателя стека FPU.
|
||||
* [**FDECSTP**](http://www.felixcloutier.com/x86/FDECSTP.html) — Декремент указателя стека FPU.
|
||||
* [**FFREE**](http://www.felixcloutier.com/x86/FFREE.html) — Освободить регистр FPU.
|
||||
* [**FINIT / FNINIT**](http://www.felixcloutier.com/x86/FINIT:FNINIT.html) — Инициализация FPU.
|
||||
* [**FCLEX / FNCLEX**](http://www.felixcloutier.com/x86/FCLEX:FNCLEX.html) — Очистка флагов исключений FPU.
|
||||
* [**FSTCW / FNSTCW**](http://www.felixcloutier.com/x86/FSTCW:FNSTCW.html) — Сохранить управляющее слово FPU.
|
||||
* [**FLDCW**](http://www.felixcloutier.com/x86/FLDCW.html) — Загрузить управляющее слово FPU.
|
||||
* [**FSTENV / FNSTENV**](http://www.felixcloutier.com/x86/FSTENV:FNSTENV.html) — Сохранить окружение FPU.
|
||||
* [**FLDENV**](http://www.felixcloutier.com/x86/FLDENV.html) — Загрузить окружение FPU.
|
||||
* [**FSAVE / FNSAVE**](http://www.felixcloutier.com/x86/FSAVE:FNSAVE.html) — Сохранить состояние FPU.
|
||||
* [**FRSTOR**](http://www.felixcloutier.com/x86/FRSTOR.html) — Восстановить состояние FPU.
|
||||
* [**FSTSW / FNSTSW**](http://www.felixcloutier.com/x86/FSTSW:FNSTSW.html) — Сохранить статусное слово FPU.
|
||||
* [**WAIT / FWAIT**](http://www.felixcloutier.com/x86/WAIT:FWAIT.html) — Ожидание готовности FPU.
|
||||
* [**FNOP**](http://www.felixcloutier.com/x86/FNOP.html) — Пустая операция FPU.
|
||||
|
||||
## Управление состоянием x87 FPU и SIMD
|
||||
|
||||
* [**FXSAVE**](http://www.felixcloutier.com/x86/FXSAVE.html) — Сохранить состояние x87 FPU и SIMD.
|
||||
* [**FXRSTOR**](http://www.felixcloutier.com/x86/FXRSTOR.html) — Восстановить состояние x87 FPU и SIMD.
|
||||
|
||||
## Обзор MMX
|
||||
|
||||
* SIMD‑модель вычислений для 64‑битных упакованных целых.
|
||||
* Восемь 64‑битных регистров данных MMX.
|
||||
* Три новых типа упакованных данных:
|
||||
* 64‑битные упакованные байтовые целые (со знаком и без)
|
||||
* 64‑битные упакованные словные целые (со знаком и без)
|
||||
* 64‑битные упакованные двойные слова (со знаком и без)
|
||||
* Состояние MMX отображается на состояние x87 FPU; при переходах к инструкциям x87 FPU требуется осторожность во избежание неконсистентности результатов.
|
||||
|
||||
## Передача данных MMX
|
||||
|
||||
* [**MOVD / MOVQ**](http://www.felixcloutier.com/x86/MOVD:MOVQ.html) — Перемещение двойного/квадрослова между MMX и памятью/регистрами.
|
||||
|
||||
## Преобразования MMX
|
||||
|
||||
* [**PACKSSWB / PACKSSDW**](http://www.felixcloutier.com/x86/PACKSSWB:PACKSSDW.html) — Упаковка слов/двойных слов в байты со знаковым насыщением.
|
||||
* [**PACKUSWB**](http://www.felixcloutier.com/x86/PACKUSWB.html) — Упаковка слов в байты с беззнаковым насыщением.
|
||||
* [**PUNPCKHBW / PUNPCKHWD / PUNPCKHDQ**](http://www.felixcloutier.com/x86/PUNPCKHBW:PUNPCKHWD:PUNPCKHDQ:PUNPCKHQDQ.html) — Распаковка старших байтов/слов/двойных слов.
|
||||
* [**PUNPCKLBW / PUNPCKLWD / PUNPCKLDQ**](http://www.felixcloutier.com/x86/PUNPCKLBW:PUNPCKLWD:PUNPCKLDQ:PUNPCKLQDQ.html) — Распаковка младших байтов/слов/двойных слов.
|
||||
|
||||
## Упакованная арифметика MMX
|
||||
|
||||
* [**PADDB / PADDW / PADDD**](http://www.felixcloutier.com/x86/PADDB:PADDW:PADDD:PADDQ.html) — Сложение упакованных байтов/слов/двойных слов.
|
||||
* [**PADDSB / PADDSW**](http://www.felixcloutier.com/x86/PADDSB:PADDSW.html) — Сложение упакованных знаковых байтов/слов со знаковым насыщением.
|
||||
* [**PADDUSB / PADDUSW**](http://www.felixcloutier.com/x86/PADDUSB:PADDUSW.html) — Сложение упакованных беззнаковых байтов/слов с беззнаковым насыщением.
|
||||
* [**PSUBB / PSUBW / PSUBD**](http://www.felixcloutier.com/x86/PSUBB:PSUBW:PSUBD.html) — Вычитание упакованных байтов/слов/двойных слов.
|
||||
* [**PSUBSB / PSUBSW**](http://www.felixcloutier.com/x86/PSUBSB:PSUBSW.html) — Вычитание упакованных знаковых байтов/слов со знаковым насыщением.
|
||||
* [**PSUBUSB / PSUBUSW**](http://www.felixcloutier.com/x86/PSUBUSB:PSUBUSW.html) — Вычитание упакованных беззнаковых байтов/слов с беззнаковым насыщением.
|
||||
* [**PMULHW**](http://www.felixcloutier.com/x86/PMULHW.html) — Умножение упакованных знаковых слов с сохранением старшей части результата.
|
||||
* [**PMULLW**](http://www.felixcloutier.com/x86/PMULLW.html) — Умножение упакованных знаковых слов с сохранением младшей части.
|
||||
* [**PMADDWD**](http://www.felixcloutier.com/x86/PMADDWD.html) — Умножение и сложение упакованных слов.
|
||||
|
||||
## Сравнения MMX
|
||||
|
||||
* [**PCMPEQB / PCMPEQW / PCMPEQD**](http://www.felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html) — Сравнение упакованных байтов/слов/двойных слов на равенство.
|
||||
* [**PCMPGTB / PCMPGTW / PCMPGTD**](http://www.felixcloutier.com/x86/PCMPGTB:PCMPGTW:PCMPGTD.html) — Сравнение упакованных знаковых байтов/слов/двойных слов «больше чем».
|
||||
|
||||
## Логика MMX
|
||||
|
||||
* [**PAND**](http://www.felixcloutier.com/x86/PAND.html) — Побитовое И.
|
||||
* [**PANDN**](http://www.felixcloutier.com/x86/PANDN.html) — Побитовое И‑НЕ.
|
||||
* [**POR**](http://www.felixcloutier.com/x86/POR.html) — Побитовое ИЛИ.
|
||||
* [**PXOR**](http://www.felixcloutier.com/x86/PXOR.html) — Побитовое исключающее ИЛИ.
|
||||
|
||||
## Сдвиги и ротации MMX
|
||||
|
||||
* [**PSLLW / PSLLD / PSLLQ**](http://www.felixcloutier.com/x86/PSLLW:PSLLD:PSLLQ.html) — Логический сдвиг упакованных слов/двойных слов/квадрослов влево.
|
||||
* [**PSRLW / PSRLD / PSRLQ**](http://www.felixcloutier.com/x86/PSRLW:PSRLD:PSRLQ.html) — Логический сдвиг упакованных слов/двойных слов/квадрослов вправо.
|
||||
* [**PSRAW / PSRAD**](http://www.felixcloutier.com/x86/PSRAW:PSRAD:PSRAQ.html) — Арифметический сдвиг упакованных слов/двойных слов вправо.
|
||||
|
||||
## Управление состоянием MMX
|
||||
|
||||
* [**EMMS**](http://www.felixcloutier.com/x86/EMMS.html) — Очистить состояние MMX.
|
||||
|
||||
## Обзор SSE
|
||||
|
||||
* Расширяет SIMD‑модель, добавляя операции над упакованными и скалярными числами одиночной точности в 128‑битных регистрах.
|
||||
* Шестнадцать (в 32‑битном режиме — восемь) 128‑битных регистров XMM.
|
||||
* 128‑битные инструкции для упакованных и скалярных чисел одиночной точности.
|
||||
* Расширения MMX‑инструкций новыми операциями над упакованными целыми в регистрах MMX.
|
||||
* Явная предвыборка данных, управление кэшируемостью данных и упорядочиванием операций записи.
|
||||
|
||||
## Передача данных SSE
|
||||
|
||||
* [**MOVAPS**](http://www.felixcloutier.com/x86/MOVAPS.html) — Перемещение четырёх выровненных упакованных чисел одиночной точности между XMM и памятью.
|
||||
* [**MOVUPS**](http://www.felixcloutier.com/x86/MOVUPS.html) — Перемещение четырёх невыровненных упакованных чисел одиночной точности между XMM и памятью.
|
||||
* [**MOVHPS**](http://www.felixcloutier.com/x86/MOVHPS.html) — Перемещение двух чисел одиночной точности из/в старшую квадрословную часть XMM и память.
|
||||
* [**MOVHLPS**](http://www.felixcloutier.com/x86/MOVHLPS.html) — Перемещение двух чисел из старшей части одного XMM в младшую часть другого XMM.
|
||||
* [**MOVLPS**](http://www.felixcloutier.com/x86/MOVLPS.html) — Перемещение двух чисел одиночной точности из/в младшую квадрословную часть XMM и память.
|
||||
* [**MOVLHPS**](http://www.felixcloutier.com/x86/MOVLHPS.html) — Перемещение двух чисел из младшей части одного XMM в старшую часть другого XMM.
|
||||
* [**MOVMSKPS**](http://www.felixcloutier.com/x86/MOVMSKPS.html) — Извлечь маску знаков из четырёх упакованных чисел одиночной точности.
|
||||
* [**MOVSS**](http://www.felixcloutier.com/x86/MOVSS.html) — Перемещение скалярного числа одиночной точности между XMM и памятью.
|
||||
|
||||
## Арифметика SSE (FP32)
|
||||
|
||||
* [**ADDPS**](http://www.felixcloutier.com/x86/ADDPS.html) — Сложение упакованных чисел одиночной точности.
|
||||
* [**ADDSS**](http://www.felixcloutier.com/x86/ADDSS.html) — Сложение скалярных чисел одиночной точности.
|
||||
* [**SUBPS**](http://www.felixcloutier.com/x86/SUBPS.html) — Вычитание упакованных чисел одиночной точности.
|
||||
* [**SUBSS**](http://www.felixcloutier.com/x86/SUBSS.html) — Вычитание скалярных чисел одиночной точности.
|
||||
* [**MULPS**](http://www.felixcloutier.com/x86/MULPS.html) — Умножение упакованных чисел одиночной точности.
|
||||
* [**MULSS**](http://www.felixcloutier.com/x86/MULSS.html) — Умножение скалярных чисел одиночной точности.
|
||||
* [**DIVPS**](http://www.felixcloutier.com/x86/DIVPS.html) — Деление упакованных чисел одиночной точности.
|
||||
* [**DIVSS**](http://www.felixcloutier.com/x86/DIVSS.html) — Деление скалярных чисел одиночной точности.
|
||||
* [**RCPPS**](http://www.felixcloutier.com/x86/RCPPS.html) — Обратные значения (1/x) для упакованных чисел.
|
||||
* [**RCPSS**](http://www.felixcloutier.com/x86/RCPSS.html) — Обратное значение для скаляра.
|
||||
* [**SQRTPS**](http://www.felixcloutier.com/x86/SQRTPS.html) — Квадратные корни упакованных чисел.
|
||||
* [**SQRTSS**](http://www.felixcloutier.com/x86/SQRTSS.html) — Квадратный корень скалярного числа.
|
||||
* [**RSQRTPS**](http://www.felixcloutier.com/x86/RSQRTPS.html) — Обратные квадратные корни упакованных чисел.
|
||||
* [**RSQRTSS**](http://www.felixcloutier.com/x86/RSQRTSS.html) — Обратный квадратный корень скаляра.
|
||||
* [**MAXPS**](http://www.felixcloutier.com/x86/MAXPS.html) — Максимумы упакованных чисел одиночной точности.
|
||||
* [**MAXSS**](http://www.felixcloutier.com/x86/MAXSS.html) — Максимум скаляра одиночной точности.
|
||||
* [**MINPS**](http://www.felixcloutier.com/x86/MINPS.html) — Минимумы упакованных чисел одиночной точности.
|
||||
* [**MINSS**](http://www.felixcloutier.com/x86/MINSS.html) — Минимум скаляра одиночной точности.
|
||||
|
||||
## Сравнения SSE
|
||||
|
||||
* [**CMPPS**](http://www.felixcloutier.com/x86/CMPPS.html) — Сравнение упакованных чисел одиночной точности.
|
||||
* [**CMPSS**](http://www.felixcloutier.com/x86/CMPSS.html) — Сравнение скалярных чисел одиночной точности.
|
||||
* [**COMISS**](http://www.felixcloutier.com/x86/COMISS.html) — Упорядоченное сравнение скаляров и установка флагов `rflags`.
|
||||
* [**UCOMISS**](http://www.felixcloutier.com/x86/UCOMISS.html) — Неупорядоченное сравнение скаляров и установка флагов `rflags`.
|
||||
|
||||
## Логика SSE
|
||||
|
||||
* [**ANDPS**](http://www.felixcloutier.com/x86/ANDPS.html) — Побитовое И упакованных чисел одиночной точности.
|
||||
* [**ANDNPS**](http://www.felixcloutier.com/x86/ANDNPS.html) — Побитовое И‑НЕ упакованных чисел.
|
||||
* [**ORPS**](http://www.felixcloutier.com/x86/ORPS.html) — Побитовое ИЛИ упакованных чисел.
|
||||
* [**XORPS**](http://www.felixcloutier.com/x86/XORPS.html) — Побитовое XOR упакованных чисел.
|
||||
|
||||
## Перестановки и распаковка SSE
|
||||
|
||||
* [**SHUFPS**](http://www.felixcloutier.com/x86/SHUFPS.html) — Перестановка значений в упакованных операндах.
|
||||
* [**UNPCKHPS**](http://www.felixcloutier.com/x86/UNPCKHPS.html) — Распаковка и чередование двух старших значений из двух операндов.
|
||||
* [**UNPCKLPS**](http://www.felixcloutier.com/x86/UNPCKLPS.html) — Распаковка и чередование двух младших значений из двух операндов.
|
||||
|
||||
## Преобразования SSE
|
||||
|
||||
* [**CVTPI2PS**](http://www.felixcloutier.com/x86/CVTPI2PS.html) — Преобразование упакованных двойных слов в упакованные числа одиночной точности.
|
||||
* [**CVTSI2SS**](http://www.felixcloutier.com/x86/CVTSI2SS.html) — Преобразование двойного слова в скаляр одиночной точности.
|
||||
* [**CVTPS2PI**](http://www.felixcloutier.com/x86/CVTPS2PI.html) — Преобразование упакованных чисел одиночной точности в упакованные двойные слова.
|
||||
* [**CVTTPS2PI**](http://www.felixcloutier.com/x86/CVTTPS2PI.html) — То же с усечением.
|
||||
* [**CVTSS2SI**](http://www.felixcloutier.com/x86/CVTSS2SI.html) — Преобразование скаляра одиночной точности в двойное слово.
|
||||
* [**CVTTSS2SI**](http://www.felixcloutier.com/x86/CVTTSS2SI.html) — То же с усечением.
|
||||
|
||||
## Управление MXCSR (SSE)
|
||||
|
||||
* [**LDMXCSR**](http://www.felixcloutier.com/x86/LDMXCSR.html) — Загрузка регистра MXCSR.
|
||||
* [**STMXCSR**](http://www.felixcloutier.com/x86/STMXCSR.html) — Сохранение состояния MXCSR.
|
||||
|
||||
## SSE: 64‑битные целые (расширения MMX)
|
||||
|
||||
* [**PAVGB / PAVGW**](http://www.felixcloutier.com/x86/PAVGB:PAVGW.html) — Среднее значение упакованных беззнаковых байтов.
|
||||
* [**PEXTRW**](http://www.felixcloutier.com/x86/PEXTRW.html) — Извлечь слово.
|
||||
* [**PINSRW**](http://www.felixcloutier.com/x86/PINSRW.html) — Вставить слово.
|
||||
* [**PMAXUB**](http://www.felixcloutier.com/x86/PMAXUB:PMAXUW.html) — Максимум упакованных беззнаковых байтов.
|
||||
* [**PMAXSW**](http://www.felixcloutier.com/x86/PMAXSB:PMAXSW:PMAXSD:PMAXSQ.html) — Максимум упакованных знаковых слов.
|
||||
* [**PMINUB**](http://www.felixcloutier.com/x86/PMINUB:PMINUW.html) — Минимум упакованных беззнаковых байтов.
|
||||
* [**PMINSW**](http://www.felixcloutier.com/x86/PMINSB:PMINSW.html) — Минимум упакованных знаковых слов.
|
||||
* [**PMOVMSKB**](http://www.felixcloutier.com/x86/PMOVMSKB.html) — Маска байтов (перенос знаков в маску).
|
||||
* [**PMULHUW**](http://www.felixcloutier.com/x86/PMULHUW.html) — Умножение упакованных беззнаковых слов, сохранение старшей части.
|
||||
* [**PSADBW**](http://www.felixcloutier.com/x86/MPSADBW.html) — Сумма абсолютных разностей.
|
||||
* [**PSHUFW**](http://www.felixcloutier.com/x86/PSHUFW.html) — Перестановка слов в регистре MMX.
|
||||
|
||||
## SSE: кэшируемость, предвыборка и упорядочивание
|
||||
|
||||
* [**MASKMOVQ**](http://www.felixcloutier.com/x86/MASKMOVQ.html) — Нетемпоральная запись выбранных байтов из MMX в память.
|
||||
* [**MOVNTQ**](http://www.felixcloutier.com/x86/MOVNTQ.html) — Нетемпоральная запись квадрослова из MMX в память.
|
||||
* [**MOVNTPS**](http://www.felixcloutier.com/x86/MOVNTPS.html) — Нетемпоральная запись четырёх упакованных чисел одиночной точности из XMM в память.
|
||||
* [**PREFETCHh**](http://www.felixcloutier.com/x86/PREFETCHh.html) — Загрузка 32 и более байт из памяти в выбранный уровень кэша.
|
||||
* [**SFENCE**](http://www.felixcloutier.com/x86/SFENCE.html) — Сериализация операций записи.
|
||||
|
||||
## Обзор SSE2
|
||||
|
||||
* Упакованные и скалярные 128‑битные числа двойной точности.
|
||||
* Дополнительные инструкции для 64‑ и 128‑битных упакованных байтов/слов/двойных слов/квадрослов.
|
||||
* 128‑битные версии целочисленных инструкций из MMX и SSE.
|
||||
* Дополнительные инструкции управления кэшируемостью и упорядочиванием инструкций.
|
||||
|
||||
## SSE2: перемещение данных FP64
|
||||
|
||||
* [**MOVAPD**](http://www.felixcloutier.com/x86/MOVAPD.html) — Перемещение двух выровненных упакованных чисел двойной точности между XMM и памятью.
|
||||
* [**MOVUPD**](http://www.felixcloutier.com/x86/MOVUPD.html) — Перемещение двух невыровненных упакованных чисел двойной точности между XMM и памятью.
|
||||
* [**MOVHPD**](http://www.felixcloutier.com/x86/MOVHPD.html) — Перемещение старшего элемента двойной точности из/в старшую часть XMM и память.
|
||||
* [**MOVLPD**](http://www.felixcloutier.com/x86/MOVLPD.html) — Перемещение младшего элемента двойной точности из/в младшую часть XMM и память.
|
||||
* [**MOVMSKPD**](http://www.felixcloutier.com/x86/MOVMSKPD.html) — Извлечь маску знаков из двух упакованных чисел двойной точности.
|
||||
* [**MOVSD**](http://www.felixcloutier.com/x86/MOVSD.html) — Перемещение скалярного числа двойной точности между XMM и памятью.
|
||||
|
||||
## SSE2: арифметика FP64
|
||||
|
||||
* [**ADDPD**](http://www.felixcloutier.com/x86/ADDPD.html) — Сложение упакованных чисел двойной точности.
|
||||
* [**ADDSD**](http://www.felixcloutier.com/x86/ADDSD.html) — Сложение скалярных чисел двойной точности.
|
||||
* [**SUBPD**](http://www.felixcloutier.com/x86/SUBPD.html) — Вычитание упакованных чисел двойной точности.
|
||||
* [**SUBSD**](http://www.felixcloutier.com/x86/SUBSD.html) — Вычитание скалярных чисел двойной точности.
|
||||
* [**MULPD**](http://www.felixcloutier.com/x86/MULPD.html) — Умножение упакованных чисел двойной точности.
|
||||
* [**MULSD**](http://www.felixcloutier.com/x86/MULSD.html) — Умножение скалярных чисел двойной точности.
|
||||
* [**DIVPD**](http://www.felixcloutier.com/x86/DIVPD.html) — Деление упакованных чисел двойной точности.
|
||||
* [**DIVSD**](http://www.felixcloutier.com/x86/DIVSD.html) — Деление скалярных чисел двойной точности.
|
||||
* [**SQRTPD**](http://www.felixcloutier.com/x86/SQRTPD.html) — Квадратные корни упакованных чисел двойной точности.
|
||||
* [**SQRTSD**](http://www.felixcloutier.com/x86/SQRTSD.html) — Квадратный корень скалярного числа двойной точности.
|
||||
* [**MAXPD**](http://www.felixcloutier.com/x86/MAXPD.html) — Максимумы упакованных чисел двойной точности.
|
||||
* [**MAXSD**](http://www.felixcloutier.com/x86/MAXSD.html) — Максимум скаляра двойной точности.
|
||||
* [**MINPD**](http://www.felixcloutier.com/x86/MINPD.html) — Минимумы упакованных чисел двойной точности.
|
||||
* [**MINSD**](http://www.felixcloutier.com/x86/MINSD.html) — Минимум скаляра двойной точности.
|
||||
|
||||
## SSE2: логические операции FP64
|
||||
|
||||
* [**ANDPD**](http://www.felixcloutier.com/x86/ANDPD.html) — Побитовое И упакованных чисел двойной точности.
|
||||
* [**ANDNPD**](http://www.felixcloutier.com/x86/ANDNPD.html) — Побитовое И‑НЕ упакованных чисел двойной точности.
|
||||
* [**ORPD**](http://www.felixcloutier.com/x86/ORPD.html) — Побитовое ИЛИ упакованных чисел двойной точности.
|
||||
* [**XORPD**](http://www.felixcloutier.com/x86/XORPD.html) — Побитовое XOR упакованных чисел двойной точности.
|
||||
|
||||
## SSE2: сравнения FP64
|
||||
|
||||
* [**CMPPD**](http://www.felixcloutier.com/x86/CMPPD.html) — Сравнение упакованных чисел двойной точности.
|
||||
* [**CMPSD**](http://www.felixcloutier.com/x86/CMPSD.html) — Сравнение скаляров двойной точности.
|
||||
* [**COMISD**](http://www.felixcloutier.com/x86/COMISD.html) — Упорядоченное сравнение скаляров двойной точности с установкой `rflags`.
|
||||
* [**UCOMISD**](http://www.felixcloutier.com/x86/UCOMISD.html) — Неупорядоченное сравнение скаляров двойной точности с установкой `rflags`.
|
||||
|
||||
## SSE2: перестановки и распаковка FP64
|
||||
|
||||
* [**SHUFPD**](http://www.felixcloutier.com/x86/SHUFPD.html) — Перестановка значений в упакованных операндах двойной точности.
|
||||
* [**UNPCKHPD**](http://www.felixcloutier.com/x86/UNPCKHPD.html) — Распаковка и чередование старших значений из двух операндов.
|
||||
* [**UNPCKLPD**](http://www.felixcloutier.com/x86/UNPCKLPD.html) — Распаковка и чередование младших значений из двух операндов.
|
||||
|
||||
## SSE2: преобразования
|
||||
|
||||
* [**CVTPD2PI**](http://www.felixcloutier.com/x86/CVTPD2PI.html) — Преобразование упакованных чисел двойной точности в упакованные двойные слова.
|
||||
* [**CVTTPD2PI**](http://www.felixcloutier.com/x86/CVTTPD2PI.html) — То же с усечением.
|
||||
* [**CVTPI2PD**](http://www.felixcloutier.com/x86/CVTPI2PD.html) — Преобразование упакованных двойных слов в упакованные числа двойной точности.
|
||||
* [**CVTPD2DQ**](http://www.felixcloutier.com/x86/CVTPD2DQ.html) — Преобразование упакованных чисел двойной точности в упакованные двойные слова.
|
||||
* [**CVTTPD2DQ**](http://www.felixcloutier.com/x86/CVTTPD2DQ.html) — То же с усечением.
|
||||
* [**CVTDQ2PD**](http://www.felixcloutier.com/x86/CVTDQ2PD.html) — Преобразование упакованных двойных слов в упакованные числа двойной точности.
|
||||
* [**CVTPS2PD**](http://www.felixcloutier.com/x86/CVTPS2PD.html) — Преобразование упакованных чисел одиночной в двойную точность.
|
||||
* [**CVTPD2PS**](http://www.felixcloutier.com/x86/CVTPS2PD.html) — Преобразование упакованных чисел двойной в одиночную точность.
|
||||
* [**CVTSS2SD**](http://www.felixcloutier.com/x86/CVTSS2SD.html) — Преобразование скаляра одиночной в двойную точность.
|
||||
* [**CVTSD2SS**](http://www.felixcloutier.com/x86/CVTSD2SS.html) — Преобразование скаляра двойной в одиночную точность.
|
||||
* [**CVTSD2SI**](http://www.felixcloutier.com/x86/CVTSD2SI.html) — Преобразование скаляра двойной точности в двойное слово.
|
||||
* [**CVTTSD2SI**](http://www.felixcloutier.com/x86/CVTTSD2SI.html) — То же с усечением.
|
||||
* [**CVTSI2SD**](http://www.felixcloutier.com/x86/CVTSI2SD.html) — Преобразование двойного слова в скаляр двойной точности.
|
||||
|
||||
## SSE2: FP32 (расширения SSE)
|
||||
|
||||
* [**CVTDQ2PS**](http://www.felixcloutier.com/x86/CVTDQ2PS.html) — Преобразование упакованных двойных слов в упакованные числа одиночной точности.
|
||||
* [**CVTPS2DQ**](http://www.felixcloutier.com/x86/CVTPS2DQ.html) — Преобразование упакованных чисел одиночной точности в упакованные двойные слова.
|
||||
* [**CVTTPS2DQ**](http://www.felixcloutier.com/x86/CVTTPS2DQ.html) — То же с усечением.
|
||||
|
||||
## SSE2: целочисленные инструкции
|
||||
|
||||
* [**MOVDQA**](http://www.felixcloutier.com/x86/MOVDQA:VMOVDQA32:VMOVDQA64.html) — Перемещение выровненного двойного квадрослова.
|
||||
* [**MOVDQU**](http://www.felixcloutier.com/x86/MOVDQU:VMOVDQU8:VMOVDQU16:VMOVDQU32:VMOVDQU64.html) — Перемещение невыровненного двойного квадрослова.
|
||||
* [**MOVQ2DQ**](http://www.felixcloutier.com/x86/MOVQ2DQ.html) — Перемещение квадрослова из MMX в XMM.
|
||||
* [**MOVDQ2Q**](http://www.felixcloutier.com/x86/MOVDQ2Q.html) — Перемещение квадрослова из XMM в MMX.
|
||||
* [**PMULUDQ**](http://www.felixcloutier.com/x86/PMULUDQ.html) — Умножение упакованных беззнаковых двойных слов.
|
||||
* [**PADDQ**](http://www.felixcloutier.com/x86/PADDB:PADDW:PADDD:PADDQ.html) — Сложение упакованных квадрослов.
|
||||
* [**PSUBQ**](http://www.felixcloutier.com/x86/PSUBQ.html) — Вычитание упакованных квадрослов.
|
||||
* [**PSHUFLW**](http://www.felixcloutier.com/x86/PSHUFLW.html) — Перестановка младших слов.
|
||||
* [**PSHUFHW**](http://www.felixcloutier.com/x86/PSHUFHW.html) — Перестановка старших слов.
|
||||
* [**PSHUFD**](http://www.felixcloutier.com/x86/PSHUFD.html) — Перестановка двойных слов.
|
||||
* [**PSLLDQ**](http://www.felixcloutier.com/x86/PSLLDQ.html) — Логический сдвиг двойного квадрослова влево.
|
||||
* [**PSRLDQ**](http://www.felixcloutier.com/x86/PSRLDQ.html) — Логический сдвиг двойного квадрослова вправо.
|
||||
* [**PUNPCKHQDQ**](http://www.felixcloutier.com/x86/PUNPCKHBW:PUNPCKHWD:PUNPCKHDQ:PUNPCKHQDQ.html) — Распаковка старших квадрослов.
|
||||
* [**PUNPCKLQDQ**](http://www.felixcloutier.com/x86/PUNPCKLBW:PUNPCKLWD:PUNPCKLDQ:PUNPCKLQDQ.html) — Распаковка младших квадрослов.
|
||||
|
||||
## SSE2: кэшируемость и упорядочивание
|
||||
|
||||
* [**CLFLUSH**](http://www.felixcloutier.com/x86/CLFLUSH.html) — Сброс линии кэша.
|
||||
* [**LFENCE**](http://www.felixcloutier.com/x86/LFENCE.html) — Сериализация операций чтения.
|
||||
* [**MFENCE**](http://www.felixcloutier.com/x86/MFENCE.html) — Сериализация операций чтения и записи.
|
||||
* [**PAUSE**](http://www.felixcloutier.com/x86/PAUSE.html) — Улучшает производительность «циклов активного ожидания» (spin‑wait).
|
||||
* [**MASKMOVDQU**](http://www.felixcloutier.com/x86/MASKMOVDQU.html) — Нетемпоральная запись выбранных байтов из XMM в память.
|
||||
* [**MOVNTPD**](http://www.felixcloutier.com/x86/MOVNTPD.html) — Нетемпоральная запись двух упакованных чисел двойной точности из XMM в память.
|
||||
* [**MOVNTDQ**](http://www.felixcloutier.com/x86/MOVNTDQ.html) — Нетемпоральная запись двойного квадрослова из XMM в память.
|
||||
* [**MOVNTI**](http://www.felixcloutier.com/x86/MOVNTI.html) — Нетемпоральная запись двойного слова из регистра общего назначения в память.
|
||||
|
||||
## Ссылки
|
||||
|
||||
* https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
|
||||
* http://www.agner.org/optimize/optimizing_assembly.pdf
|
||||
* https://www.nasm.us/xdoc/2.13.03/nasmdoc.pdf
|
||||
* https://godbolt.org/
|
||||
* https://www.lri.fr/~filliatr/ens/compil/x86-64.pdf
|
||||
* https://0xax.github.io/categories/assembler/
|
||||
|
||||
## Таблицы инструкций
|
||||
|
||||
* http://www.agner.org/optimize/instruction_tables.pdf
|
||||
|
||||
## Примеры
|
||||
|
||||
* https://github.com/torvalds/linux/tree/master/arch/x86
|
||||
* https://gist.github.com/rygorous/bf1659bf6cd1752ed114367d4b87b302
|
||||
* https://www.csee.umbc.edu/portal/help/nasm/sample_64.shtml
|
||||
|
||||
## Утилиты
|
||||
|
||||
* https://software.intel.com/sites/landingpage/IntrinsicsGuide/
|
||||
* https://git.ffmpeg.org/gitweb/ffmpeg.git/blob_plain/HEAD:/libavutil/x86/x86inc.asm
|
||||
* https://gist.github.com/rygorous/f729919ff64526a46e591d8f8b52058e
|
||||
49
minimal/.vscode/launch.json
vendored
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "lldb x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/minimal",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "cppdbg x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/minimal",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
|
||||
},
|
||||
{
|
||||
"name": "by-gdb x64",
|
||||
"type": "by-gdb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/minimal",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
"name": "gdb x64",
|
||||
"type": "gdb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/minimal",
|
||||
"preLaunchTask": "asm64",
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
"name": "lldb+GCC x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/minimal",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
nums dq 112, 113, 114, 115, 116 ; массив чисел типа qword
|
||||
currentAddr dq $
|
||||
|
||||
section .text
|
||||
_start:
|
||||
mov rdi, [currentAddr - 8] ; rdi = 116
|
||||
mov rax, 60
|
||||
syscall
|
||||
@@ -4,14 +4,28 @@
|
||||
"path": "casm"
|
||||
},
|
||||
{
|
||||
"path": "minimal"
|
||||
"path": "wayland"
|
||||
},
|
||||
{
|
||||
"path": "docs"
|
||||
}
|
||||
},
|
||||
],
|
||||
"settings": {
|
||||
"debug.allowBreakpointsEverywhere": true,
|
||||
"debug.inlineValues": "on",
|
||||
"editor.codeLens": false,
|
||||
"files.associations": {
|
||||
"flake.lock": "json",
|
||||
"ios": "c",
|
||||
"cstdint": "c",
|
||||
"array": "c",
|
||||
"string": "c",
|
||||
"string_view": "c",
|
||||
"ranges": "c",
|
||||
"span": "c",
|
||||
"vector": "c",
|
||||
"regex": "c",
|
||||
"__node_handle": "c"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
stepik_base/10_to_2/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/10_to_2",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/10_to_2",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/10_to_2",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,9 +7,9 @@
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/minimal;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
|
||||
"ld -g -m elf_x86_64 -o $rawfilename $rawfilename.o;"
|
||||
"rawfilename=$builddir/10_to_2;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/10_to_2.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
@@ -33,8 +33,8 @@
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/minimal;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
|
||||
"rawfilename=$builddir/10_to_2;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/10_to_2.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
147
stepik_base/10_to_2/10_to_2.asm
Normal file
@@ -0,0 +1,147 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 32 db 0
|
||||
buffend:
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [rel buff] ; поставить указатель на начало буфера
|
||||
call read_num
|
||||
call print_num_to_buf
|
||||
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция складывает два значения
|
||||
; In: RDI, RSI
|
||||
; Out: RAX
|
||||
calculate_sum:
|
||||
lea rax, [rdi + rsi]
|
||||
ret
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из stdin
|
||||
; Out: RAX
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
lea rdi, [rel buff] ; rdi - конец данных
|
||||
add rdi, [count]
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
mul rcx
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
; Функция выводит число в buff
|
||||
; In: RAX
|
||||
; Out: RSI - указатель на начало строки
|
||||
print_num_to_buf:
|
||||
lea rsi, [buffend]
|
||||
dec rsi
|
||||
|
||||
mov rdi, rsi ; сохранить последний адрес
|
||||
mov byte [rsi], 0 ; вписать нуль
|
||||
dec rsi
|
||||
mov byte [rsi], 0xA ; вписать перенос строки
|
||||
dec rsi
|
||||
mov byte [rsi], '0'
|
||||
|
||||
test rax, rax
|
||||
jz .return
|
||||
|
||||
.while:
|
||||
xor rdx, rdx
|
||||
test rax, rax
|
||||
jz .break
|
||||
mov dl, al
|
||||
and dl, 1
|
||||
or dl, 0x30
|
||||
mov [rsi], dl
|
||||
dec rsi
|
||||
shr rax, 1
|
||||
jmp .while
|
||||
.break:
|
||||
inc rsi
|
||||
.return:
|
||||
sub rdi, rsi
|
||||
inc rdi
|
||||
mov [count], rdi
|
||||
ret
|
||||
29
stepik_base/even_odd/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/even_odd",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/even_odd",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/even_odd",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/even_odd/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/even_odd;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/even_odd.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/even_odd;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/even_odd.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
80
stepik_base/even_odd/even_odd.asm
Normal file
@@ -0,0 +1,80 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 32 db 0
|
||||
count dq 0
|
||||
even db "Even"
|
||||
evenCount dq $-even
|
||||
odd db "Odd"
|
||||
oddCount dq $-odd
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [buff]
|
||||
add rsi, qword [count]
|
||||
sub rsi, 2
|
||||
mov rax, [rsi]
|
||||
|
||||
test rax, 0x1
|
||||
jnz .even
|
||||
.odd:
|
||||
lea rsi, [even]
|
||||
mov rdx, [evenCount]
|
||||
jmp .write
|
||||
.even:
|
||||
lea rsi, [odd]
|
||||
mov rdx, [oddCount]
|
||||
|
||||
.write:
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
29
stepik_base/is_digit/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/is_digit",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/is_digit",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/is_digit",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/is_digit/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/is_digit;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/is_digit.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/is_digit;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/is_digit.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
79
stepik_base/is_digit/is_digit.asm
Normal file
@@ -0,0 +1,79 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
g_buff times 10 db 0
|
||||
g_buffLen dq 0
|
||||
g_digit db "Digit"
|
||||
g_digitLen dq $-g_digit
|
||||
g_notDigit db "Not a digit"
|
||||
g_notDigitLen dq $-g_notDigit
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
movzx rax, byte [g_buff] ; первый символ
|
||||
|
||||
cmp rax, '0'
|
||||
jl .notDigit
|
||||
cmp rax, '9'
|
||||
jg .notDigit
|
||||
.digit:
|
||||
lea rsi, [g_digit]
|
||||
mov rdx, [g_digitLen]
|
||||
jmp .write
|
||||
.notDigit:
|
||||
lea rsi, [g_notDigit]
|
||||
mov rdx, [g_notDigitLen]
|
||||
|
||||
.write:
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel g_buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [g_buffLen], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [g_buffLen], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
29
stepik_base/less_greater_10/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/less_greater_10/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/less_greater_10;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/less_greater_10.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/less_greater_10;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/less_greater_10.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
108
stepik_base/less_greater_10/less_greater_10.asm
Normal file
@@ -0,0 +1,108 @@
|
||||
; Макрос для вывода сообщения
|
||||
; In:
|
||||
; - %1 - указатель на буффер с сообщением
|
||||
; - %2 - длина сообщения
|
||||
%macro PRINT_MSG 2
|
||||
mov rsi, %1
|
||||
mov rdx, %2
|
||||
mov rdi, 1 ; stdout
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
syscall
|
||||
%endmacro
|
||||
|
||||
global _start
|
||||
|
||||
section .data
|
||||
g_buff times 32 db 0
|
||||
g_buff_end:
|
||||
g_len dq 0
|
||||
|
||||
g_less_or_equal db "Less than or equal to 10"
|
||||
g_less_or_equal_len dq $-g_less_or_equal
|
||||
g_greater db "Greater than 10"
|
||||
g_greater_len dq $-g_greater
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [rel g_buff] ; поставить указатель на начало буфера
|
||||
call read_num
|
||||
|
||||
cmp rax, 10
|
||||
jle .le
|
||||
.g:
|
||||
lea rdi, [g_greater]
|
||||
mov rdx, [g_greater_len]
|
||||
jmp .print
|
||||
.le:
|
||||
lea rdi, [g_less_or_equal]
|
||||
mov rdx, [g_less_or_equal_len]
|
||||
|
||||
.print:
|
||||
PRINT_MSG rdi, rdx
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel g_buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [g_len], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из stdin
|
||||
; Out: RAX
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
lea rdi, [g_buff] ; rdi - конец данных
|
||||
add rdi, [g_len]
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
mul rcx
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
29
stepik_base/less_greater_10_neg/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10_neg",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10_neg",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/less_greater_10_neg",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/less_greater_10_neg/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/less_greater_10_neg;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/less_greater_10_neg.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/less_greater_10_neg;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/less_greater_10_neg.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
119
stepik_base/less_greater_10_neg/less_greater_10_neg.asm
Normal file
@@ -0,0 +1,119 @@
|
||||
; Макрос для вывода сообщения
|
||||
; In:
|
||||
; - %1 - указатель на буффер с сообщением
|
||||
; - %2 - длина сообщения
|
||||
%macro PRINT_MSG 2
|
||||
mov rsi, %1
|
||||
mov rdx, %2
|
||||
mov rdi, 1 ; stdout
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
syscall
|
||||
%endmacro
|
||||
|
||||
global _start
|
||||
|
||||
section .data
|
||||
g_buff times 32 db 0
|
||||
g_buff_end:
|
||||
g_len dq 0
|
||||
|
||||
g_less_or_equal db "B"
|
||||
g_less_or_equal_len dq $-g_less_or_equal
|
||||
g_greater db "A"
|
||||
g_greater_len dq $-g_greater
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [rel g_buff] ; поставить указатель на начало буфера
|
||||
call read_num
|
||||
|
||||
cmp rax, 10
|
||||
jle .le
|
||||
.g:
|
||||
lea rdi, [g_greater]
|
||||
mov rdx, [g_greater_len]
|
||||
jmp .print
|
||||
.le:
|
||||
lea rdi, [g_less_or_equal]
|
||||
mov rdx, [g_less_or_equal_len]
|
||||
|
||||
.print:
|
||||
PRINT_MSG rdi, rdx
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel g_buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [g_len], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из stdin
|
||||
; Out: RAX
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
|
||||
xor r8, 1 ; r8 - флаг знака
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
lea rdi, [g_buff] ; rdi - конец данных
|
||||
add rdi, [g_len]
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
cmp bl, '-'
|
||||
jne .add
|
||||
.neg:
|
||||
neg r8
|
||||
jmp .continue
|
||||
.add:
|
||||
mul rcx
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
|
||||
.continue:
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
mul r8
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
29
stepik_base/macro/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/macro",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/macro",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/macro",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/macro/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/macro;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/macro.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/macro;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/macro.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
27
stepik_base/macro/macro.asm
Normal file
@@ -0,0 +1,27 @@
|
||||
; Макрос для вывода сообщения
|
||||
; In:
|
||||
; - %1 - указатель на буффер с сообщением
|
||||
; - %2 - длина сообщения
|
||||
%macro PRINT_MSG 2
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
mov rsi, %1
|
||||
mov rdx, %2
|
||||
syscall
|
||||
%endmacro
|
||||
|
||||
global _start
|
||||
|
||||
section .data
|
||||
g_msg db "Hello, Stepic!"
|
||||
g_msg_len dq $-g_msg
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
PRINT_MSG g_msg, [g_msg_len]
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
29
stepik_base/min/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/min",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/min",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/min",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/min/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/min;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/min.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/min;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/min.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
94
stepik_base/min/min.asm
Normal file
@@ -0,0 +1,94 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
arr db 5, 7, 3, 1, 2
|
||||
arrEnd:
|
||||
|
||||
buff times 32 db 0
|
||||
buffend:
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
mov al, 0xFF
|
||||
lea rsi, [arr]
|
||||
lea rdi, [arrEnd]
|
||||
|
||||
.while:
|
||||
movzx rbx, byte [rsi] ; текущее число
|
||||
cmp rbx, rax
|
||||
cmovl rax, rbx
|
||||
inc rsi
|
||||
cmp rsi, rdi
|
||||
jne .while
|
||||
|
||||
call print_num_to_buf
|
||||
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция выводит число в buff
|
||||
; In: RAX
|
||||
; Out: RSI - указатель на начало строки
|
||||
print_num_to_buf:
|
||||
lea rsi, [buffend]
|
||||
dec rsi
|
||||
|
||||
mov rdi, rsi ; сохранить последний адрес
|
||||
mov byte [rsi], 0 ; вписать нуль
|
||||
dec rsi
|
||||
mov byte [rsi], 0xA ; вписать перенос строки
|
||||
dec rsi
|
||||
mov byte [rsi], '0'
|
||||
|
||||
test rax, rax
|
||||
jz .return
|
||||
|
||||
mov rcx, 10 ; десятичный делитель
|
||||
|
||||
.while:
|
||||
xor rdx, rdx
|
||||
test rax, rax
|
||||
jz .break
|
||||
div rcx
|
||||
or dl, 0x30
|
||||
mov [rsi], dl
|
||||
dec rsi
|
||||
jmp .while
|
||||
.break:
|
||||
inc rsi
|
||||
.return:
|
||||
sub rdi, rsi
|
||||
inc rdi
|
||||
mov [count], rdi
|
||||
ret
|
||||
29
stepik_base/repeat_str/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/repeat_str",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/repeat_str",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/repeat_str",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/repeat_str/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/repeat_str;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/repeat_str.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/repeat_str;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/repeat_str.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
130
stepik_base/repeat_str/repeat_str.asm
Normal file
@@ -0,0 +1,130 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 256 db 0
|
||||
buffSize dq $-buff
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
lea rsi, [buff]
|
||||
lea rdi, [count]
|
||||
mov rdx, [buffSize]
|
||||
call read_text
|
||||
|
||||
mov al, 0xA
|
||||
lea rdi, [buff]
|
||||
mov rcx, [count]
|
||||
repne scasb
|
||||
|
||||
; Теперь rdi указывает на символ после \n
|
||||
; Вычисляем длину строки: rdi - buff
|
||||
lea rax, [buff]
|
||||
sub rdi, rax
|
||||
mov [count], rdi ; Сохраняем длину строки в count
|
||||
|
||||
; rsi уже установлен на buff + длина строки, т.е. на числе
|
||||
mov rsi, rax
|
||||
add rsi, rdi
|
||||
mov rdx, 256
|
||||
call read_num
|
||||
|
||||
mov rcx, rax
|
||||
jrcxz .return
|
||||
.while:
|
||||
lea rsi, [buff]
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
loop .while
|
||||
|
||||
.return:
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; In:
|
||||
; - RSI - указатель на буффер
|
||||
; - RDI - указатель на адрес для сохранения длины
|
||||
; - RDX - длина буффера
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
syscall
|
||||
|
||||
pop rdi
|
||||
mov [rdi], rax
|
||||
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rcx
|
||||
push rax
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rax
|
||||
pop rcx
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из буфера
|
||||
; In:
|
||||
; - RSI - указатель на буффер
|
||||
; - RDX - длина буффера
|
||||
; Out:
|
||||
; - RAX - число
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
mov rdi, rsi
|
||||
add rdi, rdx
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0x0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
mul rcx
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
45
stepik_base/stepik_base.code-workspace
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "sum"
|
||||
},
|
||||
{
|
||||
"path": "even_odd"
|
||||
},
|
||||
{
|
||||
"path": "repeat_str"
|
||||
},
|
||||
{
|
||||
"path": "min"
|
||||
},
|
||||
{
|
||||
"path": "is_digit"
|
||||
},
|
||||
{
|
||||
"path": "macro"
|
||||
},
|
||||
{
|
||||
"path": "less_greater_10"
|
||||
},
|
||||
{
|
||||
"path": "less_greater_10_neg"
|
||||
},
|
||||
{
|
||||
"path": "10_to_2"
|
||||
},
|
||||
{
|
||||
"path": "word_count"
|
||||
},
|
||||
{
|
||||
"path": "sum_digits"
|
||||
},
|
||||
{
|
||||
"path": "../docs"
|
||||
},
|
||||
],
|
||||
"settings": {
|
||||
"debug.allowBreakpointsEverywhere": true,
|
||||
"debug.inlineValues": "on",
|
||||
"editor.codeLens": false,
|
||||
}
|
||||
}
|
||||
21
stepik_base/sum/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/sum",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/sum",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/sum/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/sum;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/sum.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/sum;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/sum.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
154
stepik_base/sum/sum.asm
Normal file
@@ -0,0 +1,154 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 32 db 0
|
||||
buffend:
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [rel buff] ; поставить указатель на начало буфера
|
||||
call read_num
|
||||
mov rbx, rax
|
||||
inc rsi
|
||||
call read_num
|
||||
mov rdi, rax
|
||||
mov rsi, rbx
|
||||
|
||||
call calculate_sum
|
||||
call print_num_to_buf
|
||||
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция складывает два значения
|
||||
; In: RDI, RSI
|
||||
; Out: RAX
|
||||
calculate_sum:
|
||||
lea rax, [rdi + rsi]
|
||||
ret
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из stdin
|
||||
; Out: RAX
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
lea rdi, [rel buff] ; rdi - конец данных
|
||||
add rdi, [count]
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
mul rcx
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
; Функция выводит число в buff
|
||||
; In: RAX
|
||||
; Out: RSI - указатель на начало строки
|
||||
print_num_to_buf:
|
||||
lea rsi, [buffend]
|
||||
dec rsi
|
||||
|
||||
mov rdi, rsi ; сохранить последний адрес
|
||||
mov byte [rsi], 0 ; вписать нуль
|
||||
dec rsi
|
||||
mov byte [rsi], 0xA ; вписать перенос строки
|
||||
dec rsi
|
||||
mov byte [rsi], '0'
|
||||
|
||||
test rax, rax
|
||||
jz .return
|
||||
|
||||
mov rcx, 10 ; десятичный делитель
|
||||
|
||||
.while:
|
||||
xor rdx, rdx
|
||||
test rax, rax
|
||||
jz .break
|
||||
div rcx
|
||||
or dl, 0x30
|
||||
mov [rsi], dl
|
||||
dec rsi
|
||||
jmp .while
|
||||
.break:
|
||||
inc rsi
|
||||
.return:
|
||||
sub rdi, rsi
|
||||
inc rdi
|
||||
mov [count], rdi
|
||||
ret
|
||||
21
stepik_base/sum_digits/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/sum_digits",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/sum_digits",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/sum_digits/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/sum_digits;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/sum_digits.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/sum_digits;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/sum_digits.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
140
stepik_base/sum_digits/sum_digits.asm
Normal file
@@ -0,0 +1,140 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 32 db 0
|
||||
buffend:
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
lea rsi, [rel buff] ; поставить указатель на начало буфера
|
||||
call read_num
|
||||
|
||||
call print_num_to_buf
|
||||
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция читает число из stdin
|
||||
; Out: RAX
|
||||
read_num:
|
||||
push rbx
|
||||
push rcx
|
||||
xor rbx, rbx ; обнулить регистр для символов
|
||||
xor rax, rax ; обнулить аккумулирующий регистр
|
||||
mov rcx, 10 ; множитель для сдвига
|
||||
|
||||
lea rdi, [rel buff] ; rdi - конец данных
|
||||
add rdi, [count]
|
||||
|
||||
.while:
|
||||
cmp rsi, rdi ; сначала проверяем границы, чтобы не читать за пределами
|
||||
jge .return
|
||||
|
||||
mov bl, [rsi] ; прочитать текущий символ
|
||||
|
||||
cmp bl, 0xA ; если конец строки (\n) - закончить
|
||||
je .return
|
||||
cmp bl, 0 ; если null (\0) - закончить
|
||||
jz .return
|
||||
cmp bl, ' ' ; если пробел - закончить
|
||||
jz .return
|
||||
|
||||
and bl, 0x0F
|
||||
add rax, rbx
|
||||
inc rsi
|
||||
jmp .while
|
||||
|
||||
.return:
|
||||
pop rcx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
; Функция выводит число в buff
|
||||
; In: RAX
|
||||
; Out: RSI - указатель на начало строки
|
||||
print_num_to_buf:
|
||||
lea rsi, [buffend]
|
||||
dec rsi
|
||||
|
||||
mov rdi, rsi ; сохранить последний адрес
|
||||
mov byte [rsi], 0 ; вписать нуль
|
||||
dec rsi
|
||||
mov byte [rsi], 0xA ; вписать перенос строки
|
||||
dec rsi
|
||||
mov byte [rsi], '0'
|
||||
|
||||
test rax, rax
|
||||
jz .return
|
||||
|
||||
mov rcx, 10 ; десятичный делитель
|
||||
|
||||
.while:
|
||||
xor rdx, rdx
|
||||
test rax, rax
|
||||
jz .break
|
||||
div rcx
|
||||
or dl, 0x30
|
||||
mov [rsi], dl
|
||||
dec rsi
|
||||
jmp .while
|
||||
.break:
|
||||
inc rsi
|
||||
.return:
|
||||
sub rdi, rsi
|
||||
inc rdi
|
||||
mov [count], rdi
|
||||
ret
|
||||
29
stepik_base/word_count/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch x64",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/word_count",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64",
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/word_count",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"name": "(gdb+gcc) Launch x64",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/word_count",
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
stepik_base/word_count/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/word_count;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/word_count.asm;",
|
||||
"ld -g -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/word_count;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/word_count.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"focus": true,
|
||||
"panel": "dedicated",
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
129
stepik_base/word_count/word_count.asm
Normal file
@@ -0,0 +1,129 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
buff times 350 db 0
|
||||
buffend:
|
||||
count dq 0
|
||||
|
||||
section .text
|
||||
default rel
|
||||
|
||||
_start:
|
||||
call read_text
|
||||
|
||||
xor rax, rax
|
||||
mov rcx, [count]
|
||||
dec rcx
|
||||
|
||||
lea rsi, [buff]
|
||||
|
||||
mov r10, 1 ; просто единица
|
||||
mov r8, 0 ; прошлый символ - не пробел
|
||||
.loop:
|
||||
mov dl, [rsi]
|
||||
|
||||
cmp dl, ' '
|
||||
jne .continue ; если сейчас не пробел - выйти
|
||||
test r8, r8
|
||||
jz .continue ; если прошлое пробел - выйти
|
||||
|
||||
.space_after_word:
|
||||
inc rax
|
||||
.continue:
|
||||
mov r8, 0
|
||||
cmp dl, ' '
|
||||
cmovne r8, r10
|
||||
inc rsi
|
||||
loop .loop
|
||||
|
||||
test r8, r8
|
||||
jz .print ; если прошлое пробел - выйти
|
||||
inc rax
|
||||
.print:
|
||||
call print_num_to_buf
|
||||
mov rdx, [count]
|
||||
call write_text
|
||||
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
; Функция читает текст в буффер
|
||||
; Регистры не изменяет
|
||||
read_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 0 ; sys_read
|
||||
mov rdi, 0 ; stdin
|
||||
lea rsi, [rel buff] ; адрес буфера
|
||||
mov rdx, 256 ; количество байт
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
; Функция выводит buff
|
||||
; In:
|
||||
; - RSI - начало буфера
|
||||
; - RDX - размер буфера
|
||||
; Регистры не изменяет
|
||||
write_text:
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
mov rax, 1 ; sys_write
|
||||
mov rdi, 1 ; stdout
|
||||
syscall
|
||||
mov [count], rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
ret
|
||||
|
||||
|
||||
; Функция выводит число в buff
|
||||
; In: RAX
|
||||
; Out: RSI - указатель на начало строки
|
||||
print_num_to_buf:
|
||||
lea rsi, [buffend]
|
||||
dec rsi
|
||||
|
||||
mov rdi, rsi ; сохранить последний адрес
|
||||
mov byte [rsi], 0 ; вписать нуль
|
||||
dec rsi
|
||||
mov byte [rsi], 0xA ; вписать перенос строки
|
||||
dec rsi
|
||||
mov byte [rsi], '0'
|
||||
|
||||
test rax, rax
|
||||
jz .return
|
||||
|
||||
mov rcx, 10 ; десятичный делитель
|
||||
|
||||
.while:
|
||||
xor rdx, rdx
|
||||
test rax, rax
|
||||
jz .break
|
||||
div rcx
|
||||
or dl, 0x30
|
||||
mov [rsi], dl
|
||||
dec rsi
|
||||
jmp .while
|
||||
.break:
|
||||
inc rsi
|
||||
.return:
|
||||
sub rdi, rsi
|
||||
inc rdi
|
||||
mov [count], rdi
|
||||
ret
|
||||
2
wayland/.ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
**/build/
|
||||
**/out/
|
||||
37
wayland/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(lldb) Launch",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"PATH": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [
|
||||
{
|
||||
"name": "PATH",
|
||||
"value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
|
||||
},
|
||||
],
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
91
wayland/CMakeLists.txt
Normal file
@@ -0,0 +1,91 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(wayland)
|
||||
|
||||
enable_language(ASM_NASM)
|
||||
|
||||
set(CMAKE_ASM_NASM_FLAGS "-f elf64 -I${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(CMAKE_ASM_NASM_FLAGS_DEBUG "-gdwarf")
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
# Находим программу wayland-scanner
|
||||
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
|
||||
|
||||
# Ищем директорию с протоколами Wayland
|
||||
find_path(WAYLAND_PROTOCOLS_DIR
|
||||
NAMES stable/xdg-shell/xdg-shell.xml
|
||||
PATHS /usr/share/wayland-protocols /usr/local/share/wayland-protocols
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
# Путь к протоколу xdg-shell
|
||||
set(PROTOCOL_XML ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml)
|
||||
|
||||
# Генерируем заголовочный файл протокола
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.h
|
||||
COMMAND ${WAYLAND_SCANNER} client-header ${PROTOCOL_XML} ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.h
|
||||
DEPENDS ${PROTOCOL_XML}
|
||||
)
|
||||
|
||||
# Генерируем исходный файл протокола
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.c
|
||||
COMMAND ${WAYLAND_SCANNER} private-code ${PROTOCOL_XML} ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.c
|
||||
DEPENDS ${PROTOCOL_XML}
|
||||
)
|
||||
|
||||
# Цель для генерации протокола
|
||||
add_custom_target(generate-xdg-shell DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.c)
|
||||
|
||||
# Генерируем offsets.inc
|
||||
add_executable(generate-offsets ${CMAKE_CURRENT_SOURCE_DIR}/generate-offsets.c)
|
||||
target_include_directories(generate-offsets PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/offsets.inc
|
||||
COMMAND generate-offsets > ${CMAKE_CURRENT_BINARY_DIR}/offsets.inc
|
||||
DEPENDS generate-offsets
|
||||
COMMENT "Generating offsets.inc"
|
||||
)
|
||||
|
||||
add_custom_target(generate-offsets-file DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/offsets.inc)
|
||||
|
||||
# Создаем исполняемый файл из ассемблерного, C и сгенерированного кода
|
||||
file(GLOB_RECURSE WAYLAND_SOURCES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.c"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.asm"
|
||||
)
|
||||
|
||||
# Append the generated XDG C source (will be created in the binary dir)
|
||||
list(APPEND WAYLAND_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.c)
|
||||
|
||||
# Create executable from collected sources
|
||||
add_executable(wayland ${WAYLAND_SOURCES})
|
||||
|
||||
# Ensure generated files are produced before building the target
|
||||
add_dependencies(wayland generate-xdg-shell generate-offsets-file)
|
||||
|
||||
# Include headers and binary dir where generated headers are written
|
||||
target_include_directories(wayland PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${WAYLAND_CLIENT_INCLUDE_DIRS}
|
||||
${XKBCOMMON_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
|
||||
set(WAYLAND_CLIENT_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES})
|
||||
set(WAYLAND_CLIENT_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS})
|
||||
|
||||
# xkbcommon for keyboard layout handling
|
||||
pkg_check_modules(XKBCOMMON REQUIRED xkbcommon)
|
||||
set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARIES})
|
||||
set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIRS})
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Link to system libraries
|
||||
target_link_libraries(wayland PRIVATE ${WAYLAND_CLIENT_LIBRARIES} ${XKBCOMMON_LIBRARIES} Threads::Threads)
|
||||
23
wayland/CMakePresets.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": 8,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "debug-gcc",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "/usr/bin/gcc",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release-gcc",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "/usr/bin/gcc",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
wayland/docs/uml/diagram-activity-animation.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
51
wayland/docs/uml/diagram-activity-animation.puml
Normal file
@@ -0,0 +1,51 @@
|
||||
@startuml
|
||||
title Цикл анимации фигуры (aux_thread)
|
||||
|
||||
start
|
||||
|
||||
:aux_thread запущен;
|
||||
|
||||
while (окно не закрыто?) is (да)
|
||||
|
||||
:Залочить figure_mutex;
|
||||
|
||||
:figure_animation_step(draw_info);
|
||||
|
||||
partition "figure_animation_step (ASM)" {
|
||||
:Обработка коллизий;
|
||||
note right
|
||||
figure_handle_collision():
|
||||
- Расчёт точек фигуры на окружности
|
||||
- Проверка выхода за границы
|
||||
- Инверсия velocity при коллизии
|
||||
- Обновление angular_velocity
|
||||
end note
|
||||
|
||||
:Обновить позицию;
|
||||
note right
|
||||
position += (velocity * speed) / height
|
||||
end note
|
||||
|
||||
:Обновить угол поворота;
|
||||
note right
|
||||
angle += angular_velocity * speed
|
||||
end note
|
||||
|
||||
:Применить трение к вращению;
|
||||
note right
|
||||
angular_velocity *= 0.95
|
||||
end note
|
||||
}
|
||||
|
||||
:Разлочить figure_mutex;
|
||||
|
||||
:usleep(33 ms);
|
||||
note right: ~30 FPS обновление физики
|
||||
|
||||
endwhile (нет)
|
||||
|
||||
:Завершение потока;
|
||||
|
||||
stop
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-activity-render.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
76
wayland/docs/uml/diagram-activity-render.puml
Normal file
@@ -0,0 +1,76 @@
|
||||
@startuml
|
||||
title Цикл отрисовки окна (window_thread)
|
||||
|
||||
start
|
||||
|
||||
:window_thread запущен;
|
||||
|
||||
:Создать event queue;
|
||||
:window_init();
|
||||
:Запустить aux_thread для анимации;
|
||||
|
||||
while (окно не закрыто?) is (да)
|
||||
|
||||
:wl_display_dispatch_queue();
|
||||
note right: Ожидание событий Wayland
|
||||
|
||||
if (событие произошло?) then (да)
|
||||
|
||||
if (xdg_surface_configure?) then (да)
|
||||
:xdg_surface_ack_configure();
|
||||
if (буфер не создан?) then (да)
|
||||
:resize_canvas();
|
||||
endif
|
||||
:draw();
|
||||
endif
|
||||
|
||||
if (xdg_toplevel_configure?) then (да)
|
||||
if (размер изменился?) then (да)
|
||||
:resize_new(w, h);
|
||||
note right
|
||||
- Освободить старый буфер
|
||||
- Создать новый буфер
|
||||
- Обновить размеры draw_info
|
||||
end note
|
||||
endif
|
||||
endif
|
||||
|
||||
if (xdg_toplevel_close?) then (да)
|
||||
:Установить need_close = 1;
|
||||
endif
|
||||
|
||||
if (frame_callback?) then (да)
|
||||
:Уничтожить старый callback;
|
||||
:Создать новый callback;
|
||||
|
||||
partition "draw()" {
|
||||
:Залочить figure_mutex;
|
||||
|
||||
:Очистить фон (черный);
|
||||
|
||||
:figure_draw();
|
||||
note right
|
||||
- Получить текущую фигуру
|
||||
- Нарисовать границу и заливку
|
||||
- Цвет зависит от типа фигуры
|
||||
end note
|
||||
|
||||
:Разлочить figure_mutex;
|
||||
|
||||
:wl_surface_attach(buffer);
|
||||
:wl_surface_damage_buffer();
|
||||
:wl_surface_commit();
|
||||
}
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
endwhile (нет)
|
||||
|
||||
:window_destroy();
|
||||
:Уничтожить event queue;
|
||||
:signal_thread_exit();
|
||||
|
||||
stop
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-class.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
127
wayland/docs/uml/diagram-class.puml
Normal file
@@ -0,0 +1,127 @@
|
||||
@startuml
|
||||
title Структура классов проекта Wayland
|
||||
|
||||
class wayland_runtime {
|
||||
+ init_wayland() : int32_t
|
||||
+ run_window() : int32_t
|
||||
+ wait_for_windows() : void
|
||||
+ destroy_wayland() : void
|
||||
+ get_window_by_surface(surf) : wayland_window*
|
||||
--
|
||||
- g_display : wl_display*
|
||||
- g_slots[MAX_WINDOW_THREADS] : window_thread_slot
|
||||
- g_thread_lock : pthread_mutex_t
|
||||
- g_active_threads : atomic_int
|
||||
- g_shutdown : atomic_bool
|
||||
}
|
||||
|
||||
class window_thread_slot {
|
||||
+ thread : pthread_t
|
||||
+ aux_thread : pthread_t
|
||||
+ active : int
|
||||
+ queue : wl_event_queue*
|
||||
+ window : wayland_window
|
||||
}
|
||||
|
||||
class wayland_window {
|
||||
+ id : int
|
||||
+ wl_surface : wl_surface*
|
||||
+ xdg_surface : xdg_surface*
|
||||
+ xdg_toplevel : xdg_toplevel*
|
||||
+ buffer : wl_buffer*
|
||||
+ frame_callback : wl_callback*
|
||||
+ queue : wl_event_queue*
|
||||
+ need_close : int
|
||||
+ draw_info : window_draw_info
|
||||
--
|
||||
+ window_init(display, queue) : int
|
||||
+ window_destroy() : void
|
||||
+ window_should_close() : int
|
||||
}
|
||||
|
||||
class window_draw_info {
|
||||
+ width : uint32_t
|
||||
+ height : uint32_t
|
||||
+ data : void*
|
||||
+ figure : figure_animation_info
|
||||
+ figure_mutex : pthread_mutex_t
|
||||
}
|
||||
|
||||
class figure_animation_info {
|
||||
+ type : figure_type
|
||||
+ position : vec2
|
||||
+ velocity : vec2
|
||||
+ angle : float
|
||||
+ angular_velocity : float
|
||||
+ speed : float
|
||||
+ radius : float
|
||||
}
|
||||
|
||||
enum figure_type {
|
||||
FIGURE_CIRCLE = 0
|
||||
FIGURE_TRIANGLE = 1
|
||||
FIGURE_SQUARE = 2
|
||||
}
|
||||
|
||||
class registry {
|
||||
+ registry_global_bind(display) : int
|
||||
+ registry_global_unbind() : void
|
||||
+ registry_get_compositor() : wl_compositor*
|
||||
+ registry_get_shm() : wl_shm*
|
||||
+ registry_get_xdg_wm_base() : xdg_wm_base*
|
||||
--
|
||||
- global_compositor : wl_compositor*
|
||||
- global_shm : wl_shm*
|
||||
- global_wm_base : xdg_wm_base*
|
||||
- global_registry : wl_registry*
|
||||
- global_thread : pthread_t
|
||||
- global_thread_running : atomic_int
|
||||
}
|
||||
|
||||
class input {
|
||||
+ input_register_seat(seat) : void
|
||||
+ input_cleanup() : void
|
||||
--
|
||||
- seat : wl_seat*
|
||||
- keyboard : wl_keyboard*
|
||||
- xkb_ctx : xkb_context*
|
||||
- xkb_keymap : xkb_keymap*
|
||||
- xkb_state : xkb_state*
|
||||
- focused_window : wayland_window*
|
||||
}
|
||||
|
||||
class input_handle {
|
||||
+ keyboard_key_handle(kc, ks, state, window) : void
|
||||
}
|
||||
|
||||
class figure_animate <<ASM>> {
|
||||
+ figure_animation_step(draw_info) : void
|
||||
+ place_points_on_circle(...) : void
|
||||
--
|
||||
- figure_handle_collision(draw_info) : void
|
||||
- check_collision_mask(...) : uint64_t
|
||||
- sincos_f32_rbp(angle) : (sin, cos)
|
||||
}
|
||||
|
||||
class figure_draw {
|
||||
+ figure_draw(draw_info, thickness, border_color, fill_color) : void
|
||||
}
|
||||
|
||||
wayland_runtime "1" *-- "0..128" window_thread_slot
|
||||
window_thread_slot "1" *-- "1" wayland_window
|
||||
wayland_window "1" *-- "1" window_draw_info
|
||||
window_draw_info "1" *-- "1" figure_animation_info
|
||||
figure_animation_info --> figure_type
|
||||
|
||||
wayland_runtime ..> registry : uses
|
||||
wayland_runtime ..> input : uses
|
||||
wayland_window ..> registry : uses
|
||||
wayland_window ..> figure_draw : uses
|
||||
|
||||
input ..> input_handle : uses
|
||||
input --> wayland_window : focuses
|
||||
|
||||
window_thread_slot ..> figure_animate : uses (aux_thread)
|
||||
figure_draw ..> figure_animate : uses
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-component.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
74
wayland/docs/uml/diagram-component.puml
Normal file
@@ -0,0 +1,74 @@
|
||||
@startuml
|
||||
title Компоненты проекта Wayland
|
||||
|
||||
package "Wayland Application" {
|
||||
|
||||
[wayland-runtime] as Runtime
|
||||
[registry] as Registry
|
||||
[input] as Input
|
||||
[input-handle] as InputHandle
|
||||
[window] as Window
|
||||
|
||||
package "Rendering & Animation" {
|
||||
[figure-draw] as FigureDraw
|
||||
[figure-animate (ASM)] as FigureAnimate
|
||||
}
|
||||
|
||||
Runtime --> Registry : использует глобальные объекты
|
||||
Runtime --> Input : инициализирует
|
||||
Runtime --> Window : создаёт и управляет
|
||||
|
||||
Input --> InputHandle : делегирует обработку
|
||||
InputHandle --> Runtime : вызывает run_window()
|
||||
InputHandle --> Window : изменяет параметры фигуры
|
||||
|
||||
Window --> Registry : получает compositor, shm, wm_base
|
||||
Window --> FigureDraw : отрисовка фигуры
|
||||
Window --> FigureAnimate : aux_thread для анимации
|
||||
|
||||
FigureDraw --> FigureAnimate : использует place_points_on_circle
|
||||
}
|
||||
|
||||
package "System Libraries" {
|
||||
[wayland-client] as WaylandLib
|
||||
[xdg-shell-protocol] as XDGShell
|
||||
[xkbcommon] as XKB
|
||||
}
|
||||
|
||||
Registry --> WaylandLib
|
||||
Registry --> XDGShell
|
||||
Window --> WaylandLib
|
||||
Window --> XDGShell
|
||||
Input --> WaylandLib
|
||||
Input --> XKB
|
||||
|
||||
cloud "Wayland Compositor" as Compositor
|
||||
|
||||
Runtime --> Compositor : wl_display_connect()
|
||||
Registry --> Compositor : получение глобальных объектов
|
||||
Window --> Compositor : создание окон и буферов
|
||||
Input --> Compositor : получение событий клавиатуры
|
||||
|
||||
note right of Runtime
|
||||
Центральный компонент:
|
||||
- Управление жизненным циклом
|
||||
- Многопоточность (окна + анимация)
|
||||
- Синхронизация через mutex
|
||||
end note
|
||||
|
||||
note right of FigureAnimate
|
||||
Реализация на NASM:
|
||||
- Физика движения
|
||||
- Обработка коллизий
|
||||
- Математика (sin/cos)
|
||||
- SSE инструкции
|
||||
end note
|
||||
|
||||
note right of Window
|
||||
Каждое окно в отдельном потоке:
|
||||
- window_thread: отрисовка
|
||||
- aux_thread: анимация
|
||||
- Event queue для изоляции событий
|
||||
end note
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-sequence-init.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
78
wayland/docs/uml/diagram-sequence-init.puml
Normal file
@@ -0,0 +1,78 @@
|
||||
@startuml
|
||||
title Последовательность инициализации Wayland приложения
|
||||
|
||||
actor Main
|
||||
participant "wayland-runtime" as Runtime
|
||||
participant "registry" as Registry
|
||||
participant "input" as Input
|
||||
participant "window" as Window
|
||||
|
||||
Main -> Runtime: init_wayland()
|
||||
activate Runtime
|
||||
|
||||
Runtime -> Runtime: wl_display_connect()
|
||||
note right: Подключение к Wayland серверу
|
||||
|
||||
Runtime -> Registry: registry_global_bind(display)
|
||||
activate Registry
|
||||
|
||||
Registry -> Registry: wl_display_get_registry()
|
||||
Registry -> Registry: wl_registry_add_listener()
|
||||
Registry -> Registry: wl_display_roundtrip()
|
||||
note right: Получение глобальных объектов:\n- wl_compositor\n- wl_shm\n- xdg_wm_base\n- wl_seat
|
||||
|
||||
Registry -> Input: input_register_seat(seat)
|
||||
activate Input
|
||||
Input -> Input: wl_seat_add_listener()
|
||||
Input -> Input: wl_seat_get_keyboard()
|
||||
Input -> Input: xkb_context_new()
|
||||
note right: Инициализация обработки клавиатуры
|
||||
deactivate Input
|
||||
|
||||
Registry -> Registry: pthread_create(global_thread)
|
||||
note right: Запуск потока обработки\nглобальных событий
|
||||
|
||||
deactivate Registry
|
||||
|
||||
Runtime -> Runtime: Инициализация слотов окон
|
||||
Runtime -> Runtime: atomic_store(g_shutdown, 0)
|
||||
|
||||
deactivate Runtime
|
||||
|
||||
Main -> Runtime: run_window()
|
||||
activate Runtime
|
||||
|
||||
Runtime -> Runtime: Найти свободный слот
|
||||
Runtime -> Runtime: pthread_create(window_thread)
|
||||
note right: Создание потока окна
|
||||
|
||||
Runtime --> Main: slot_index
|
||||
|
||||
== Поток окна ==
|
||||
|
||||
Runtime -> Runtime: wl_display_create_queue()
|
||||
Runtime -> Window: window_init(display, queue)
|
||||
activate Window
|
||||
|
||||
Window -> Registry: registry_get_compositor()
|
||||
Window -> Registry: registry_get_shm()
|
||||
Window -> Registry: registry_get_xdg_wm_base()
|
||||
|
||||
Window -> Window: wl_compositor_create_surface()
|
||||
Window -> Window: xdg_wm_base_get_xdg_surface()
|
||||
Window -> Window: xdg_surface_get_toplevel()
|
||||
Window -> Window: Инициализация listeners
|
||||
Window -> Window: Инициализация figure_animation_info
|
||||
note right: Параметры анимации:\n- position, velocity\n- angle, angular_velocity\n- speed, radius
|
||||
|
||||
deactivate Window
|
||||
|
||||
Runtime -> Runtime: pthread_create(aux_thread)
|
||||
note right: Вспомогательный поток\nдля анимации
|
||||
|
||||
Runtime -> Runtime: wl_display_dispatch_queue()
|
||||
note right: Цикл обработки событий окна
|
||||
|
||||
deactivate Runtime
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-sequence-keyboard.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
61
wayland/docs/uml/diagram-sequence-keyboard.puml
Normal file
@@ -0,0 +1,61 @@
|
||||
@startuml
|
||||
title Обработка клавиатурного ввода
|
||||
|
||||
actor User
|
||||
participant "Wayland Server" as Server
|
||||
participant "input" as Input
|
||||
participant "input-handle" as Handler
|
||||
participant "wayland-runtime" as Runtime
|
||||
participant "window" as Window
|
||||
|
||||
User -> Server: Нажатие клавиши
|
||||
Server -> Input: keyboard_key(key, state)
|
||||
activate Input
|
||||
|
||||
Input -> Input: xkb_state_key_get_one_sym()
|
||||
note right: Преобразование keycode в keysym
|
||||
|
||||
Input -> Handler: keyboard_key_handle(kc, ks, state, window)
|
||||
activate Handler
|
||||
|
||||
alt Клавиша Enter
|
||||
Handler -> Runtime: run_window()
|
||||
note right: Создание нового окна
|
||||
else Клавиша '1'
|
||||
Handler -> Window: figure.type = CIRCLE
|
||||
else Клавиша '2'
|
||||
Handler -> Window: figure.type = TRIANGLE
|
||||
else Клавиша '3'
|
||||
Handler -> Window: figure.type = SQUARE
|
||||
else Клавиша '-'
|
||||
Handler -> Handler: pthread_mutex_lock()
|
||||
Handler -> Window: figure.speed -= 0.5
|
||||
Handler -> Handler: Ограничить [1, 30]
|
||||
Handler -> Handler: pthread_mutex_unlock()
|
||||
else Клавиша '+' или '='
|
||||
Handler -> Handler: pthread_mutex_lock()
|
||||
Handler -> Window: figure.speed += 0.5
|
||||
Handler -> Handler: Ограничить [1, 30]
|
||||
Handler -> Handler: pthread_mutex_unlock()
|
||||
else Клавиша Up
|
||||
Handler -> Handler: pthread_mutex_lock()
|
||||
Handler -> Window: figure.radius += 2.0
|
||||
Handler -> Handler: Ограничить [1, max]
|
||||
Handler -> Handler: pthread_mutex_unlock()
|
||||
else Клавиша Down
|
||||
Handler -> Handler: pthread_mutex_lock()
|
||||
Handler -> Window: figure.radius -= 2.0
|
||||
Handler -> Handler: Ограничить [1, max]
|
||||
Handler -> Handler: pthread_mutex_unlock()
|
||||
end
|
||||
|
||||
deactivate Handler
|
||||
deactivate Input
|
||||
|
||||
note over Window
|
||||
Изменения параметров фигуры
|
||||
влияют на следующий frame
|
||||
анимации и отрисовки
|
||||
end note
|
||||
|
||||
@enduml
|
||||
BIN
wayland/docs/uml/diagram-state-figure.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
62
wayland/docs/uml/diagram-state-figure.puml
Normal file
@@ -0,0 +1,62 @@
|
||||
@startuml
|
||||
title Диаграмма состояний фигуры
|
||||
|
||||
[*] --> Circle : init (по умолчанию)
|
||||
|
||||
Circle : Красный цвет (0xFFFF0000)
|
||||
Circle : radius точек = 16
|
||||
Triangle : Зелёный цвет (0xFF00FF00)
|
||||
Triangle : radius точек = 3
|
||||
Square : Синий цвет (0xFF0000FF)
|
||||
Square : radius точек = 4
|
||||
|
||||
Circle --> Triangle : Клавиша '2'
|
||||
Circle --> Square : Клавиша '3'
|
||||
|
||||
Triangle --> Circle : Клавиша '1'
|
||||
Triangle --> Square : Клавиша '3'
|
||||
|
||||
Square --> Circle : Клавиша '1'
|
||||
Square --> Triangle : Клавиша '2'
|
||||
|
||||
Circle : velocity движет позицию
|
||||
Circle : angular_velocity вращает
|
||||
Triangle : velocity движет позицию
|
||||
Triangle : angular_velocity вращает
|
||||
Square : velocity движет позицию
|
||||
Square : angular_velocity вращает
|
||||
|
||||
note right of Circle
|
||||
Общие свойства для всех состояний:
|
||||
- position (x, y)
|
||||
- velocity (vx, vy)
|
||||
- angle (угол поворота)
|
||||
- angular_velocity (скорость вращения)
|
||||
- speed (множитель скорости: 1-30)
|
||||
- radius (радиус в пикселях)
|
||||
|
||||
Управление:
|
||||
- '+'/'-': изменение speed
|
||||
- Up/Down: изменение radius
|
||||
- Enter: создать новое окно
|
||||
end note
|
||||
|
||||
state "Коллизия с границей" as Collision {
|
||||
[*] --> CheckCollision
|
||||
CheckCollision --> InvertVelocity : точка вышла за границу
|
||||
InvertVelocity --> UpdateAngularVel : инверсия компоненты velocity
|
||||
UpdateAngularVel --> CheckCollision : обновление angular_velocity
|
||||
|
||||
CheckCollision : проверка всех точек фигуры
|
||||
InvertVelocity : vel = -vel (по оси коллизии)
|
||||
UpdateAngularVel : зависит от направления движения
|
||||
}
|
||||
|
||||
Circle --> Collision : каждый frame
|
||||
Triangle --> Collision : каждый frame
|
||||
Square --> Collision : каждый frame
|
||||
Collision --> Circle
|
||||
Collision --> Triangle
|
||||
Collision --> Square
|
||||
|
||||
@enduml
|
||||
2564
wayland/docs/wayland-report.md
Normal file
30
wayland/generate-offsets.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/* generate-offsets.c
|
||||
* Генерирует offsets.inc с смещениями полей структур
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include "window.h"
|
||||
|
||||
int main(void) {
|
||||
printf("; offsets.inc — generated automatically\n");
|
||||
|
||||
// window_draw_info offsets
|
||||
printf("WDI_DATA equ %zu\n", offsetof(struct window_draw_info, data));
|
||||
printf("WDI_WIDTH equ %zu\n", offsetof(struct window_draw_info, width));
|
||||
printf("WDI_HEIGHT equ %zu\n", offsetof(struct window_draw_info, height));
|
||||
printf("WDI_FIGURE equ %zu\n", offsetof(struct window_draw_info, figure));
|
||||
printf("WDI_FIGURE_MUTEX equ %zu\n", offsetof(struct window_draw_info, figure_mutex));
|
||||
printf("\n");
|
||||
|
||||
// figure_animation_info offsets
|
||||
printf("FIG_TYPE equ %zu\n", offsetof(struct figure_animation_info, type));
|
||||
printf("FIG_POSITION equ %zu\n", offsetof(struct figure_animation_info, position));
|
||||
printf("FIG_VELOCITY equ %zu\n", offsetof(struct figure_animation_info, velocity));
|
||||
printf("FIG_ANGLE equ %zu\n", offsetof(struct figure_animation_info, angle));
|
||||
printf("FIG_ANG_VEL equ %zu\n", offsetof(struct figure_animation_info, angular_velocity));
|
||||
printf("FIG_SPEED equ %zu\n", offsetof(struct figure_animation_info, speed));
|
||||
printf("FIG_RADIUS equ %zu\n", offsetof(struct figure_animation_info, radius));
|
||||
|
||||
return 0;
|
||||
}
|
||||
9
wayland/include/figure-animate.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef FIGURE_ANIMATE_H
|
||||
#define FIGURE_ANIMATE_H
|
||||
|
||||
#include "window.h"
|
||||
|
||||
/* Провести один шаг анимации на окне */
|
||||
void figure_animation_step(struct window_draw_info* draw_info);
|
||||
|
||||
#endif
|
||||
9
wayland/include/figure-draw.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef FIGURE_DRAW_H
|
||||
#define FIGURE_DRAW_H
|
||||
|
||||
#include "window.h"
|
||||
|
||||
/* Нарисовать фигуру на холсте */
|
||||
void figure_draw(struct window_draw_info* draw_info, float border_thikness, uint32_t border_color, uint32_t fill_color);
|
||||
|
||||
#endif
|
||||
26
wayland/include/figure.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef FIGURE_H
|
||||
#define FIGURE_H
|
||||
|
||||
#include "geomerty.h"
|
||||
|
||||
enum figure_type
|
||||
{
|
||||
FIGURE_CIRCLE = 0,
|
||||
FIGURE_TRIANGLE = 1,
|
||||
FIGURE_SQUARE = 2
|
||||
};
|
||||
|
||||
struct figure_animation_info {
|
||||
enum figure_type type;
|
||||
struct vec2 position;
|
||||
struct vec2 velocity;
|
||||
|
||||
float angle;
|
||||
float angular_velocity;
|
||||
|
||||
|
||||
float speed;
|
||||
float radius;
|
||||
};
|
||||
|
||||
#endif
|
||||
9
wayland/include/geomerty.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef GEOMETRY_H
|
||||
#define GEOMETRY_H
|
||||
|
||||
struct vec2 {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
#endif
|
||||
10
wayland/include/input-handle.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef INPUT_HANDLE_H
|
||||
#define INPUT_HANDLE_H
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include "window.h"
|
||||
#include "input.h"
|
||||
|
||||
void keyboard_key_handle(xkb_keycode_t kc, xkb_keysym_t ks, enum keyboard_key_state state, struct wayland_window* window);
|
||||
|
||||
#endif
|
||||
26
wayland/include/input.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef WAYLAND_INPUT_H
|
||||
#define WAYLAND_INPUT_H
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
/* Вызывается при привязке wl_seat регистром */
|
||||
void input_register_seat(struct wl_seat *seat);
|
||||
/* Очистка input (разрушить keyboard и xkb state) */
|
||||
void input_cleanup(void);
|
||||
|
||||
enum keyboard_key_state {
|
||||
/**
|
||||
* key is not pressed
|
||||
*/
|
||||
KEYBOARD_KEY_STATE_RELEASED = 0,
|
||||
/**
|
||||
* key is pressed
|
||||
*/
|
||||
KEYBOARD_KEY_STATE_PRESSED = 1,
|
||||
/**
|
||||
* key was repeated
|
||||
*/
|
||||
KEYBOARD_KEY_STATE_REPEATED = 2,
|
||||
};
|
||||
|
||||
#endif
|
||||
14
wayland/include/registry.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef WAYLAND_REGISTRY_H
|
||||
#define WAYLAND_REGISTRY_H
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
int registry_global_bind(struct wl_display *display);
|
||||
void registry_global_unbind(void);
|
||||
|
||||
/* Доступ к привязанным глобальным объектам */
|
||||
struct wl_compositor *registry_get_compositor(void);
|
||||
struct wl_shm *registry_get_shm(void);
|
||||
struct xdg_wm_base *registry_get_xdg_wm_base(void);
|
||||
|
||||
#endif
|
||||
21
wayland/include/wayland-runtime.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef WAYLAND_RUNTIME_H
|
||||
#define WAYLAND_RUNTIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Инициализация общего соединения Wayland и подготовка глобальных данных */
|
||||
int32_t init_wayland(void);
|
||||
|
||||
/* Создать поток для окна; вернуть индекс слота или отрицательное при ошибке */
|
||||
int32_t run_window(void);
|
||||
|
||||
/* Блокировать до завершения всех оконных потоков */
|
||||
void wait_for_windows(void);
|
||||
|
||||
/* Остановить оконные потоки, очистить input и закрыть соединение Wayland */
|
||||
void destroy_wayland(void);
|
||||
|
||||
/* Поиск окна по wl_surface */
|
||||
struct wayland_window* get_window_by_surface(struct wl_surface* surf);
|
||||
|
||||
#endif
|
||||
39
wayland/include/window.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef WAYLAND_WINDOW_H
|
||||
#define WAYLAND_WINDOW_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <wayland-client.h>
|
||||
#include "figure.h"
|
||||
|
||||
struct window_draw_info {
|
||||
uint8_t *data;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
struct figure_animation_info figure;
|
||||
pthread_mutex_t figure_mutex;
|
||||
};
|
||||
|
||||
/* Данные одного Wayland-окна (одна поверхность) */
|
||||
struct wayland_window {
|
||||
int id;
|
||||
struct wl_surface *wl_surface;
|
||||
struct wl_buffer *buffer;
|
||||
struct wl_callback *frame_callback;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct wl_event_queue *queue; /* очередь событий для окна */
|
||||
struct window_draw_info draw_info;
|
||||
int need_close;
|
||||
};
|
||||
|
||||
|
||||
/* Инициализация окна; структура предоставляется вызывающим */
|
||||
int window_init(struct wl_display *display, struct wl_event_queue *queue, struct wayland_window *win);
|
||||
|
||||
/* Нечётное значение — окно запросило закрытие */
|
||||
int window_should_close(struct wayland_window *win);
|
||||
|
||||
/* Уничтожить окно и связанные ресурсы */
|
||||
void window_destroy(struct wayland_window *win);
|
||||
|
||||
#endif
|
||||
23
wayland/src/asm.asm
Normal file
@@ -0,0 +1,23 @@
|
||||
global main
|
||||
extern init_wayland
|
||||
extern run_window
|
||||
extern wait_for_windows
|
||||
extern destroy_wayland
|
||||
|
||||
section .text
|
||||
main:
|
||||
enter 0, 0
|
||||
|
||||
call init_wayland
|
||||
cmp eax, 0
|
||||
jl .shutdown
|
||||
|
||||
; Launch the first window thread (duplicate calls for more windows)
|
||||
call run_window
|
||||
|
||||
call wait_for_windows
|
||||
|
||||
.shutdown:
|
||||
call destroy_wayland
|
||||
leave
|
||||
ret
|
||||
616
wayland/src/figure-animate.asm
Normal file
@@ -0,0 +1,616 @@
|
||||
; Макрос для локальных переменных
|
||||
%macro local 2
|
||||
%assign __local_offset __local_offset - %2
|
||||
%define %1 (__local_offset)
|
||||
%endmacro
|
||||
|
||||
; Подключаем автоматически сгенерированные offsets из C структур
|
||||
%include "offsets.inc"
|
||||
|
||||
section .rodata
|
||||
PI: dd 3.1415926
|
||||
TWO_PI: dd 6.2831852
|
||||
NEG_ONE_CONST: dd -1.0
|
||||
ONE_CONST: dd 1.0
|
||||
ZERO_CONST: dd 0.0
|
||||
ABS_MASK: dd 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff
|
||||
ANG_COLLIDE_COEF: dd 0.2
|
||||
ANG_BOOST_FACTOR: dd 0.01
|
||||
; Скорость углового обновления (можно регулировать независимо от FIG_SPEED)
|
||||
ANG_SPEED: dd 15.0
|
||||
ANG_MAX: dd 0.03
|
||||
ANG_SWITCH_FACTOR: dd 0.2
|
||||
ANG_MAX_DELTA: dd 0.006
|
||||
ANG_FRICTION: dd 0.94
|
||||
|
||||
section .text
|
||||
|
||||
; void figure_animation_step(struct window_draw_info* draw_info);
|
||||
; Параметры:
|
||||
; rdi - указатель на struct window_draw_info
|
||||
%assign __local_offset 0
|
||||
global figure_animation_step
|
||||
figure_animation_step:
|
||||
enter 0, 0
|
||||
|
||||
; Отработка коллизий
|
||||
push rdi
|
||||
call figure_handle_collision
|
||||
pop rdi
|
||||
|
||||
; Вычислить нормализующий коэффициент: 1.0 / height_pixels
|
||||
cvtsi2ss xmm4, dword [rdi + WDI_HEIGHT]
|
||||
movss xmm5, [rel ONE_CONST]
|
||||
divss xmm5, xmm4 ; xmm5 = 1.0 / height
|
||||
|
||||
; Обновить позицию: position += (velocity * speed) / height_pixels
|
||||
; pos_x += (vel_x * speed) / height
|
||||
movss xmm0, [rdi + WDI_FIGURE + FIG_VELOCITY]
|
||||
mulss xmm0, [rdi + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm0, xmm5 ; нормализовать скорость
|
||||
addss xmm0, [rdi + WDI_FIGURE + FIG_POSITION]
|
||||
movss [rdi + WDI_FIGURE + FIG_POSITION], xmm0
|
||||
|
||||
; pos_y += (vel_y * speed) / height
|
||||
movss xmm0, [rdi + WDI_FIGURE + FIG_VELOCITY + 4]
|
||||
mulss xmm0, [rdi + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm0, xmm5 ; нормализовать скорость
|
||||
addss xmm0, [rdi + WDI_FIGURE + FIG_POSITION + 4]
|
||||
movss [rdi + WDI_FIGURE + FIG_POSITION + 4], xmm0
|
||||
|
||||
; Обновить угол: angle += angular_velocity * ANG_SPEED (локальная константа)
|
||||
movss xmm0, [rdi + WDI_FIGURE + FIG_ANG_VEL]
|
||||
mulss xmm0, [rel ANG_SPEED]
|
||||
addss xmm0, [rdi + WDI_FIGURE + FIG_ANGLE]
|
||||
movss [rdi + WDI_FIGURE + FIG_ANGLE], xmm0
|
||||
|
||||
; Apply angular friction to slow down rotation over time
|
||||
movss xmm0, [rdi + WDI_FIGURE + FIG_ANG_VEL]
|
||||
mulss xmm0, [rel ANG_FRICTION]
|
||||
movss [rdi + WDI_FIGURE + FIG_ANG_VEL], xmm0
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
; Функция для обработки коллизии, изменяет velocity при обнаружении коллизии с границами
|
||||
; Параметры:
|
||||
; rdi - указатель на struct window_draw_info
|
||||
%assign __local_offset 0
|
||||
figure_handle_collision:
|
||||
enter 128,0
|
||||
|
||||
local point_buffer, 128
|
||||
|
||||
; Сохранить регистры
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov r12, rdi
|
||||
|
||||
; Нормализовать радиус: radius_normalized = radius_pixels / height_pixels
|
||||
movss xmm3, [r12 + WDI_FIGURE + FIG_RADIUS]
|
||||
cvtsi2ss xmm4, dword [r12 + WDI_HEIGHT]
|
||||
divss xmm3, xmm4
|
||||
|
||||
; Вызов place_points_on_circle
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_POSITION]
|
||||
movss xmm1, [r12 + WDI_FIGURE + FIG_POSITION + 4]
|
||||
movss xmm2, [r12 + WDI_FIGURE + FIG_ANGLE] ; смещение угла = 0
|
||||
mulss xmm2, [rel NEG_ONE_CONST]
|
||||
|
||||
; Установка правильного количества точек
|
||||
mov eax, dword [r12 + WDI_FIGURE + FIG_TYPE]
|
||||
cmp eax, 1 ; FIGURE_TRIANGLE
|
||||
je .figure_triangle
|
||||
cmp eax, 2 ; FIGURE_SQUARE
|
||||
je .figure_square
|
||||
; default (FIGURE_CIRCLE and others): 16 points
|
||||
mov rsi, 16
|
||||
jmp .figure_points_done
|
||||
.figure_triangle:
|
||||
mov rsi, 3
|
||||
jmp .figure_points_done
|
||||
.figure_square:
|
||||
mov rsi, 4
|
||||
.figure_points_done:
|
||||
|
||||
lea rdi, [rbp + point_buffer]
|
||||
call place_points_on_circle
|
||||
|
||||
; Вычислить canvas_width = width_pixels / height_pixels
|
||||
cvtsi2ss xmm0, dword [r12 + WDI_WIDTH]
|
||||
cvtsi2ss xmm1, dword [r12 + WDI_HEIGHT]
|
||||
divss xmm0, xmm1
|
||||
movss xmm14, xmm0 ; сохраняем canvas_width в xmm14
|
||||
movss xmm13, [rel ONE_CONST] ; canvas_height = 1.0 в xmm13
|
||||
|
||||
; Инициализация: r14 = маска коллизий (OR всех точек), r15 = указатель на текущую точку
|
||||
xor r14, r14
|
||||
lea r15, [rbp + point_buffer]
|
||||
mov rcx, rsi
|
||||
|
||||
.point_check:
|
||||
; Загрузить координаты точки
|
||||
movss xmm2, [r15] ; x
|
||||
movss xmm3, [r15 + 4] ; y
|
||||
|
||||
; Вызвать check_collision_mask(canvas_width, canvas_height, x, y)
|
||||
movss xmm0, xmm14 ; width
|
||||
movss xmm1, xmm13 ; height = 1.0
|
||||
push rcx
|
||||
call check_collision_mask
|
||||
pop rcx
|
||||
|
||||
; Объединить маску коллизий
|
||||
or r14, rax
|
||||
|
||||
; Перейти к следующей точке
|
||||
add r15, 8
|
||||
loop .point_check
|
||||
|
||||
; Проверить, были ли коллизии
|
||||
test r14, r14
|
||||
jz .no_collision
|
||||
|
||||
; -----------------------
|
||||
; Обновить угловую скорость при коллизии
|
||||
; Формула: delta = |relevant_velocity| * speed * ANG_COLLIDE_COEF
|
||||
; Знак delta зависит от границы и направления движения.
|
||||
; Если итоговое направление совпадает с текущим - даём небольшой буст.
|
||||
; -----------------------
|
||||
|
||||
; Сохраняем старую угловую скорость и обнуляем суммарный эффект
|
||||
movss xmm6, [r12 + WDI_FIGURE + FIG_ANG_VEL] ; old ang vel
|
||||
xorps xmm7, xmm7 ; total delta
|
||||
|
||||
; LEFT (bit 0): use vertical motion (vel_y)
|
||||
test r14, 0x1
|
||||
jz .skip_left_ang
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY + 4] ; vel_y
|
||||
movss xmm1, xmm0
|
||||
; Broadcast ABS_MASK (0x7fffffff) into xmm2 and AND to get abs
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2 ; abs(vel_y)
|
||||
mulss xmm1, [r12 + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm1, [rel ANG_COLLIDE_COEF]
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
jb .left_up_ang ; vel_y < 0 -> moving UP
|
||||
; moving DOWN -> clockwise (+)
|
||||
addss xmm7, xmm1
|
||||
jmp .skip_left_ang
|
||||
.left_up_ang:
|
||||
; moving UP -> anticlockwise (-)
|
||||
movss xmm2, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm2
|
||||
addss xmm7, xmm1
|
||||
.skip_left_ang:
|
||||
|
||||
; RIGHT (bit 2): use vertical motion (vel_y)
|
||||
test r14, 0x4
|
||||
jz .skip_right_ang
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY + 4] ; vel_y
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2
|
||||
mulss xmm1, [r12 + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm1, [rel ANG_COLLIDE_COEF]
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
jb .right_up_ang ; vel_y < 0 -> moving UP
|
||||
; moving DOWN -> anticlockwise (-)
|
||||
movss xmm2, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm2
|
||||
addss xmm7, xmm1
|
||||
jmp .skip_right_ang
|
||||
.right_up_ang:
|
||||
; moving UP -> clockwise (+)
|
||||
addss xmm7, xmm1
|
||||
.skip_right_ang:
|
||||
|
||||
; TOP (bit 1): use horizontal motion (vel_x)
|
||||
test r14, 0x2
|
||||
jz .skip_top_ang
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY] ; vel_x
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2
|
||||
mulss xmm1, [r12 + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm1, [rel ANG_COLLIDE_COEF]
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
ja .top_right_ang ; vel_x > 0 -> moving RIGHT
|
||||
; moving LEFT -> clockwise (+)
|
||||
addss xmm7, xmm1
|
||||
jmp .skip_top_ang
|
||||
.top_right_ang:
|
||||
; moving RIGHT -> anticlockwise (-)
|
||||
movss xmm2, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm2
|
||||
addss xmm7, xmm1
|
||||
.skip_top_ang:
|
||||
|
||||
; BOTTOM (bit 3): use horizontal motion (vel_x)
|
||||
test r14, 0x8
|
||||
jz .skip_bottom_ang
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY] ; vel_x
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2
|
||||
mulss xmm1, [r12 + WDI_FIGURE + FIG_SPEED]
|
||||
mulss xmm1, [rel ANG_COLLIDE_COEF]
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
ja .bottom_right_ang ; vel_x > 0 -> moving RIGHT
|
||||
; moving LEFT -> anticlockwise (-)
|
||||
movss xmm2, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm2
|
||||
addss xmm7, xmm1
|
||||
jmp .skip_bottom_ang
|
||||
.bottom_right_ang:
|
||||
; moving RIGHT -> clockwise (+)
|
||||
addss xmm7, xmm1
|
||||
.skip_bottom_ang:
|
||||
|
||||
; Если суммарный эффект нулевой - ничего не делаем
|
||||
ucomiss xmm7, [rel ZERO_CONST]
|
||||
je .ang_no_change
|
||||
|
||||
; Invert direction rules to match drawing coordinate system
|
||||
; (User requested flip — so we reverse sign of computed delta)
|
||||
movss xmm0, [rel NEG_ONE_CONST]
|
||||
mulss xmm7, xmm0
|
||||
|
||||
; Decide: same direction or switch sign
|
||||
ucomiss xmm6, [rel ZERO_CONST]
|
||||
jb .old_neg_dir
|
||||
; old >= 0
|
||||
ucomiss xmm7, [rel ZERO_CONST]
|
||||
jae .same_dir
|
||||
jmp .switch_dir
|
||||
.old_neg_dir:
|
||||
; old < 0
|
||||
ucomiss xmm7, [rel ZERO_CONST]
|
||||
jb .same_dir
|
||||
jmp .switch_dir
|
||||
|
||||
; If same direction -> boost and add
|
||||
.same_dir:
|
||||
mulss xmm7, [rel ANG_BOOST_FACTOR]
|
||||
; Clamp delta magnitude to ANG_MAX_DELTA
|
||||
movss xmm0, xmm7
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2 ; xmm1 = abs(delta)
|
||||
movss xmm3, [rel ANG_MAX_DELTA]
|
||||
ucomiss xmm1, xmm3
|
||||
ja .cap_delta_same
|
||||
jmp .after_cap_same
|
||||
.cap_delta_same:
|
||||
; Set abs(delta) = ANG_MAX_DELTA, preserve sign
|
||||
movss xmm1, [rel ANG_MAX_DELTA]
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
jae .cap_delta_same_pos
|
||||
movss xmm4, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm4
|
||||
.cap_delta_same_pos:
|
||||
movss xmm7, xmm1
|
||||
.after_cap_same:
|
||||
addss xmm6, xmm7
|
||||
jmp .finish_dir_logic
|
||||
|
||||
; Switch sign -> compute new magnitude using old magnitude and delta
|
||||
.switch_dir:
|
||||
; xmm6 = old, xmm7 = delta
|
||||
; abs_old = abs(xmm6)
|
||||
movss xmm0, xmm6
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2 ; xmm1 = abs_old
|
||||
|
||||
; abs_delta = abs(xmm7)
|
||||
movss xmm3, xmm7
|
||||
movss xmm4, xmm3
|
||||
andps xmm4, xmm2 ; xmm4 = abs_delta
|
||||
; Clamp abs_delta
|
||||
movss xmm5, [rel ANG_MAX_DELTA]
|
||||
ucomiss xmm4, xmm5
|
||||
ja .cap_delta_switch
|
||||
jmp .delta_not_capped
|
||||
.cap_delta_switch:
|
||||
movss xmm4, xmm5
|
||||
.delta_not_capped:
|
||||
|
||||
; abs_old *= ANG_SWITCH_FACTOR
|
||||
movss xmm5, [rel ANG_SWITCH_FACTOR]
|
||||
mulss xmm1, xmm5
|
||||
|
||||
; new_mag = abs_old + abs_delta
|
||||
addss xmm1, xmm4
|
||||
|
||||
; apply sign of delta (xmm7)
|
||||
ucomiss xmm7, [rel ZERO_CONST]
|
||||
jae .switch_positive
|
||||
; negative
|
||||
movss xmm5, [rel NEG_ONE_CONST]
|
||||
mulss xmm1, xmm5
|
||||
movss xmm6, xmm1
|
||||
jmp .finish_dir_logic
|
||||
.switch_positive:
|
||||
movss xmm6, xmm1
|
||||
|
||||
.finish_dir_logic:
|
||||
; Clamp angular velocity: |xmm6| <= ANG_MAX
|
||||
movss xmm0, xmm6
|
||||
movss xmm1, xmm0
|
||||
mov eax, dword [rel ABS_MASK]
|
||||
movd xmm2, eax
|
||||
pshufd xmm2, xmm2, 0x0
|
||||
andps xmm1, xmm2 ; xmm1 = abs(xmm0)
|
||||
movss xmm2, [rel ANG_MAX]
|
||||
ucomiss xmm1, xmm2
|
||||
ja .ang_clamp_needed
|
||||
movss [r12 + WDI_FIGURE + FIG_ANG_VEL], xmm6
|
||||
jmp .ang_no_change2
|
||||
.ang_clamp_needed:
|
||||
; If xmm0 >= 0 -> set +ANG_MAX else set -ANG_MAX
|
||||
ucomiss xmm0, [rel ZERO_CONST]
|
||||
jae .ang_positive_clamp
|
||||
; negative
|
||||
movss xmm3, [rel ANG_MAX]
|
||||
movss xmm4, [rel NEG_ONE_CONST]
|
||||
mulss xmm3, xmm4
|
||||
movss xmm6, xmm3
|
||||
movss [r12 + WDI_FIGURE + FIG_ANG_VEL], xmm6
|
||||
jmp .ang_no_change2
|
||||
.ang_positive_clamp:
|
||||
movss xmm3, [rel ANG_MAX]
|
||||
movss xmm6, xmm3
|
||||
movss [r12 + WDI_FIGURE + FIG_ANG_VEL], xmm6
|
||||
.ang_no_change2:
|
||||
.ang_no_change:
|
||||
|
||||
; Обработка коллизий: инвертировать velocity только если движемся к границе
|
||||
|
||||
; Обработка velocity
|
||||
; Проверка left (bit 0): инвертировать velocity.x только если vel.x < 0
|
||||
test r14, 0x1
|
||||
jz .check_right
|
||||
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jae .check_right ; если vel.x >= 0, пропускаем
|
||||
|
||||
; vel.x < 0, инвертируем
|
||||
movss xmm1, [rel NEG_ONE_CONST]
|
||||
mulss xmm0, xmm1
|
||||
movss [r12 + WDI_FIGURE + FIG_VELOCITY], xmm0
|
||||
|
||||
.check_right:
|
||||
; Проверка right (bit 2): инвертировать velocity.x только если vel.x > 0
|
||||
test r14, 0x4
|
||||
jz .check_top
|
||||
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jbe .check_top ; если vel.x <= 0, пропускаем
|
||||
|
||||
; vel.x > 0, инвертируем
|
||||
movss xmm1, [rel NEG_ONE_CONST]
|
||||
mulss xmm0, xmm1
|
||||
movss [r12 + WDI_FIGURE + FIG_VELOCITY], xmm0
|
||||
|
||||
.check_top:
|
||||
; Проверка top (bit 1): инвертировать velocity.y только если vel.y < 0
|
||||
test r14, 0x2
|
||||
jz .check_bottom
|
||||
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY + 4]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jae .check_bottom ; если vel.y >= 0, пропускаем
|
||||
|
||||
; vel.y < 0, инвертируем
|
||||
movss xmm1, [rel NEG_ONE_CONST]
|
||||
mulss xmm0, xmm1
|
||||
movss [r12 + WDI_FIGURE + FIG_VELOCITY + 4], xmm0
|
||||
|
||||
.check_bottom:
|
||||
; Проверка bottom (bit 3): инвертировать velocity.y только если vel.y > 0
|
||||
test r14, 0x8
|
||||
jz .no_collision
|
||||
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_VELOCITY + 4]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jbe .no_collision ; если vel.y <= 0, пропускаем
|
||||
|
||||
; vel.y > 0, инвертируем
|
||||
movss xmm1, [rel NEG_ONE_CONST]
|
||||
mulss xmm0, xmm1
|
||||
movss [r12 + WDI_FIGURE + FIG_VELOCITY + 4], xmm0
|
||||
|
||||
.no_collision:
|
||||
; Костыль: если центр фигуры вышел за границу, вернуть его на границу
|
||||
|
||||
; Проверка pos_x < 0
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_POSITION]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jae .check_pos_x_max
|
||||
movss [r12 + WDI_FIGURE + FIG_POSITION], xmm1 ; pos_x = 0
|
||||
|
||||
.check_pos_x_max:
|
||||
; Проверка pos_x > canvas_width
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_POSITION]
|
||||
ucomiss xmm0, xmm14
|
||||
jbe .check_pos_y_min
|
||||
movss [r12 + WDI_FIGURE + FIG_POSITION], xmm14 ; pos_x = canvas_width
|
||||
|
||||
.check_pos_y_min:
|
||||
; Проверка pos_y < 0
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_POSITION + 4]
|
||||
movss xmm1, [rel ZERO_CONST]
|
||||
ucomiss xmm0, xmm1
|
||||
jae .check_pos_y_max
|
||||
movss [r12 + WDI_FIGURE + FIG_POSITION + 4], xmm1 ; pos_y = 0
|
||||
|
||||
.check_pos_y_max:
|
||||
; Проверка pos_y > canvas_height (1.0)
|
||||
movss xmm0, [r12 + WDI_FIGURE + FIG_POSITION + 4]
|
||||
ucomiss xmm0, xmm13
|
||||
jbe .position_clamped
|
||||
movss [r12 + WDI_FIGURE + FIG_POSITION + 4], xmm13 ; pos_y = 1.0
|
||||
|
||||
.position_clamped:
|
||||
; Восстановить регистры
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
|
||||
; Функция для расположения точек на окружности
|
||||
; Вход:
|
||||
; xmm0 - pos_x
|
||||
; xmm1 - pos_y
|
||||
; xmm2 - смещение точек на окружности в радианах
|
||||
; xmm3 - радиус
|
||||
; rdi - адрес буфера для точек
|
||||
; rsi - количество точек
|
||||
; Уничтожает: rax, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7
|
||||
global place_points_on_circle
|
||||
place_points_on_circle:
|
||||
enter 0, 0
|
||||
|
||||
; Сохранить координаты центра
|
||||
movss xmm6, xmm0
|
||||
movss xmm7, xmm1
|
||||
|
||||
; Рассчитать TWO_PI / rsi и сохранить в xmm4
|
||||
movss xmm4, [rel TWO_PI]
|
||||
cvtsi2ss xmm5, rsi
|
||||
divss xmm4, xmm5
|
||||
|
||||
movss xmm5, [rel ZERO_CONST] ; счётчик
|
||||
mov rcx, rsi
|
||||
.loop:
|
||||
movss xmm0, xmm5
|
||||
|
||||
mulss xmm0, xmm4 ; Счётчик*шаг
|
||||
addss xmm0, xmm2 ; Прибавить смещение
|
||||
call sincos_f32_rbp ; Посчитать sincos
|
||||
mulss xmm0, xmm3 ; sin *= radius
|
||||
mulss xmm1, xmm3 ; cos *= radius
|
||||
|
||||
addss xmm1, xmm6 ; x = center_x + cos*radius
|
||||
addss xmm0, xmm7 ; y = center_y + sin*radius
|
||||
|
||||
movss [rdi], xmm1
|
||||
movss [rdi + 4], xmm0
|
||||
add rdi, 8
|
||||
|
||||
|
||||
addss xmm5, [rel ONE_CONST]
|
||||
loop .loop
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
|
||||
; Функция для рассчёта sin и cos
|
||||
; Вход:
|
||||
; xmm0 - угол в радианах (float)
|
||||
; Выход:
|
||||
; xmm0 - sin(angle)
|
||||
; xmm1 - cos(angle)
|
||||
; Уничтожает: rax, flags
|
||||
sincos_f32_rbp:
|
||||
enter 24, 0 ; 24 байта локального места:
|
||||
; [rbp-8] — временно угол (для fld)
|
||||
; [rbp-16] — sin
|
||||
; [rbp-24] — cos
|
||||
; (выравнивание по 16 будет соблюдено за счёт enter)
|
||||
|
||||
; Сохраняем входной угол как float32 в стек для загрузки в x87
|
||||
movss [rbp-8], xmm0
|
||||
fld dword [rbp-8] ; ST(0) = angle (в extended precision)
|
||||
|
||||
fsincos ; ST(0) = cos, ST(1) = sin
|
||||
|
||||
; Сохраняем результаты обратно в память как float32
|
||||
fstp dword [rbp-24] ; pop cos → [rbp-24]
|
||||
fstp dword [rbp-16] ; pop sin → [rbp-16]
|
||||
|
||||
; Загружаем результаты в xmm0 и xmm1
|
||||
movss xmm0, [rbp-16] ; xmm0 = sin
|
||||
movss xmm1, [rbp-24] ; xmm1 = cos
|
||||
|
||||
leave ; эквивалент: mov rsp, rbp / pop rbp
|
||||
ret
|
||||
|
||||
|
||||
; Функция проверки выхода за границы с маской
|
||||
; Вход:
|
||||
; xmm0 - width
|
||||
; xmm1 - height
|
||||
; xmm2 - x
|
||||
; xmm3 - y
|
||||
; Выход:
|
||||
; rax - битовая маска границ (left=1, top=2, right=4, bottom=8)
|
||||
check_collision_mask:
|
||||
xor rax, rax ; очистим rax (маска)
|
||||
|
||||
movss xmm4, [rel ZERO_CONST]
|
||||
|
||||
; left: x < 0
|
||||
ucomiss xmm2, xmm4
|
||||
jb .set_left
|
||||
|
||||
.next_left:
|
||||
; right: x > width
|
||||
ucomiss xmm2, xmm0
|
||||
ja .set_right
|
||||
|
||||
.next_right:
|
||||
; top: y < 0
|
||||
ucomiss xmm3, xmm4
|
||||
jb .set_top
|
||||
|
||||
.next_top:
|
||||
; bottom: y > height
|
||||
ucomiss xmm3, xmm1
|
||||
ja .set_bottom
|
||||
|
||||
ret
|
||||
|
||||
.set_left:
|
||||
or rax, 1
|
||||
jmp .next_left
|
||||
|
||||
.set_top:
|
||||
or rax, 2
|
||||
jmp .next_top
|
||||
|
||||
.set_right:
|
||||
or rax, 4
|
||||
jmp .next_right
|
||||
|
||||
.set_bottom:
|
||||
or rax, 8
|
||||
ret
|
||||
239
wayland/src/figure-draw.c
Normal file
@@ -0,0 +1,239 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "figure-draw.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Вспомогательная функция для установки пикселя */
|
||||
static inline void set_pixel(uint8_t *data, int32_t width, int32_t height, int x, int y, uint32_t color)
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height)
|
||||
return;
|
||||
|
||||
uint32_t *pixel = (uint32_t *)(data + (y * width + x) * 4);
|
||||
*pixel = color;
|
||||
}
|
||||
|
||||
/* Проверка, находится ли точка внутри треугольника (барицентрические координаты) */
|
||||
static int point_in_triangle(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3)
|
||||
{
|
||||
float d1 = (px - x2) * (y1 - y2) - (x1 - x2) * (py - y2);
|
||||
float d2 = (px - x3) * (y2 - y3) - (x2 - x3) * (py - y3);
|
||||
float d3 = (px - x1) * (y3 - y1) - (x3 - x1) * (py - y1);
|
||||
|
||||
int has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
|
||||
int has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
|
||||
|
||||
return !(has_neg && has_pos);
|
||||
}
|
||||
|
||||
/* Расстояние от точки до отрезка */
|
||||
static float point_to_segment_distance(float px, float py, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
float len_sq = dx * dx + dy * dy;
|
||||
|
||||
if (len_sq < 0.0001f) {
|
||||
dx = px - x1;
|
||||
dy = py - y1;
|
||||
return sqrtf(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
float t = ((px - x1) * dx + (py - y1) * dy) / len_sq;
|
||||
t = fmaxf(0.0f, fminf(1.0f, t));
|
||||
|
||||
float proj_x = x1 + t * dx;
|
||||
float proj_y = y1 + t * dy;
|
||||
|
||||
dx = px - proj_x;
|
||||
dy = py - proj_y;
|
||||
|
||||
return sqrtf(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/* Рисование круга */
|
||||
static void draw_circle(struct window_draw_info* draw_info, float cx, float cy, float radius,
|
||||
float border_thickness, uint32_t border_color, uint32_t fill_color)
|
||||
{
|
||||
int x_min = (int)fmaxf(0, cx - radius - border_thickness);
|
||||
int x_max = (int)fminf(draw_info->width - 1, cx + radius + border_thickness);
|
||||
int y_min = (int)fmaxf(0, cy - radius - border_thickness);
|
||||
int y_max = (int)fminf(draw_info->height - 1, cy + radius + border_thickness);
|
||||
|
||||
for (int y = y_min; y <= y_max; y++) {
|
||||
for (int x = x_min; x <= x_max; x++) {
|
||||
float dx = x - cx;
|
||||
float dy = y - cy;
|
||||
float dist = sqrtf(dx * dx + dy * dy);
|
||||
|
||||
if (dist <= radius) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
|
||||
} else if (dist <= radius + border_thickness) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Рисование треугольника */
|
||||
static void draw_triangle(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle,
|
||||
float border_thickness, uint32_t border_color, uint32_t fill_color)
|
||||
{
|
||||
/* Вычисляем координаты вершин равностороннего треугольника */
|
||||
/* Угол 0 означает, что одна вершина справа от центра */
|
||||
float vertices[3][2];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float vertex_angle = angle + i * (2.0f * M_PI / 3.0f);
|
||||
vertices[i][0] = cx + radius * cosf(vertex_angle);
|
||||
vertices[i][1] = cy - radius * sinf(vertex_angle); /* Инвертируем Y для экранных координат */
|
||||
}
|
||||
|
||||
/* Находим ограничивающий прямоугольник */
|
||||
float min_x = fminf(vertices[0][0], fminf(vertices[1][0], vertices[2][0])) - border_thickness;
|
||||
float max_x = fmaxf(vertices[0][0], fmaxf(vertices[1][0], vertices[2][0])) + border_thickness;
|
||||
float min_y = fminf(vertices[0][1], fminf(vertices[1][1], vertices[2][1])) - border_thickness;
|
||||
float max_y = fmaxf(vertices[0][1], fmaxf(vertices[1][1], vertices[2][1])) + border_thickness;
|
||||
|
||||
int x_min = (int)fmaxf(0, min_x);
|
||||
int x_max = (int)fminf(draw_info->width - 1, max_x);
|
||||
int y_min = (int)fmaxf(0, min_y);
|
||||
int y_max = (int)fminf(draw_info->height - 1, max_y);
|
||||
|
||||
/* Рисуем треугольник */
|
||||
for (int y = y_min; y <= y_max; y++) {
|
||||
for (int x = x_min; x <= x_max; x++) {
|
||||
int inside = point_in_triangle((float)x, (float)y,
|
||||
vertices[0][0], vertices[0][1],
|
||||
vertices[1][0], vertices[1][1],
|
||||
vertices[2][0], vertices[2][1]);
|
||||
|
||||
if (inside) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
|
||||
} else {
|
||||
/* Проверяем расстояние до границ */
|
||||
float dist1 = point_to_segment_distance((float)x, (float)y,
|
||||
vertices[0][0], vertices[0][1],
|
||||
vertices[1][0], vertices[1][1]);
|
||||
float dist2 = point_to_segment_distance((float)x, (float)y,
|
||||
vertices[1][0], vertices[1][1],
|
||||
vertices[2][0], vertices[2][1]);
|
||||
float dist3 = point_to_segment_distance((float)x, (float)y,
|
||||
vertices[2][0], vertices[2][1],
|
||||
vertices[0][0], vertices[0][1]);
|
||||
|
||||
float min_dist = fminf(dist1, fminf(dist2, dist3));
|
||||
if (min_dist <= border_thickness) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Рисование квадрата */
|
||||
static void draw_square(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle,
|
||||
float border_thickness, uint32_t border_color, uint32_t fill_color)
|
||||
{
|
||||
/* Вычисляем координаты вершин квадрата */
|
||||
/* Угол 0 означает, что одна вершина справа от центра */
|
||||
float vertices[4][2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float vertex_angle = angle + i * (M_PI / 2.0f);
|
||||
vertices[i][0] = cx + radius * cosf(vertex_angle);
|
||||
vertices[i][1] = cy - radius * sinf(vertex_angle); /* Инвертируем Y для экранных координат */
|
||||
}
|
||||
|
||||
/* Находим ограничивающий прямоугольник */
|
||||
float min_x = vertices[0][0], max_x = vertices[0][0];
|
||||
float min_y = vertices[0][1], max_y = vertices[0][1];
|
||||
for (int i = 1; i < 4; i++) {
|
||||
min_x = fminf(min_x, vertices[i][0]);
|
||||
max_x = fmaxf(max_x, vertices[i][0]);
|
||||
min_y = fminf(min_y, vertices[i][1]);
|
||||
max_y = fmaxf(max_y, vertices[i][1]);
|
||||
}
|
||||
|
||||
min_x -= border_thickness;
|
||||
max_x += border_thickness;
|
||||
min_y -= border_thickness;
|
||||
max_y += border_thickness;
|
||||
|
||||
int x_min = (int)fmaxf(0, min_x);
|
||||
int x_max = (int)fminf(draw_info->width - 1, max_x);
|
||||
int y_min = (int)fmaxf(0, min_y);
|
||||
int y_max = (int)fminf(draw_info->height - 1, max_y);
|
||||
|
||||
/* Рисуем квадрат */
|
||||
for (int y = y_min; y <= y_max; y++) {
|
||||
for (int x = x_min; x <= x_max; x++) {
|
||||
float px = (float)x;
|
||||
float py = (float)y;
|
||||
|
||||
/* Проверяем, находится ли точка внутри квадрата */
|
||||
/* Используем два треугольника */
|
||||
int inside = point_in_triangle(px, py,
|
||||
vertices[0][0], vertices[0][1],
|
||||
vertices[1][0], vertices[1][1],
|
||||
vertices[2][0], vertices[2][1]) ||
|
||||
point_in_triangle(px, py,
|
||||
vertices[0][0], vertices[0][1],
|
||||
vertices[2][0], vertices[2][1],
|
||||
vertices[3][0], vertices[3][1]);
|
||||
|
||||
if (inside) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
|
||||
} else {
|
||||
/* Проверяем расстояние до границ */
|
||||
float min_dist = INFINITY;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int next = (i + 1) % 4;
|
||||
float dist = point_to_segment_distance(px, py,
|
||||
vertices[i][0], vertices[i][1],
|
||||
vertices[next][0], vertices[next][1]);
|
||||
min_dist = fminf(min_dist, dist);
|
||||
}
|
||||
|
||||
if (min_dist <= border_thickness) {
|
||||
set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void figure_draw(struct window_draw_info* draw_info, float border_thickness, uint32_t border_color, uint32_t fill_color)
|
||||
{
|
||||
if (!draw_info || !draw_info->data)
|
||||
return;
|
||||
|
||||
/* Координаты приходят в относительных единицах
|
||||
* Y: [0..1] -> умножаем на высоту
|
||||
* X: нормализован относительно высоты -> умножаем на высоту же
|
||||
*/
|
||||
float cx = draw_info->figure.position.x * draw_info->height;
|
||||
float cy = draw_info->figure.position.y * draw_info->height;
|
||||
float radius = draw_info->figure.radius;
|
||||
float angle = draw_info->figure.angle;
|
||||
enum figure_type type = draw_info->figure.type;
|
||||
|
||||
switch (type) {
|
||||
case FIGURE_CIRCLE:
|
||||
draw_circle(draw_info, cx, cy, radius, border_thickness, border_color, fill_color);
|
||||
break;
|
||||
|
||||
case FIGURE_TRIANGLE:
|
||||
draw_triangle(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
|
||||
break;
|
||||
|
||||
case FIGURE_SQUARE:
|
||||
draw_square(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
81
wayland/src/input-handle.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "input-handle.h"
|
||||
#include "wayland-runtime.h"
|
||||
#include <math.h>
|
||||
|
||||
void keyboard_key_handle(xkb_keycode_t kc, xkb_keysym_t ks, enum keyboard_key_state state, struct wayland_window *window)
|
||||
{
|
||||
if (ks != XKB_KEY_NoSymbol)
|
||||
{
|
||||
char buf[64];
|
||||
int n = xkb_keysym_to_utf8(ks, buf, sizeof(buf));
|
||||
|
||||
// if (n > 0)
|
||||
// printf("keyboard: symbol '%s' (keysym 0x%x) on surface:%p\n", buf, ks, window);
|
||||
// else
|
||||
// printf("keyboard: keysym 0x%x (no UTF-8 representation)\n", ks);
|
||||
|
||||
if(state != KEYBOARD_KEY_STATE_RELEASED)
|
||||
{
|
||||
switch (ks)
|
||||
{
|
||||
case XKB_KEY_Return:
|
||||
run_window();
|
||||
break;
|
||||
|
||||
case '1':
|
||||
window->draw_info.figure.type = FIGURE_CIRCLE;
|
||||
break;
|
||||
case '2':
|
||||
window->draw_info.figure.type = FIGURE_TRIANGLE;
|
||||
break;
|
||||
case '3':
|
||||
window->draw_info.figure.type = FIGURE_SQUARE;
|
||||
break;
|
||||
case '-':
|
||||
/* decrease animation speed (multiplicative delta, clamped) */
|
||||
pthread_mutex_lock(&window->draw_info.figure_mutex);
|
||||
window->draw_info.figure.speed -= 1;
|
||||
if (window->draw_info.figure.speed < 1)
|
||||
window->draw_info.figure.speed = 1;
|
||||
printf("keyboard: speed decreased to %.2f\n", window->draw_info.figure.speed);
|
||||
pthread_mutex_unlock(&window->draw_info.figure_mutex);
|
||||
break;
|
||||
case '+':
|
||||
case '=': /* some layouts may emit '=' for unshifted, handle + via keysym */
|
||||
/* increase animation speed (multiplicative delta, clamped) */
|
||||
pthread_mutex_lock(&window->draw_info.figure_mutex);
|
||||
window->draw_info.figure.speed += 1;
|
||||
if (window->draw_info.figure.speed > 50.0f)
|
||||
window->draw_info.figure.speed = 50.0f;
|
||||
printf("keyboard: speed increased to %.2f\n", window->draw_info.figure.speed);
|
||||
pthread_mutex_unlock(&window->draw_info.figure_mutex);
|
||||
break;
|
||||
case XKB_KEY_Up:
|
||||
/* increase figure radius */
|
||||
pthread_mutex_lock(&window->draw_info.figure_mutex);
|
||||
{
|
||||
float maxr = fminf(window->draw_info.width, window->draw_info.height) / 2.0f - 1.0f;
|
||||
window->draw_info.figure.radius += 2.0f;
|
||||
if (window->draw_info.figure.radius > maxr)
|
||||
window->draw_info.figure.radius = maxr;
|
||||
}
|
||||
printf("keyboard: radius increased to %.2f\n", window->draw_info.figure.radius);
|
||||
pthread_mutex_unlock(&window->draw_info.figure_mutex);
|
||||
break;
|
||||
case XKB_KEY_Down:
|
||||
/* decrease figure radius */
|
||||
pthread_mutex_lock(&window->draw_info.figure_mutex);
|
||||
{
|
||||
window->draw_info.figure.radius -= 2.0f;
|
||||
if (window->draw_info.figure.radius < 1.0f)
|
||||
window->draw_info.figure.radius = 1.0f;
|
||||
}
|
||||
printf("keyboard: radius decreased to %.2f\n", window->draw_info.figure.radius);
|
||||
pthread_mutex_unlock(&window->draw_info.figure_mutex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
200
wayland/src/input.c
Normal file
@@ -0,0 +1,200 @@
|
||||
#include <stdio.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <wayland-client.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "input.h"
|
||||
#include "input-handle.h"
|
||||
#include "wayland-runtime.h"
|
||||
#include "window.h"
|
||||
|
||||
static struct wl_seat *seat = NULL;
|
||||
static struct wl_keyboard *keyboard = NULL;
|
||||
static struct xkb_context *xkb_ctx = NULL;
|
||||
static struct xkb_keymap *xkb_keymap = NULL;
|
||||
static struct xkb_state *xkb_state = NULL;
|
||||
static struct wayland_window* focused_window = NULL;
|
||||
|
||||
/* Обработчики клавиатуры */
|
||||
static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
printf("keyboard: keymap format=%u size=%u\n", format, size);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
char *map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
{
|
||||
perror("mmap");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xkb_ctx)
|
||||
xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
|
||||
if (xkb_state)
|
||||
{
|
||||
xkb_state_unref(xkb_state);
|
||||
xkb_state = NULL;
|
||||
}
|
||||
if (xkb_keymap)
|
||||
{
|
||||
xkb_keymap_unref(xkb_keymap);
|
||||
xkb_keymap = NULL;
|
||||
}
|
||||
|
||||
xkb_keymap = xkb_keymap_new_from_string(xkb_ctx, map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!xkb_keymap)
|
||||
{
|
||||
fprintf(stderr, "Unable to compile xkb keymap\n");
|
||||
munmap(map, size);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_state = xkb_state_new(xkb_keymap);
|
||||
|
||||
munmap(map, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
/* Сохраняем поверхность, которая получила фокус клавиатуры */
|
||||
focused_window = get_window_by_surface(surface);
|
||||
}
|
||||
|
||||
static void keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
/* Если уходим с фокусной поверхности — сбросить фокус */
|
||||
if (focused_window && focused_window->wl_surface == surface)
|
||||
focused_window = NULL;
|
||||
}
|
||||
|
||||
static void keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
enum keyboard_key_state key_state = (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? KEYBOARD_KEY_STATE_PRESSED
|
||||
: (state == WL_KEYBOARD_KEY_STATE_REPEATED) ? KEYBOARD_KEY_STATE_REPEATED
|
||||
: KEYBOARD_KEY_STATE_RELEASED;
|
||||
|
||||
if (xkb_state && focused_window)
|
||||
{
|
||||
xkb_keycode_t kc = (xkb_keycode_t)key + 8;
|
||||
xkb_keysym_t ks = xkb_state_key_get_one_sym(xkb_state, kc);
|
||||
|
||||
keyboard_key_handle(kc, ks, key_state, focused_window);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
if (xkb_state)
|
||||
{
|
||||
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, group, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.keymap = keyboard_keymap,
|
||||
.enter = keyboard_enter,
|
||||
.leave = keyboard_leave,
|
||||
.key = keyboard_key,
|
||||
.modifiers = keyboard_modifiers,
|
||||
.repeat_info = keyboard_repeat_info};
|
||||
|
||||
/* Обработчики wl_seat */
|
||||
static void seat_capabilities(void *data, struct wl_seat *seat_local, uint32_t caps)
|
||||
{
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard)
|
||||
{
|
||||
keyboard = wl_seat_get_keyboard(seat_local);
|
||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
|
||||
printf("Seat reports keyboard capability - keyboard bound\n");
|
||||
}
|
||||
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard)
|
||||
{
|
||||
wl_keyboard_destroy(keyboard);
|
||||
keyboard = NULL;
|
||||
if (xkb_state)
|
||||
{
|
||||
xkb_state_unref(xkb_state);
|
||||
xkb_state = NULL;
|
||||
}
|
||||
if (xkb_keymap)
|
||||
{
|
||||
xkb_keymap_unref(xkb_keymap);
|
||||
xkb_keymap = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void seat_name(void *data, struct wl_seat *seat, const char *name)
|
||||
{
|
||||
printf("Seat name: %s\n", name);
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
.capabilities = seat_capabilities,
|
||||
.name = seat_name};
|
||||
|
||||
void input_register_seat(struct wl_seat *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
if (seat)
|
||||
{
|
||||
if (keyboard)
|
||||
{
|
||||
wl_keyboard_destroy(keyboard);
|
||||
keyboard = NULL;
|
||||
}
|
||||
wl_seat_destroy(seat);
|
||||
seat = NULL;
|
||||
}
|
||||
seat = s;
|
||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
||||
}
|
||||
|
||||
void input_cleanup(void)
|
||||
{
|
||||
if (keyboard)
|
||||
{
|
||||
wl_keyboard_destroy(keyboard);
|
||||
keyboard = NULL;
|
||||
}
|
||||
if (seat)
|
||||
{
|
||||
wl_seat_destroy(seat);
|
||||
seat = NULL;
|
||||
}
|
||||
if (xkb_state)
|
||||
{
|
||||
xkb_state_unref(xkb_state);
|
||||
xkb_state = NULL;
|
||||
}
|
||||
if (xkb_keymap)
|
||||
{
|
||||
xkb_keymap_unref(xkb_keymap);
|
||||
xkb_keymap = NULL;
|
||||
}
|
||||
if (xkb_ctx)
|
||||
{
|
||||
xkb_context_unref(xkb_ctx);
|
||||
xkb_ctx = NULL;
|
||||
}
|
||||
focused_window = NULL;
|
||||
}
|
||||
221
wayland/src/registry.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "registry.h"
|
||||
#include "input.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
|
||||
{
|
||||
(void)data;
|
||||
xdg_wm_base_pong(wm_base, serial);
|
||||
}
|
||||
|
||||
static struct xdg_wm_base_listener wm_base_listener = {
|
||||
.ping = wm_base_ping
|
||||
};
|
||||
|
||||
|
||||
/* Глобальные объекты, привязанные к единому registry-потоку (общие для процесса) */
|
||||
static struct wl_compositor *global_compositor;
|
||||
static struct wl_shm *global_shm;
|
||||
static struct xdg_wm_base *global_wm_base;
|
||||
/* Глобальный реестр живёт на весь срок программы (удаляется при сбросе) */
|
||||
static struct wl_registry *global_registry = NULL;
|
||||
|
||||
/* Поток обработки глобальных событий: использует дефолтную очередь */
|
||||
static pthread_t global_thread;
|
||||
static _Atomic int global_thread_running = 0;
|
||||
static struct wl_display *global_display = NULL;
|
||||
|
||||
static uint32_t clamp_version(uint32_t announced, uint32_t supported)
|
||||
{
|
||||
return (announced < supported) ? announced : supported;
|
||||
}
|
||||
|
||||
static void registry_global(void *data, struct wl_registry *reg, uint32_t name, const char *intf, uint32_t version)
|
||||
{
|
||||
printf("%u\t%s\tv:%u\n", name, intf, version);
|
||||
if (!strcmp(intf, wl_compositor_interface.name))
|
||||
{
|
||||
uint32_t ver = clamp_version(version, wl_compositor_interface.version);
|
||||
/* Привязать compositor на дефолтной очереди сразу */
|
||||
if (!global_compositor)
|
||||
global_compositor = wl_registry_bind(reg, name, &wl_compositor_interface, ver);
|
||||
}
|
||||
else if (!strcmp(intf, wl_shm_interface.name))
|
||||
{
|
||||
uint32_t ver = clamp_version(version, wl_shm_interface.version);
|
||||
if (!global_shm)
|
||||
global_shm = wl_registry_bind(reg, name, &wl_shm_interface, ver);
|
||||
}
|
||||
else if (!strcmp(intf, xdg_wm_base_interface.name))
|
||||
{
|
||||
uint32_t ver = clamp_version(version, xdg_wm_base_interface.version);
|
||||
if (!global_wm_base)
|
||||
global_wm_base = wl_registry_bind(reg, name, &xdg_wm_base_interface, ver);
|
||||
if (global_wm_base)
|
||||
xdg_wm_base_add_listener(global_wm_base, &wm_base_listener, NULL);
|
||||
}
|
||||
else if (!strcmp(intf, wl_seat_interface.name))
|
||||
{
|
||||
uint32_t ver = clamp_version(version, wl_seat_interface.version);
|
||||
/* Привязать seat на дефолтной очереди */
|
||||
struct wl_seat *seat = wl_registry_bind(reg, name, &wl_seat_interface, ver);
|
||||
if (seat)
|
||||
input_register_seat(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static void registry_global_remove(void *data, struct wl_registry *reg, uint32_t name)
|
||||
{
|
||||
(void)data;
|
||||
(void)reg;
|
||||
(void)name;
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = registry_global,
|
||||
.global_remove = registry_global_remove
|
||||
};
|
||||
|
||||
|
||||
static void destroy_global_objects(void)
|
||||
{
|
||||
if (global_compositor)
|
||||
{
|
||||
wl_compositor_destroy(global_compositor);
|
||||
global_compositor = NULL;
|
||||
}
|
||||
if (global_shm)
|
||||
{
|
||||
wl_shm_destroy(global_shm);
|
||||
global_shm = NULL;
|
||||
}
|
||||
if (global_wm_base)
|
||||
{
|
||||
xdg_wm_base_destroy(global_wm_base);
|
||||
global_wm_base = NULL;
|
||||
}
|
||||
if (global_registry)
|
||||
{
|
||||
wl_registry_destroy(global_registry);
|
||||
global_registry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Привязка глобального реестра — привязать compositor/shm/xdg_wm_base и
|
||||
* запустить поток обработки глобальной очереди. Возвращает 0 при успехе,
|
||||
* -1 при ошибке.
|
||||
*/
|
||||
static void *registry_global_thread(void *arg)
|
||||
{
|
||||
struct wl_display *display = arg;
|
||||
int fd = wl_display_get_fd(display);
|
||||
while (global_thread_running)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
int ret = poll(&pfd, 1, 100); /* таймаут 100 ms для проверки флага запуска */
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ret == 0)
|
||||
continue;
|
||||
if (pfd.revents & POLLIN)
|
||||
{
|
||||
int r = wl_display_dispatch(display);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int registry_global_bind(struct wl_display *display)
|
||||
{
|
||||
if (!display)
|
||||
return -1;
|
||||
if (global_thread_running)
|
||||
return 0; /* уже привязан */
|
||||
/* Используем дефолтную очередь для глобальных объектов.
|
||||
* Добавим listener и сделаем roundtrip, чтобы вызывающий код
|
||||
* мог не делать этого заранее. Реестр привяжет глобалы сразу
|
||||
* при получении событий. */
|
||||
|
||||
struct wl_registry *registry = wl_display_get_registry(display);
|
||||
if (!registry)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||
if (wl_display_roundtrip(display) < 0)
|
||||
{
|
||||
/* произошла ошибка — уничтожить registry и вернуть ошибку */
|
||||
wl_registry_destroy(registry);
|
||||
return -1;
|
||||
}
|
||||
global_registry = registry;
|
||||
|
||||
if (!global_compositor || !global_shm || !global_wm_base)
|
||||
{
|
||||
destroy_global_objects();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Запустить поток обработки событий дефолтной очереди */
|
||||
global_thread_running = 1;
|
||||
global_display = display;
|
||||
int thr_res = pthread_create(&global_thread, NULL, registry_global_thread, display);
|
||||
if (thr_res != 0)
|
||||
{
|
||||
global_thread_running = 0;
|
||||
destroy_global_objects();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void registry_global_unbind(void)
|
||||
{
|
||||
if (!global_thread_running)
|
||||
return;
|
||||
|
||||
/* Корректно остановить поток */
|
||||
global_thread_running = 0;
|
||||
pthread_join(global_thread, NULL);
|
||||
global_display = NULL;
|
||||
|
||||
destroy_global_objects();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_compositor *registry_get_compositor(void)
|
||||
{
|
||||
return global_compositor;
|
||||
}
|
||||
|
||||
struct wl_shm *registry_get_shm(void)
|
||||
{
|
||||
return global_shm;
|
||||
}
|
||||
|
||||
struct xdg_wm_base *registry_get_xdg_wm_base(void)
|
||||
{
|
||||
return global_wm_base;
|
||||
}
|
||||
|
||||
272
wayland/src/wayland-runtime.c
Normal file
@@ -0,0 +1,272 @@
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "input.h"
|
||||
#include "registry.h"
|
||||
#include "wayland-runtime.h"
|
||||
#include "window.h"
|
||||
#include "figure-animate.h"
|
||||
|
||||
#define MAX_WINDOW_THREADS 128
|
||||
|
||||
struct window_thread_slot
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_t aux_thread;
|
||||
int active;
|
||||
struct wl_event_queue *queue;
|
||||
struct wayland_window window;
|
||||
};
|
||||
|
||||
static struct wl_display *g_display = NULL;
|
||||
static struct window_thread_slot g_slots[MAX_WINDOW_THREADS];
|
||||
static pthread_mutex_t g_thread_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t g_thread_cond = PTHREAD_COND_INITIALIZER;
|
||||
static atomic_int g_active_threads = 0;
|
||||
static atomic_bool g_shutdown = 0;
|
||||
static int g_initialized = 0;
|
||||
|
||||
static void signal_thread_exit(struct window_thread_slot *slot)
|
||||
{
|
||||
pthread_mutex_lock(&g_thread_lock);
|
||||
slot->active = 0;
|
||||
atomic_fetch_sub(&g_active_threads, 1);
|
||||
pthread_cond_broadcast(&g_thread_cond);
|
||||
pthread_mutex_unlock(&g_thread_lock);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Прототип ASM-функции
|
||||
void place_points_on_circle(
|
||||
float pos_x,
|
||||
float pos_y,
|
||||
float offset_rad,
|
||||
float radius,
|
||||
float *points,
|
||||
size_t count);
|
||||
|
||||
struct Point
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
static void *window_aux_loop(void *arg)
|
||||
{
|
||||
struct window_thread_slot *slot = arg;
|
||||
struct window_draw_info *draw_info = &slot->window.draw_info;
|
||||
while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
|
||||
{
|
||||
/* На время обновления позиции фигуры локаем мутекс */
|
||||
pthread_mutex_lock(&draw_info->figure_mutex);
|
||||
figure_animation_step(draw_info);
|
||||
|
||||
// const size_t n = 8;
|
||||
// struct vec2 *pts = malloc(sizeof(struct vec2) * n);
|
||||
// if (!pts)
|
||||
// {
|
||||
// printf("malloc failed\n");
|
||||
// }
|
||||
|
||||
// float center_x = 0.0f;
|
||||
// float center_y = 0.0f;
|
||||
// float offset = 0.78f;
|
||||
// float radius = 1.0f;
|
||||
|
||||
// // Вызов ASM-функции
|
||||
// place_points_on_circle(
|
||||
// center_x,
|
||||
// center_y,
|
||||
// offset,
|
||||
// radius,
|
||||
// (float *)pts, // адрес выходного массива
|
||||
// n);
|
||||
|
||||
// // Вывод для проверки (и удобной точки останова)
|
||||
// for (size_t i = 0; i < n; i++)
|
||||
// {
|
||||
// printf("%zu: x = %f, y = %f\n", i, pts[i].x, pts[i].y);
|
||||
// }
|
||||
|
||||
// free(pts);
|
||||
pthread_mutex_unlock(&draw_info->figure_mutex);
|
||||
|
||||
usleep(15 * 1000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *window_thread_main(void *arg)
|
||||
{
|
||||
struct window_thread_slot *slot = arg;
|
||||
memset(&slot->window, 0, sizeof(slot->window));
|
||||
|
||||
slot->queue = wl_display_create_queue(g_display);
|
||||
if (!slot->queue)
|
||||
{
|
||||
signal_thread_exit(slot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (window_init(g_display, slot->queue, &slot->window) < 0)
|
||||
{
|
||||
wl_event_queue_destroy(slot->queue);
|
||||
slot->queue = NULL;
|
||||
signal_thread_exit(slot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Запуск вспомогательного потока, который пока просто крутится с паузой 100 ms.
|
||||
* Поток завершится, как только окно будет помечено как закрыто. */
|
||||
int aux_res = pthread_create(&slot->aux_thread, NULL, window_aux_loop, slot);
|
||||
if (aux_res == 0)
|
||||
pthread_detach(slot->aux_thread);
|
||||
|
||||
while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
|
||||
{
|
||||
int dispatch = wl_display_dispatch_queue(g_display, slot->queue);
|
||||
if (dispatch < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
atomic_store(&g_shutdown, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("Window #%d closed\n", slot->window.id);
|
||||
|
||||
window_destroy(&slot->window);
|
||||
|
||||
if (slot->queue)
|
||||
{
|
||||
wl_event_queue_destroy(slot->queue);
|
||||
slot->queue = NULL;
|
||||
}
|
||||
|
||||
signal_thread_exit(slot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t init_wayland(void)
|
||||
{
|
||||
if (g_initialized)
|
||||
return 0;
|
||||
|
||||
g_display = wl_display_connect(NULL);
|
||||
if (!g_display)
|
||||
return -1;
|
||||
|
||||
/* Привязать глобальные объекты и запустить поток обработки (дефолтная очередь).
|
||||
* `registry_global_bind` добавляет listener и делает roundtrip. */
|
||||
if (registry_global_bind(g_display) < 0)
|
||||
{
|
||||
wl_display_disconnect(g_display);
|
||||
g_display = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
atomic_store(&g_shutdown, 0);
|
||||
atomic_store(&g_active_threads, 0);
|
||||
g_initialized = 1;
|
||||
|
||||
/* Print keyboard help to the console so users know available shortcuts and
|
||||
* behaviors. This mirrors the handling implemented in
|
||||
* `input-handle.c` and acts as a quick usage hint. */
|
||||
printf("\nKeyboard shortcuts and behavior:\n");
|
||||
printf(" - Enter: open a new window\n");
|
||||
printf(" - 1: switch to circle figure\n");
|
||||
printf(" - 2: switch to triangle figure\n");
|
||||
printf(" - 3: switch to square figure\n");
|
||||
printf(" - - : decrease animation speed (clamped, multiplicative)\n");
|
||||
printf(" - + or = : increase animation speed (clamped, multiplicative)\n");
|
||||
printf(" - Up arrow: increase figure radius (clamped to window bounds)\n");
|
||||
printf(" - Down arrow: decrease figure radius (minimum 1.0)\n");
|
||||
printf(" Note: actions are triggered on key press (not on release).\n\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t run_window(void)
|
||||
{
|
||||
if (!g_initialized || !g_display)
|
||||
return -1;
|
||||
|
||||
int slot_index = -1;
|
||||
|
||||
pthread_mutex_lock(&g_thread_lock);
|
||||
for (int i = 0; i < MAX_WINDOW_THREADS; ++i)
|
||||
{
|
||||
if (!g_slots[i].active)
|
||||
{
|
||||
slot_index = i;
|
||||
g_slots[i].active = 1;
|
||||
atomic_fetch_add(&g_active_threads, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_thread_lock);
|
||||
|
||||
if (slot_index < 0)
|
||||
return -1;
|
||||
|
||||
int create_res = pthread_create(&g_slots[slot_index].thread, NULL, window_thread_main, &g_slots[slot_index]);
|
||||
if (create_res != 0)
|
||||
{
|
||||
pthread_mutex_lock(&g_thread_lock);
|
||||
g_slots[slot_index].active = 0;
|
||||
atomic_fetch_sub(&g_active_threads, 1);
|
||||
pthread_mutex_unlock(&g_thread_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_detach(g_slots[slot_index].thread);
|
||||
return slot_index;
|
||||
}
|
||||
|
||||
void wait_for_windows(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_thread_lock);
|
||||
while (atomic_load(&g_active_threads) > 0)
|
||||
pthread_cond_wait(&g_thread_cond, &g_thread_lock);
|
||||
pthread_mutex_unlock(&g_thread_lock);
|
||||
}
|
||||
|
||||
void destroy_wayland(void)
|
||||
{
|
||||
if (!g_initialized)
|
||||
return;
|
||||
|
||||
wait_for_windows();
|
||||
|
||||
input_cleanup();
|
||||
/* Остановить поток глобального реестра и сбросить глобальные данные */
|
||||
registry_global_unbind();
|
||||
|
||||
if (g_display)
|
||||
{
|
||||
wl_display_disconnect(g_display);
|
||||
g_display = NULL;
|
||||
}
|
||||
|
||||
memset(g_slots, 0, sizeof(g_slots));
|
||||
|
||||
g_initialized = 0;
|
||||
atomic_store(&g_shutdown, 0);
|
||||
}
|
||||
|
||||
struct wayland_window *get_window_by_surface(struct wl_surface *surf)
|
||||
{
|
||||
for (int i = 0; i < MAX_WINDOW_THREADS; i++)
|
||||
if (g_slots[i].window.wl_surface == surf)
|
||||
return &g_slots[i].window;
|
||||
return NULL;
|
||||
}
|
||||
299
wayland/src/window.c
Normal file
@@ -0,0 +1,299 @@
|
||||
#include "window.h"
|
||||
#include "registry.h"
|
||||
#include "figure-draw.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include <wayland-client.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "registry.h"
|
||||
|
||||
static atomic_uint_fast64_t shm_counter = 0;
|
||||
|
||||
static int32_t alloc_shm(size_t size)
|
||||
{
|
||||
char name[64];
|
||||
uint64_t id = atomic_fetch_add(&shm_counter, 1);
|
||||
snprintf(name, sizeof(name), "/wayland-shm-%d-%llu", getpid(), (unsigned long long)id);
|
||||
|
||||
int32_t fd = shm_open(name, O_RDWR | O_CREAT, 0600);
|
||||
if (fd == -1)
|
||||
return fd;
|
||||
shm_unlink(name);
|
||||
if (ftruncate(fd, size) == -1)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void destroy_frame_callback(struct wayland_window *win)
|
||||
{
|
||||
if (win->frame_callback)
|
||||
{
|
||||
wl_callback_destroy(win->frame_callback);
|
||||
win->frame_callback = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void resize_canvas(struct wayland_window *win)
|
||||
{
|
||||
struct window_draw_info *draw_info = &win->draw_info;
|
||||
size_t stride = draw_info->width * 4;
|
||||
size_t size = stride * draw_info->height;
|
||||
int32_t fd = alloc_shm(size);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
if (win->buffer)
|
||||
{
|
||||
wl_buffer_destroy(win->buffer);
|
||||
win->buffer = NULL;
|
||||
}
|
||||
|
||||
void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
{
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
struct wl_shm *shm = registry_get_shm();
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
||||
win->buffer = wl_shm_pool_create_buffer(pool, 0, draw_info->width, draw_info->height, stride, WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
draw_info->data = map;
|
||||
}
|
||||
|
||||
static void resize_new(struct wayland_window *win, int32_t w, int32_t h)
|
||||
{
|
||||
if (!w || !h)
|
||||
return;
|
||||
struct window_draw_info *draw_info = &win->draw_info;
|
||||
|
||||
if (draw_info->width != w || draw_info->height != h)
|
||||
{
|
||||
if (draw_info->data)
|
||||
{
|
||||
munmap(draw_info->data, draw_info->width * draw_info->height * 4);
|
||||
draw_info->data = NULL;
|
||||
}
|
||||
draw_info->width = w;
|
||||
draw_info->height = h;
|
||||
resize_canvas(win);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(struct wayland_window *win)
|
||||
{
|
||||
struct window_draw_info *draw_info = &win->draw_info;
|
||||
|
||||
size_t stride = draw_info->width * 4;
|
||||
size_t size = stride * draw_info->height;
|
||||
|
||||
if (!draw_info->data || !win->buffer)
|
||||
return;
|
||||
|
||||
// Залочиться, чтобы операции обновления позиции фигуры происходили атомарно
|
||||
pthread_mutex_lock(&draw_info->figure_mutex);
|
||||
struct figure_animation_info figure = draw_info->figure;
|
||||
|
||||
uint32_t color;
|
||||
switch (figure.type)
|
||||
{
|
||||
case FIGURE_CIRCLE:
|
||||
color = 0xFFFF0000;
|
||||
break;
|
||||
case FIGURE_TRIANGLE:
|
||||
color = 0xFF00FF00;
|
||||
break;
|
||||
case FIGURE_SQUARE:
|
||||
color = 0xFF0000FF;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t *bytes = (uint8_t *)draw_info->data;
|
||||
for (int32_t y = 0; y < (int32_t)draw_info->height; ++y)
|
||||
{
|
||||
uint32_t *row = (uint32_t *)(bytes + y * stride);
|
||||
for (int32_t x = 0; x < (int32_t)draw_info->width; ++x)
|
||||
row[x] = 0xAA5F5F5F; /* background black */
|
||||
}
|
||||
|
||||
/* Draw figure into buffer. border thickness in pixels = 3.0f */
|
||||
figure_draw(&win->draw_info, 3.0f, 0xFF000000, color);
|
||||
pthread_mutex_unlock(&draw_info->figure_mutex);
|
||||
|
||||
|
||||
wl_surface_attach(win->wl_surface, win->buffer, 0, 0);
|
||||
wl_surface_damage_buffer(win->wl_surface, 0, 0, draw_info->width, draw_info->height);
|
||||
wl_surface_commit(win->wl_surface);
|
||||
}
|
||||
|
||||
static void xdg_surface_conf(void *data, struct xdg_surface *xdg_surface_local, uint32_t serial)
|
||||
{
|
||||
xdg_surface_ack_configure(xdg_surface_local, serial);
|
||||
struct wayland_window *win = data;
|
||||
if (!win->draw_info.data)
|
||||
resize_canvas(win);
|
||||
|
||||
draw(win);
|
||||
}
|
||||
|
||||
static void xdg_toplevel_conf(void *data, struct xdg_toplevel *xdg_top, int32_t w, int32_t h, struct wl_array *states)
|
||||
{
|
||||
(void)states;
|
||||
struct wayland_window *win = data;
|
||||
resize_new(win, w, h);
|
||||
}
|
||||
|
||||
static void xdg_toplevel_cls(void *data, struct xdg_toplevel *xdg_top)
|
||||
{
|
||||
struct wayland_window *win = data;
|
||||
(void)xdg_top;
|
||||
win->need_close = 1;
|
||||
}
|
||||
|
||||
static void xdg_toplevel_bounds(void *data, struct xdg_toplevel *xdg_top, int32_t w, int32_t h)
|
||||
{
|
||||
(void)data;
|
||||
(void)xdg_top;
|
||||
(void)w;
|
||||
(void)h;
|
||||
}
|
||||
|
||||
static void xdg_toplevel_wm_caps(void *data, struct xdg_toplevel *xdg_top, struct wl_array *caps)
|
||||
{
|
||||
(void)data;
|
||||
(void)xdg_top;
|
||||
(void)caps;
|
||||
}
|
||||
|
||||
static struct wl_callback_listener callback_listener;
|
||||
static void setup_frame_callback(struct wayland_window *win, struct wl_event_queue *queue)
|
||||
{
|
||||
struct wl_callback *cb = wl_surface_frame(win->wl_surface);
|
||||
win->frame_callback = cb;
|
||||
if (cb && queue)
|
||||
wl_proxy_set_queue((struct wl_proxy *)cb, queue);
|
||||
wl_callback_add_listener(cb, &callback_listener, win);
|
||||
}
|
||||
|
||||
static void frame_new(void *data, struct wl_callback *cb, uint32_t _)
|
||||
{
|
||||
struct wayland_window *win = data;
|
||||
if (win->frame_callback == cb)
|
||||
win->frame_callback = NULL;
|
||||
wl_callback_destroy(cb);
|
||||
setup_frame_callback(win, win->queue);
|
||||
draw(win);
|
||||
}
|
||||
|
||||
static struct wl_callback_listener callback_listener = {
|
||||
.done = frame_new};
|
||||
static struct xdg_surface_listener surface_listener = {
|
||||
.configure = xdg_surface_conf};
|
||||
static struct xdg_toplevel_listener top_listener = {
|
||||
.configure = xdg_toplevel_conf,
|
||||
.close = xdg_toplevel_cls,
|
||||
.configure_bounds = xdg_toplevel_bounds,
|
||||
.wm_capabilities = xdg_toplevel_wm_caps};
|
||||
|
||||
int window_init(struct wl_display *display, struct wl_event_queue *queue, struct wayland_window *win)
|
||||
{
|
||||
static int id = 0;
|
||||
(void)display;
|
||||
struct wl_compositor *compositor = registry_get_compositor();
|
||||
struct wl_shm *shm = registry_get_shm();
|
||||
struct xdg_wm_base *wm = registry_get_xdg_wm_base();
|
||||
if (!compositor || !shm || !wm)
|
||||
return -1;
|
||||
|
||||
win->wl_surface = wl_compositor_create_surface(compositor);
|
||||
win->queue = queue;
|
||||
if (win->wl_surface && queue)
|
||||
wl_proxy_set_queue((struct wl_proxy *)win->wl_surface, queue);
|
||||
|
||||
setup_frame_callback(win, queue);
|
||||
|
||||
win->xdg_surface = xdg_wm_base_get_xdg_surface(wm, win->wl_surface);
|
||||
if (win->xdg_surface && queue)
|
||||
wl_proxy_set_queue((struct wl_proxy *)win->xdg_surface, queue);
|
||||
xdg_surface_add_listener(win->xdg_surface, &surface_listener, win);
|
||||
|
||||
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
|
||||
if (win->xdg_toplevel && queue)
|
||||
wl_proxy_set_queue((struct wl_proxy *)win->xdg_toplevel, queue);
|
||||
|
||||
xdg_toplevel_add_listener(win->xdg_toplevel, &top_listener, win);
|
||||
|
||||
wl_surface_commit(win->wl_surface);
|
||||
|
||||
struct window_draw_info *draw_info = &win->draw_info;
|
||||
|
||||
/* Инициализация состояния */
|
||||
win->id = id++;
|
||||
draw_info->width = 400;
|
||||
draw_info->height = 250;
|
||||
draw_info->data = NULL;
|
||||
|
||||
/* Default animation state */
|
||||
draw_info->figure.type = FIGURE_CIRCLE;
|
||||
draw_info->figure.position.x = 0.5f;
|
||||
draw_info->figure.position.y = 0.5f;
|
||||
draw_info->figure.velocity.x = 0.5f;
|
||||
draw_info->figure.velocity.y = 0.5f;
|
||||
draw_info->figure.angle = 0.0f;
|
||||
draw_info->figure.angular_velocity = 0.01; /* radians per frame */
|
||||
draw_info->figure.speed = 10; /* speed multiplier */
|
||||
draw_info->figure.radius = 25.0f; /* radius in pixels */
|
||||
|
||||
win->buffer = NULL;
|
||||
win->frame_callback = NULL;
|
||||
win->need_close = 0;
|
||||
/* Мьютекс нужен для атомарного обращения к структуре фигуры */
|
||||
if (pthread_mutex_init(&win->draw_info.figure_mutex, NULL) != 0)
|
||||
{
|
||||
/* if mutex init failed — cleanup and return error */
|
||||
window_destroy(win);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xdg_toplevel_set_app_id(win->xdg_toplevel, "my-wayland-window");
|
||||
xdg_toplevel_set_title(win->xdg_toplevel, "Custom Title");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int window_should_close(struct wayland_window *win)
|
||||
{
|
||||
return win->need_close;
|
||||
}
|
||||
|
||||
void window_destroy(struct wayland_window *win)
|
||||
{
|
||||
destroy_frame_callback(win);
|
||||
if (win->buffer)
|
||||
wl_buffer_destroy(win->buffer);
|
||||
if (win->xdg_toplevel)
|
||||
xdg_toplevel_destroy(win->xdg_toplevel);
|
||||
if (win->xdg_surface)
|
||||
xdg_surface_destroy(win->xdg_surface);
|
||||
if (win->wl_surface)
|
||||
wl_surface_destroy(win->wl_surface);
|
||||
if (win->draw_info.data)
|
||||
munmap(win->draw_info.data, win->draw_info.width * win->draw_info.height * 4);
|
||||
/* destroy mutex if initialized */
|
||||
pthread_mutex_destroy(&win->draw_info.figure_mutex);
|
||||
}
|
||||