Compare commits
51 Commits
95e5aab32d
...
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 | ||
|
|
a6d997f61d | ||
|
|
241f5acf71 | ||
|
|
4cff9ad987 | ||
|
|
c8c5518597 | ||
|
|
d7cb83d869 | ||
|
|
98877d6100 | ||
|
|
13cbb905ec | ||
|
|
8f705bb7b8 |
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
**/build/
|
||||
**/build/
|
||||
**/out/
|
||||
|
||||
30
.vscode/launch.json
vendored
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "<file>: lldb debug x64",
|
||||
"program": "${fileDirname}/build/${fileBasenameNoExtension}",
|
||||
"cwd": "${fileDirname}/build",
|
||||
"preLaunchTask": "asm64"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "<file>: lldb+GCC debug x64",
|
||||
"program": "${fileDirname}/build/${fileBasenameNoExtension}",
|
||||
"cwd": "${fileDirname}/build",
|
||||
"preLaunchTask": "asm64+gcc"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "minimal: lldb debug x64",
|
||||
"program": "${workspaceFolder}/minimal/build/minimal",
|
||||
"cwd": "${workspaceFolder}/minimal/build"
|
||||
// TODO тут требуется ещё указать задачу сборки minimal, но её ещё нет
|
||||
},
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"debug.allowBreakpointsEverywhere": true
|
||||
}
|
||||
37
casm/.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
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
10
casm/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(casm)
|
||||
|
||||
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)
|
||||
25
casm/CMakePresets.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 8,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "debug-gcc",
|
||||
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
|
||||
"CMAKE_C_COMPILER": "/usr/bin/gcc",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release-gcc",
|
||||
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
|
||||
"CMAKE_C_COMPILER": "/usr/bin/gcc",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/g++",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
23
casm/asm.asm
Normal file
@@ -0,0 +1,23 @@
|
||||
global main
|
||||
extern sum, print, scan
|
||||
extern a
|
||||
|
||||
section .text
|
||||
main:
|
||||
push rbx
|
||||
call scan ; прочитать первое число
|
||||
mov rbx, rax ; сохранить первое число
|
||||
call scan ; прочитать второе число
|
||||
mov rdi, rbx ; a
|
||||
mov rsi, rax ; b
|
||||
call sum ; сумма
|
||||
mov rdi, rax ; результат
|
||||
push rax
|
||||
call print ; напечатать
|
||||
pop rax
|
||||
pop rbx
|
||||
|
||||
mov dword [rel a], eax ; записать значение 42 в переменную a
|
||||
|
||||
mov rax, 0
|
||||
ret
|
||||
24
casm/c.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int a;
|
||||
|
||||
int sum(int a, int b) {
|
||||
return a+b;
|
||||
}
|
||||
|
||||
void print(int num) {
|
||||
printf("%d\n", num);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int scan() {
|
||||
char buffer[100];
|
||||
int res;
|
||||
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
||||
if (sscanf(buffer, "%d", &res) == 1) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0; // or handle error
|
||||
}
|
||||
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 — сохраняй, если меняешь).
|
||||
BIN
docs/x86-64_Registers.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
569
docs/x86-64_asm_sheet.md
Normal file
@@ -0,0 +1,569 @@
|
||||
# x86-64 ASM sheet
|
||||
|
||||
## Addressing
|
||||
|
||||
* No segmentation (except for `fs` and `gs` for special purposes like threading)
|
||||
|
||||
* Relative to base register
|
||||
* used for **data on the stack**, **arrays**, **structs** and **class members**
|
||||
* [`base` + `index` * `scale` + `immediate_offset`]
|
||||
* `base` is mandatory, can be any 64-bit register
|
||||
* `index` can be any 64-bit register except `rsp`
|
||||
* `scale` can be 1, 2, 4, or 8
|
||||
* `immediate_offset` (called displacement with Gas) relative to the base register
|
||||
* Gas syntax is `immediate_offset(base, index, scale)`
|
||||
|
||||
* RIP-relative (a.k.a. PC-relative)
|
||||
* used for **static data**
|
||||
* contains a 32-bit sign-extended offset relative to the instruction pointer
|
||||
* explicitely specified using `mov eax [rel label]` or `default rel` / `default abs` commands with NASM (uses 32-bit absolute addressing otherwise)
|
||||
* explicitely specified using `mov eax label(%rip)` with Gas
|
||||
|
||||
* 32-bit absolute
|
||||
* 32 bits constant address sign-extended to 64 bits
|
||||
* works for addresses below 2^31
|
||||
* don't use for simple memory operands since RIP-relative addressing is shorter, faster (no need for relocations) and works everywhere
|
||||
* used to access **static arrays with an index register** like `mov ebx, [intarray + rsi*4]` though it doesn't work for Windows and Linux DLLs and for MacOSX exes and DLLs because addresses are above 2^32 (it is used by gcc and clang for Linux exes, an image base relative addressing is used on Windows exes by MASM)
|
||||
* an alternative that works everywhere is first loading the static array address into `rbx` using `lea` with a RIP-relative address and then address relatively from this base register (`lea rbx, [array]` then `mov eax, [rbx + rcx*4]`), other static arrays can then be accessed relatively (`mov [(array2-array1) + rbx + rcx*4], eax`)
|
||||
|
||||
* 64-bit absolute
|
||||
* `mov eax, dword [qword a]`
|
||||
* can only be used with `mov` and registers `al`, `ax`, `eax` or `rax` (src or dst)
|
||||
* can't contain a segment, base or index register
|
||||
|
||||
## Position-Independent Code (PIC)
|
||||
|
||||
* Easier and faster than the 32-bit Global Offset Table (GOT) technique since RIP-relative is position independent (note that the technique to access static arrays with an index register described earlier is position independent too)
|
||||
|
||||
## General purpose registers
|
||||
|
||||
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` register
|
||||
|
||||
* **CF (Carry Flag, bit 0)** — Set if an arithmetic operation generates a carry or a borrow out of the most-significant bit of the result; cleared otherwise. This flag indicates an overflow condition for unsigned-integer arithmetic. It is also used in multiple-precision arithmetic.
|
||||
* **PF (Parity Flag, bit 2)** — Set if the least-significant byte of the result contains an even number of 1 bits; cleared otherwise.
|
||||
* **AF (Auxiliary carry Flag, bit 4)** — Set if an arithmetic operation generates a carry or a borrow out of bit 3 of the result; cleared otherwise. This flag is used in binary-coded decimal (BCD) arithmetic.
|
||||
* **ZF (Zero Flag, bit 6)** — Set if the result is zero; cleared otherwise.
|
||||
* **SF (Sign Flag, bit 7)** — Set equal to the most-significant bit of the result, which is the sign bit of a signed integer. (0 indicates a positive value and 1 indicates a negative value.)
|
||||
* **OF (Overflow Flag, bit 11)** — Set if the integer result is too large a positive number or too small a negative number (excluding the sign-bit) to fit in the destination operand; cleared otherwise. This flag indicates an overflow condition for signed-integer (two’s complement) arithmetic.
|
||||
|
||||
## Saturation and wraparound modes (of the instruction set)
|
||||
|
||||
* **Wraparound arithmetic** — With wraparound arithmetic, a true out-of-range result is truncated (that is, the carry or overflow bit is ignored and only the least significant bits of the result are returned to the destination). Wraparound arithmetic is suitable for applications that control the range of operands to prevent out-of-range results. If the range of operands is not controlled, however, wraparound arithmetic can lead to large errors. For example, adding two large signed numbers can cause positive overflow and produce a negative result.
|
||||
* **Signed saturation arithmetic** — With signed saturation arithmetic, out-of-range results are limited to the representable range of signed integers for the integer size being operated on. For example, if positive overflow occurs when operating on signed word integers, the result is saturated to 7FFFH, which is the largest positive integer that can be represented in 16 bits; if negative overflow occurs, the result is saturated to 8000H.
|
||||
* **Unsigned saturation arithmetic** — With unsigned saturation arithmetic, out-of-range results are limited to the representable range of unsigned integers for the integer size. So, positive overflow when operating on unsigned byte integers results in FFH being returned and negative overflow results in 00H being returned.
|
||||
|
||||
## Stack frames
|
||||
|
||||
## Data transfer instructions
|
||||
|
||||
* [**MOV**](http://www.felixcloutier.com/x86/MOV.html) — Move data between general-purpose registers; move data between memory and general-purpose or segment registers; move immediates to general-purpose registers.
|
||||
* [**CMOVcc**](http://www.felixcloutier.com/x86/CMOVcc.html) — Conditional move.
|
||||
* [**XCHG**](http://www.felixcloutier.com/x86/XCHG.html) — Exchange.
|
||||
* [**BSWAP**](http://www.felixcloutier.com/x86/BSWAP.html) — Byte swap.
|
||||
* [**XADD**](http://www.felixcloutier.com/x86/XADD.html) — Exchange and add.
|
||||
* [**CMPXCHG**](http://www.felixcloutier.com/x86/CMPXCHG.html) — Compare and exchange.
|
||||
* [**CMPXCHG8B / CMPXCHG16B**](http://www.felixcloutier.com/x86/CMPXCHG8B:CMPXCHG16B.html) — Compare and exchange 8/16 bytes.
|
||||
* [**PUSH**](http://www.felixcloutier.com/x86/PUSH.html) — Push onto stack.
|
||||
* [**POP**](http://www.felixcloutier.com/x86/POP.html) — Pop off of stack.
|
||||
* [**PUSHA / PUSHAD**](http://www.felixcloutier.com/x86/PUSHA:PUSHAD.html) — Push general-purpose registers onto stack.
|
||||
* [**POPA / POPAD**](http://www.felixcloutier.com/x86/POPA:POPAD.html) — Pop general-purpose registers from stack.
|
||||
* [**CWD / CDQ / CQO**](http://www.felixcloutier.com/x86/CWD:CDQ:CQO.html) — Convert word to doubleword/Convert doubleword to quadword.
|
||||
* [**CBW / CWDE / CDQE**](http://www.felixcloutier.com/x86/CBW:CWDE:CDQE.html) — Convert byte to word/Convert word to doubleword in `rax` register.
|
||||
* [**MOVSX / MOVSXD**](http://www.felixcloutier.com/x86/MOVSX:MOVSXD.html) — Move and sign extend.
|
||||
* [**MOVZX**](http://www.felixcloutier.com/x86/MOVZX.html) — Move and zero extend.
|
||||
|
||||
## Binary arithmetic instructions
|
||||
|
||||
* [**ADCX**](http://www.felixcloutier.com/x86/ADCX.html) — Unsigned integer add with carry.
|
||||
* [**ADOX**](http://www.felixcloutier.com/x86/ADOX.html) — Unsigned integer add with overflow.
|
||||
* [**ADD**](http://www.felixcloutier.com/x86/ADD.html) — Integer add.
|
||||
* [**ADC**](http://www.felixcloutier.com/x86/ADC.html) — Add with carry.
|
||||
* [**SUB**](http://www.felixcloutier.com/x86/SUB.html) — Subtract.
|
||||
* [**SBB**](http://www.felixcloutier.com/x86/SBB.html) — Subtract with borrow.
|
||||
* [**IMUL**](http://www.felixcloutier.com/x86/IMUL.html) — Signed multiply.
|
||||
* [**MUL**](http://www.felixcloutier.com/x86/MUL.html) — Unsigned multiply.
|
||||
* [**IDIV**](http://www.felixcloutier.com/x86/IDIV.html) — Signed divide.
|
||||
* [**DIV**](http://www.felixcloutier.com/x86/DIV.html) — Unsigned divide.
|
||||
* [**INC**](http://www.felixcloutier.com/x86/INC.html) — Increment.
|
||||
* [**DEC**](http://www.felixcloutier.com/x86/DEC.html) — Decrement.
|
||||
* [**NEG**](http://www.felixcloutier.com/x86/NEG.html) — Negate.
|
||||
* [**CMP**](http://www.felixcloutier.com/x86/CMP.html) — Compare.
|
||||
|
||||
## Logical instructions
|
||||
|
||||
* [**AND**](http://www.felixcloutier.com/x86/AND.html) — Perform bitwise logical AND.
|
||||
* [**OR**](http://www.felixcloutier.com/x86/OR.html) — Perform bitwise logical OR.
|
||||
* [**XOR**](http://www.felixcloutier.com/x86/XOR.html) — Perform bitwise logical exclusive OR.
|
||||
* [**NOT**](http://www.felixcloutier.com/x86/NOT.html) — Perform bitwise logical NOT.
|
||||
|
||||
## Shift and rotate instructions
|
||||
|
||||
* [**SAL / SAR / SHL / SHR**](http://www.felixcloutier.com/x86/SAL:SAR:SHL:SHR.html) — Shift arithmetic/logical left/right.
|
||||
* [**SHLD**](http://www.felixcloutier.com/x86/SHLD.html) — Shift left double.
|
||||
* [**SHRD**](http://www.felixcloutier.com/x86/SHRD.html) — Shift right double.
|
||||
* [**RCL / RCR / ROL / ROR**](http://www.felixcloutier.com/x86/RCL:RCR:ROL:ROR.html) — Rotate left/right and rotate left/right through carry.
|
||||
|
||||
## Bit and byte instructions
|
||||
|
||||
* [**BT**](http://www.felixcloutier.com/x86/BT.html) — Bit test.
|
||||
* [**BTS**](http://www.felixcloutier.com/x86/BTS.html) — Bit test and set.
|
||||
* [**BTR**](http://www.felixcloutier.com/x86/BTR.html) — Bit test and reset.
|
||||
* [**BTC**](http://www.felixcloutier.com/x86/BTC.html) — Bit test and complement.
|
||||
* [**BSF**](http://www.felixcloutier.com/x86/BSF.html) — Bit scan forward.
|
||||
* [**BSR**](http://www.felixcloutier.com/x86/BSR.html) — Bit scan reverse.
|
||||
* [**SETcc**](http://www.felixcloutier.com/x86/SETcc.html) — Set byte on condition.
|
||||
* [**TEST**](http://www.felixcloutier.com/x86/TEST.html) — Logical compare.
|
||||
* [**CRC32**](http://www.felixcloutier.com/x86/CRC32.html) — Provides hardware acceleration to calculate cyclic redundancy checks for fast and efficient implementation of data integrity protocols.
|
||||
* [**POPCNT**](http://www.felixcloutier.com/x86/POPCNT.html) — This instruction calculates the number of bits set to 1 in the second operand (source) and returns the count in the first operand (a destination register).
|
||||
|
||||
## Control transfer instructions
|
||||
|
||||
* [**JMP**](http://www.felixcloutier.com/x86/JMP.html) — Jump.
|
||||
* [**Jcc**](http://www.felixcloutier.com/x86/Jcc.html) — Jump if condition is met (RIP-relative operand).
|
||||
* [**LOOP / LOOPcc**](http://www.felixcloutier.com/x86/LOOP:LOOPcc.html) — Loop with `rcx` counter.
|
||||
* [**CALL**](http://www.felixcloutier.com/x86/CALL.html) — Call procedure.
|
||||
* [**RET**](http://www.felixcloutier.com/x86/RET.html) — Return.
|
||||
* [**IRET / IRETD / IRETQ**](http://www.felixcloutier.com/x86/IRET:IRETD.html) — Return from interrupt.
|
||||
* [**INT n / INTO / INTO 3**](http://www.felixcloutier.com/x86/INTn:INTO:INT3.html) — Call to interrupt procedure.
|
||||
* [**ENTER**](http://www.felixcloutier.com/x86/ENTER.html) — High-level procedure entry.
|
||||
* [**LEAVE**](http://www.felixcloutier.com/x86/LEAVE.html) — High-level procedure exit.
|
||||
|
||||
## String instructions
|
||||
|
||||
* [**MOVS / MOVSB / MOVSW / MOVSD / MOVSQ**](http://www.felixcloutier.com/x86/MOVS:MOVSB:MOVSW:MOVSD:MOVSQ.html) — Move data from string to string.
|
||||
* [**CMPS / CMPSB / CMPSW / CMPSD / CMPSQ**](http://www.felixcloutier.com/x86/CMPS:CMPSB:CMPSW:CMPSD:CMPSQ.html) — Compare string operands.
|
||||
* [**SCAS / SCASB / SCASW / SCASD**](http://www.felixcloutier.com/x86/SCAS:SCASB:SCASW:SCASD.html) — Scan string.
|
||||
* [**LODS / LODSB / LODSW / LODSD / LODSQ**](http://www.felixcloutier.com/x86/LODS:LODSB:LODSW:LODSD:LODSQ.html) — Load string.
|
||||
* [**STOS / STOSB / STOSW / STOSD / STOSQ**](http://www.felixcloutier.com/x86/STOS:STOSB:STOSW:STOSD:STOSQ.html) — Store string.
|
||||
* [**REP / REPE / REPZ / REPNE / REPNZ**](http://www.felixcloutier.com/x86/REP:REPE:REPZ:REPNE:REPNZ.html) — Repeat string operation prefix.
|
||||
|
||||
## `rflags` control instructions
|
||||
|
||||
* [**STC**](http://www.felixcloutier.com/x86/STC.html) — Set carry flag.
|
||||
* [**CLC**](http://www.felixcloutier.com/x86/CLC.html) — Clear the carry flag.
|
||||
* [**CMC**](http://www.felixcloutier.com/x86/CMC.html) — Complement the carry flag.
|
||||
* [**CLD**](http://www.felixcloutier.com/x86/CLD.html) — Clear the direction flag.
|
||||
* [**STD**](http://www.felixcloutier.com/x86/STD.html) — Set direction flag.
|
||||
* [**LAHF**](http://www.felixcloutier.com/x86/LAHF.html) — Load flags into `ah` register.
|
||||
* [**SAHF**](http://www.felixcloutier.com/x86/SAHF.html) — Store `ah` register into flags.
|
||||
* [**PUSHF / PUSHFQ**](http://www.felixcloutier.com/x86/PUSHF:PUSHFD:PUSHFQ.html) — Push `rflags` onto stack.
|
||||
* [**POPF / POPFQ**](http://www.felixcloutier.com/x86/POPF:POPFD:POPFQ.html) — Pop `rflags` from stack.
|
||||
* [**STI**](http://www.felixcloutier.com/x86/STI.html) — Set interrupt flag.
|
||||
* [**CLI**](http://www.felixcloutier.com/x86/CLI.html) — Clear the interrupt flag.
|
||||
|
||||
## Miscellaneous instructions
|
||||
|
||||
* [**LEA**](http://www.felixcloutier.com/x86/LEA.html) — Load effective address.
|
||||
* [**NOP**](http://www.felixcloutier.com/x86/NOP.html) — No operation.
|
||||
* [**UD**](http://www.felixcloutier.com/x86/UD.html) — Undefined instruction.
|
||||
* [**XLAT / XLATB**](http://www.felixcloutier.com/x86/XLAT:XLATB.html) — Table lookup translation.
|
||||
* [**CPUID**](http://www.felixcloutier.com/x86/CPUID.html) — Processor identification.
|
||||
* [**MOVBE**](http://www.felixcloutier.com/x86/MOVBE.html) — Move data after swapping data bytes.
|
||||
* [**PREFETCHW**](http://www.felixcloutier.com/x86/PREFETCHW.html) — Prefetch data into cache in anticipation of write.
|
||||
* [**CLFLUSH**](http://www.felixcloutier.com/x86/CLFLUSH.html) — Flushes and invalidates a memory operand and its associated cache line from all levels of the processor’s cache hierarchy.
|
||||
* [**CLFLUSHOPT**](http://www.felixcloutier.com/x86/CLFLUSHOPT.html) — Flushes and invalidates a memory operand and its associated cache line from all levels of the processor’s cache hierarchy with optimized memory system throughput.
|
||||
* [**RDRAND**](http://www.felixcloutier.com/x86/RDRAND.html) — Retrieves a random number generated from hardware.
|
||||
* [**RDSEED**](http://www.felixcloutier.com/x86/RDSEED.html) — Seed the random number generator from hardware.
|
||||
|
||||
## User-mode extended states save/restore instructions
|
||||
|
||||
* [**XSAVE**](http://www.felixcloutier.com/x86/XSAVE.html) — Save processor extended states to memory.
|
||||
* [**XSAVEC**](http://www.felixcloutier.com/x86/XSAVEC.html) — Save processor extended states with compaction to memory.
|
||||
* [**XSAVEOPT**](http://www.felixcloutier.com/x86/XSAVEOPT.html) — Save processor extended states to memory, optimized.
|
||||
* [**XRSTOR**](http://www.felixcloutier.com/x86/XRSTOR.html) — Restore processor extended states from memory.
|
||||
* [**XGETBV**](http://www.felixcloutier.com/x86/XGETBV.html) — Reads the state of an extended control register.
|
||||
|
||||
## Bit manipulation instructions (BMI1, BMI2)
|
||||
|
||||
* [**ANDN**](http://www.felixcloutier.com/x86/ANDN.html) — Bitwise AND of first source with inverted 2nd source operands.
|
||||
* [**BEXTR**](http://www.felixcloutier.com/x86/BEXTR.html) — Contiguous bitwise extract.
|
||||
* [**BLSI**](http://www.felixcloutier.com/x86/BLSI.html) — Extract lowest set bit.
|
||||
* [**BLSMSK**](http://www.felixcloutier.com/x86/BLSMSK.html) — Set all lower bits below first set bit to 1.
|
||||
* [**BLSR**](http://www.felixcloutier.com/x86/BLSR.html) — Reset lowest set bit.
|
||||
* [**BZHI**](http://www.felixcloutier.com/x86/BZHI.html) — Zero high bits starting from specified bit position.
|
||||
* [**LZCNT**](http://www.felixcloutier.com/x86/LZCNT.html) — Count the number leading zero bits.
|
||||
* [**MULX**](http://www.felixcloutier.com/x86/MULX.html) — Unsigned multiply without affecting arithmetic flags.
|
||||
* [**PDEP**](http://www.felixcloutier.com/x86/PDEP.html) — Parallel deposit of bits using a mask.
|
||||
* [**PEXT**](http://www.felixcloutier.com/x86/PEXT.html) — Parallel extraction of bits using a mask.
|
||||
* [**RORX**](http://www.felixcloutier.com/x86/RORX.html) — Rotate right without affecting arithmetic flags.
|
||||
* [**SARX / SHLX / SHRX**](http://www.felixcloutier.com/x86/SARX:SHLX:SHRX.html) — Shift arithmetic/logic left/right without affecting flags.
|
||||
* [**TZCNT**](http://www.felixcloutier.com/x86/TZCNT.html) — Count the number trailing zero bits.
|
||||
|
||||
## x87 FPU overview
|
||||
|
||||
* x87 FPU state is aliased to the MMX state, care must be taken when making transitions to MMX instructions to prevent incoherent or unexpected results.
|
||||
|
||||
## x87 FPU data transfer instructions
|
||||
|
||||
* [**FLD**](http://www.felixcloutier.com/x86/FLD.html) — Load floating-point value.
|
||||
* [**FST / FSTP**](http://www.felixcloutier.com/x86/FST:FSTP.html) — Store floating-point value without/with pop.
|
||||
* [**FILD**](http://www.felixcloutier.com/x86/FILD.html) — Load integer.
|
||||
* [**FIST / FISTP**](http://www.felixcloutier.com/x86/FIST:FISTP.html) — Store integer with/without pop.
|
||||
* [**FBLD**](http://www.felixcloutier.com/x86/FBLD.html) — Load BCD.
|
||||
* [**FBSTP**](http://www.felixcloutier.com/x86/FBSTP.html) — Store BCD and pop.
|
||||
* [**FXCH**](http://www.felixcloutier.com/x86/FXCH.html) — Exchange registers.
|
||||
* [**FCMOVcc**](http://www.felixcloutier.com/x86/FCMOVcc.html) — Floating-point conditional move.
|
||||
|
||||
## x87 FPU basic arithmetic instructions
|
||||
|
||||
* [**FADD / FADDP / FIADD**](http://www.felixcloutier.com/x86/FADD:FADDP:FIADD.html) — Add floating-point.
|
||||
* [**FSUB / FSUBP / FISUB**](http://www.felixcloutier.com/x86/FSUB:FSUBP:FISUB.html) — Subtract floating-point.
|
||||
* [**FSUBR / FSUBRP / FISUBR**](http://www.felixcloutier.com/x86/FSUBR:FSUBRP:FISUBR.html) — Subtract floating-point reverse.
|
||||
* [**FMUL / FMULP / FIMUL**](http://www.felixcloutier.com/x86/FMUL:FMULP:FIMUL.html) — Multiply floating-point.
|
||||
* [**FDIV / FDIVP / FIDIV**](http://www.felixcloutier.com/x86/FDIV:FDIVP:FIDIV.html) — Divide floating-point.
|
||||
* [**FDIVR / FDIVRP / FIDIVR**](http://www.felixcloutier.com/x86/FDIVR:FDIVRP:FIDIVR.html) — Divide floating-point reverse.
|
||||
* [**FPREM**](http://www.felixcloutier.com/x86/FPREM.html) — Partial remainder.
|
||||
* [**FPREM1**](http://www.felixcloutier.com/x86/FPREM1.html) — IEEE Partial remainder.
|
||||
* [**FABS**](http://www.felixcloutier.com/x86/FABS.html) — Absolute value.
|
||||
* [**FCHS**](http://www.felixcloutier.com/x86/FCHS.html) — Change sign.
|
||||
* [**FRNDINT**](http://www.felixcloutier.com/x86/FRNDINT.html) — Round to integer.
|
||||
* [**FSCALE**](http://www.felixcloutier.com/x86/FSCALE.html) — Scale by power of two.
|
||||
* [**FSQRT**](http://www.felixcloutier.com/x86/FSQRT.html) — Square root.
|
||||
* [**FXTRACT**](http://www.felixcloutier.com/x86/FXTRACT.html) — Extract exponent and significand.
|
||||
|
||||
## x87 FPU comparison instructions
|
||||
|
||||
* [**FCOM / FCOMP / FCOMPP**](http://www.felixcloutier.com/x86/FCOM:FCOMP:FCOMPP.html) — Compare floating-point.
|
||||
* [**FUCOM / FUCOMP / FUCOMPP**](http://www.felixcloutier.com/x86/FUCOM:FUCOMP:FUCOMPP.html) — Unordered compare floating-point.
|
||||
* [**FICOM / FICOMP**](http://www.felixcloutier.com/x86/FICOM:FICOMP.html) — Compare integer.
|
||||
* [**FCOMI / FCOMIP / FUCOMI / FUCOMIP**](http://www.felixcloutier.com/x86/FCOMI:FCOMIP:FUCOMI:FUCOMIP.html) — Compare floating-point and set `rflags`.
|
||||
* [**FTST**](http://www.felixcloutier.com/x86/FTST.html) — Test floating-point (compare with 0.0).
|
||||
* [**FXAM**](http://www.felixcloutier.com/x86/FXAM.html) — Examine floating-point.
|
||||
|
||||
## x87 FPU transcendental instructions
|
||||
|
||||
* [**FSIN**](http://www.felixcloutier.com/x86/FSIN.html) — Sine.
|
||||
* [**FCOS**](http://www.felixcloutier.com/x86/FCOS.html) — Cosine.
|
||||
* [**FSINCOS**](http://www.felixcloutier.com/x86/FSINCOS.html) — Sine and cosine.
|
||||
* [**FPTAN**](http://www.felixcloutier.com/x86/FPTAN.html) — Partial tangent.
|
||||
* [**FPATAN**](http://www.felixcloutier.com/x86/FPATAN.html) — Partial arctangent.
|
||||
* [**F2XM1**](http://www.felixcloutier.com/x86/F2XM1.html) — 2x − 1.
|
||||
* [**FYL2X**](http://www.felixcloutier.com/x86/FYL2X.html) — y ∗ log2x.
|
||||
* [**FYL2XP1**](http://www.felixcloutier.com/x86/FYL2XP1.html) — y ∗ log2(x + 1).
|
||||
|
||||
## x87 FPU load constants instructions
|
||||
|
||||
* [**FLD1 / FLDL2T / FLDL2E / FLDPI / FLDLG2 / FLDLN2 / FLDZ**](http://www.felixcloutier.com/x86/FLD1:FLDL2T:FLDL2E:FLDPI:FLDLG2:FLDLN2:FLDZ.html) — Load constants.
|
||||
|
||||
## x87 FPU control instructions
|
||||
|
||||
* [**FINCSTP**](http://www.felixcloutier.com/x86/FINCSTP.html) — Increment FPU register stack pointer.
|
||||
* [**FDECSTP**](http://www.felixcloutier.com/x86/FDECSTP.html) — Decrement FPU register stack pointer.
|
||||
* [**FFREE**](http://www.felixcloutier.com/x86/FFREE.html) — Free floating-point register.
|
||||
* [**FINIT / FNINIT**](http://www.felixcloutier.com/x86/FINIT:FNINIT.html) — Initialize FPU.
|
||||
* [**FCLEX / FNCLEX**](http://www.felixcloutier.com/x86/FCLEX:FNCLEX.html) — Clear floating-point exception flags.
|
||||
* [**FSTCW / FNSTCW**](http://www.felixcloutier.com/x86/FSTCW:FNSTCW.html) — Store FPU control word.
|
||||
* [**FLDCW**](http://www.felixcloutier.com/x86/FLDCW.html) — Load FPU control word.
|
||||
* [**FSTENV / FNSTENV**](http://www.felixcloutier.com/x86/FSTENV:FNSTENV.html) — Store FPU environment.
|
||||
* [**FLDENV**](http://www.felixcloutier.com/x86/FLDENV.html) — Load FPU environment.
|
||||
* [**FSAVE / FNSAVE**](http://www.felixcloutier.com/x86/FSAVE:FNSAVE.html) — Save FPU state.
|
||||
* [**FRSTOR**](http://www.felixcloutier.com/x86/FRSTOR.html) — Restore FPU state.
|
||||
* [**FSTSW / FNSTSW**](http://www.felixcloutier.com/x86/FSTSW:FNSTSW.html) — Store FPU status word.
|
||||
* [**WAIT / FWAIT**](http://www.felixcloutier.com/x86/WAIT:FWAIT.html) — Wait for FPU.
|
||||
* [**FNOP**](http://www.felixcloutier.com/x86/FNOP.html) — FPU no operation.
|
||||
|
||||
## x87 FPU and SIMD state management instructions
|
||||
|
||||
* [**FXSAVE**](http://www.felixcloutier.com/x86/FXSAVE.html) — Save x87 FPU and SIMD state.
|
||||
* [**FXRSTOR**](http://www.felixcloutier.com/x86/FXRSTOR.html) — Restore x87 FPU and SIMD state.
|
||||
|
||||
## MMX overview
|
||||
|
||||
* SIMD execution model to handle 64-bit packed integer data.
|
||||
* Eight new 64-bit data registers, called MMX registers.
|
||||
* Three new packed data types:
|
||||
* 64-bit packed byte integers (signed and unsigned)
|
||||
* 64-bit packed word integers (signed and unsigned)
|
||||
* 64-bit packed doubleword integers (signed and unsigned)
|
||||
* MMX state is aliased to the x87 FPU state, care must be taken when making transitions to x87 FPU instructions to prevent incoherent or unexpected results.
|
||||
|
||||
## MMX data transfer instructions
|
||||
|
||||
* [**MOVD / MOVQ**](http://www.felixcloutier.com/x86/MOVD:MOVQ.html) — Move doubleword/quadword from/to MMX registers.
|
||||
|
||||
## MMX conversion instructions
|
||||
|
||||
* [**PACKSSWB / PACKSSDW**](http://www.felixcloutier.com/x86/PACKSSWB:PACKSSDW.html) — Pack words/doublewords into bytes with signed saturation.
|
||||
* [**PACKUSWB**](http://www.felixcloutier.com/x86/PACKUSWB.html) — Pack words into bytes with unsigned saturation.
|
||||
* [**PUNPCKHBW / PUNPCKHWD / PUNPCKHDQ**](http://www.felixcloutier.com/x86/PUNPCKHBW:PUNPCKHWD:PUNPCKHDQ:PUNPCKHQDQ.html) — Unpack high-order bytes/words/doublewords.
|
||||
* [**PUNPCKLBW / PUNPCKLWD / PUNPCKLDQ**](http://www.felixcloutier.com/x86/PUNPCKLBW:PUNPCKLWD:PUNPCKLDQ:PUNPCKLQDQ.html) — Unpack low-order bytes/words/doublewords.
|
||||
|
||||
## MMX packed arithmetic instructions
|
||||
|
||||
* [**PADDB / PADDW / PADDD**](http://www.felixcloutier.com/x86/PADDB:PADDW:PADDD:PADDQ.html) — Add packed byte/word/doubleword integers.
|
||||
* [**PADDSB / PADDSW**](http://www.felixcloutier.com/x86/PADDSB:PADDSW.html) — Add packed signed byte/word integers with signed saturation.
|
||||
* [**PADDUSB / PADDUSW**](http://www.felixcloutier.com/x86/PADDUSB:PADDUSW.html) — Add packed unsigned byte/word integers with unsigned saturation.
|
||||
* [**PSUBB / PSUBW / PSUBD**](http://www.felixcloutier.com/x86/PSUBB:PSUBW:PSUBD.html) — Subtract packed byte/word/doubleword integers.
|
||||
* [**PSUBSB / PSUBSW**](http://www.felixcloutier.com/x86/PSUBSB:PSUBSW.html) — Subtract packed signed byte/word integers with signed saturation.
|
||||
* [**PSUBUSB / PSUBUSW**](http://www.felixcloutier.com/x86/PSUBUSB:PSUBUSW.html) — Subtract packed unsigned byte/word integers with unsigned saturation.
|
||||
* [**PMULHW**](http://www.felixcloutier.com/x86/PMULHW.html) — Multiply packed signed word integers and store high result.
|
||||
* [**PMULLW**](http://www.felixcloutier.com/x86/PMULLW.html) — Multiply packed signed word integers and store low result.
|
||||
* [**PMADDWD**](http://www.felixcloutier.com/x86/PMADDWD.html) — Multiply and add packed word integers.
|
||||
|
||||
## MMX comparison instructions
|
||||
|
||||
* [**PCMPEQB / PCMPEQW / PCMPEQD**](http://www.felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html) — Compare packed bytes/words/doublewords for equal.
|
||||
* [**PCMPGTB / PCMPGTW / PCMPGTD**](http://www.felixcloutier.com/x86/PCMPGTB:PCMPGTW:PCMPGTD.html) — Compare packed signed byte/word/doubleword integers for greater than.
|
||||
|
||||
## MMX logical instructions
|
||||
|
||||
* [**PAND**](http://www.felixcloutier.com/x86/PAND.html) — Bitwise logical AND.
|
||||
* [**PANDN**](http://www.felixcloutier.com/x86/PANDN.html) — Bitwise logical AND NOT.
|
||||
* [**POR**](http://www.felixcloutier.com/x86/POR.html) — Bitwise logical OR.
|
||||
* [**PXOR**](http://www.felixcloutier.com/x86/PXOR.html) — Bitwise logical exclusive OR.
|
||||
|
||||
## MMX shift and rotate instructions
|
||||
|
||||
* [**PSLLW / PSLLD / PSLLQ**](http://www.felixcloutier.com/x86/PSLLW:PSLLD:PSLLQ.html) — Shift packed words/doublewords/quadwoards left logical.
|
||||
* [**PSRLW / PSRLD / PSRLQ**](http://www.felixcloutier.com/x86/PSRLW:PSRLD:PSRLQ.html) — Shift packed words/doublewords/quadwords right logical.
|
||||
* [**PSRAW / PSRAD**](http://www.felixcloutier.com/x86/PSRAW:PSRAD:PSRAQ.html) — Shift packed words/doublewords right arithmetic.
|
||||
|
||||
## MMX state management instructions
|
||||
|
||||
* [**EMMS**](http://www.felixcloutier.com/x86/EMMS.html) — Empty MMX state.
|
||||
|
||||
## SSE overview
|
||||
|
||||
* Expand the SIMD execution model by adding facilities for handling packed and scalar single-precision floating-point values contained in 128-bit registers.
|
||||
* Sixteen (eight for 32-bit mode) new 128-bit packed single-precision floating-point XMM registers available.
|
||||
* 128-bit packed and scalar single-precision floating-point instructions.
|
||||
* Enhancements to MMX instruction set with new operations on packed integer operands located in MMX registers.
|
||||
* Explicit prefetching of data, control of the cacheability of data, control of the
|
||||
ordering of store operations.
|
||||
|
||||
## SSE data transfer instructions
|
||||
|
||||
* [**MOVAPS**](http://www.felixcloutier.com/x86/MOVAPS.html) — Move four aligned packed single-precision floating-point values between XMM registers or between XMM register and memory.
|
||||
* [**MOVUPS**](http://www.felixcloutier.com/x86/MOVUPS.html) — Move four unaligned packed single-precision floating-point values between XMM registers or between XMM register and memory.
|
||||
* [**MOVHPS**](http://www.felixcloutier.com/x86/MOVHPS.html) — Move two packed single-precision floating-point values to an from the high quadword of an XMM register and memory.
|
||||
* [**MOVHLPS**](http://www.felixcloutier.com/x86/MOVHLPS.html) — Move two packed single-precision floating-point values from the high quadword of an XMM register to the low quadword of another XMM register.
|
||||
* [**MOVLPS**](http://www.felixcloutier.com/x86/MOVLPS.html) — Move two packed single-precision floating-point values to an from the low quadword of an XMM register and memory.
|
||||
* [**MOVLHPS**](http://www.felixcloutier.com/x86/MOVLHPS.html) — Move two packed single-precision floating-point values from the low quadword of an XMM register to the high quadword of another XMM register.
|
||||
* [**MOVMSKPS**](http://www.felixcloutier.com/x86/MOVMSKPS.html) — Extract sign mask from four packed single-precision floating-point values.
|
||||
* [**MOVSS**](http://www.felixcloutier.com/x86/MOVSS.html) — Move scalar single-precision floating-point value between XMM registers or between an XMM register and memory.
|
||||
|
||||
## SSE packed arithmetic instructions
|
||||
|
||||
* [**ADDPS**](http://www.felixcloutier.com/x86/ADDPS.html) — Add packed single-precision floating-point values.
|
||||
* [**ADDSS**](http://www.felixcloutier.com/x86/ADDSS.html) — Add scalar single-precision floating-point values.
|
||||
* [**SUBPS**](http://www.felixcloutier.com/x86/SUBPS.html) — Subtract packed single-precision floating-point values.
|
||||
* [**SUBSS**](http://www.felixcloutier.com/x86/SUBSS.html) — Subtract scalar single-precision floating-point values.
|
||||
* [**MULPS**](http://www.felixcloutier.com/x86/MULPS.html) — Multiply packed single-precision floating-point values.
|
||||
* [**MULSS**](http://www.felixcloutier.com/x86/MULSS.html) — Multiply scalar single-precision floating-point values.
|
||||
* [**DIVPS**](http://www.felixcloutier.com/x86/DIVPS.html) — Divide packed single-precision floating-point values.
|
||||
* [**DIVSS**](http://www.felixcloutier.com/x86/DIVSS.html) — Divide scalar single-precision floating-point values.
|
||||
* [**RCPPS**](http://www.felixcloutier.com/x86/RCPPS.html) — Compute reciprocals of packed single-precision floating-point values.
|
||||
* [**RCPSS**](http://www.felixcloutier.com/x86/RCPSS.html) — Compute reciprocal of scalar single-precision floating-point values.
|
||||
* [**SQRTPS**](http://www.felixcloutier.com/x86/SQRTPS.html) — Compute square roots of packed single-precision floating-point values.
|
||||
* [**SQRTSS**](http://www.felixcloutier.com/x86/SQRTSS.html) — Compute square root of scalar single-precision floating-point values.
|
||||
* [**RSQRTPS**](http://www.felixcloutier.com/x86/RSQRTPS.html) — Compute reciprocals of square roots of packed single-precision floating-point values.
|
||||
* [**RSQRTSS**](http://www.felixcloutier.com/x86/RSQRTSS.html) — Compute reciprocal of square root of scalar single-precision floating-point values.
|
||||
* [**MAXPS**](http://www.felixcloutier.com/x86/MAXPS.html) — Return maximum packed single-precision floating-point values.
|
||||
* [**MAXSS**](http://www.felixcloutier.com/x86/MAXSS.html) — Return maximum scalar single-precision floating-point values.
|
||||
* [**MINPS**](http://www.felixcloutier.com/x86/MINPS.html) — Return minimum packed single-precision floating-point values.
|
||||
* [**MINSS**](http://www.felixcloutier.com/x86/MINSS.html) — Return minimum scalar single-precision floating-point values.
|
||||
|
||||
## SSE comparison instructions
|
||||
|
||||
* [**CMPPS**](http://www.felixcloutier.com/x86/CMPPS.html) — Compare packed single-precision floating-point values.
|
||||
* [**CMPSS**](http://www.felixcloutier.com/x86/CMPSS.html) — Compare scalar single-precision floating-point values.
|
||||
* [**COMISS**](http://www.felixcloutier.com/x86/COMISS.html) — Perform ordered comparison of scalar single-precision floating-point values and set flags in `rflags` register.
|
||||
* [**UCOMISS**](http://www.felixcloutier.com/x86/UCOMISS.html) — Perform unordered comparison of scalar single-precision floating-point values and set flags in `rflags` register.
|
||||
|
||||
## SSE logical instructions
|
||||
|
||||
* [**ANDPS**](http://www.felixcloutier.com/x86/ANDPS.html) — Perform bitwise logical AND of packed single-precision floating-point values.
|
||||
* [**ANDNPS**](http://www.felixcloutier.com/x86/ANDNPS.html) — Perform bitwise logical AND NOT of packed single-precision floating-point values.
|
||||
* [**ORPS**](http://www.felixcloutier.com/x86/ORPS.html) — Perform bitwise logical OR of packed single-precision floating-point values.
|
||||
* [**XORPS**](http://www.felixcloutier.com/x86/XORPS.html) — Perform bitwise logical XOR of packed single-precision floating-point values.
|
||||
|
||||
## SSE shuffle and unpack instructions
|
||||
|
||||
* [**SHUFPS**](http://www.felixcloutier.com/x86/SHUFPS.html) — Shuffles values in packed single-precision floating-point operands.
|
||||
* [**UNPCKHPS**](http://www.felixcloutier.com/x86/UNPCKHPS.html) — Unpacks and interleaves the two high-order values from two single-precision floating-point operands.
|
||||
* [**UNPCKLPS**](http://www.felixcloutier.com/x86/UNPCKLPS.html) — Unpacks and interleaves the two low-order values from two single-precision floating-point operands.
|
||||
|
||||
## SSE conversion instructions
|
||||
|
||||
* [**CVTPI2PS**](http://www.felixcloutier.com/x86/CVTPI2PS.html) — Convert packed doubleword integers to packed single-precision floating-point values.
|
||||
* [**CVTSI2SS**](http://www.felixcloutier.com/x86/CVTSI2SS.html) — Convert doubleword integer to scalar single-precision floating-point value.
|
||||
* [**CVTPS2PI**](http://www.felixcloutier.com/x86/CVTPS2PI.html) — Convert packed single-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTTPS2PI**](http://www.felixcloutier.com/x86/CVTTPS2PI.html) — Convert with truncation packed single-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTSS2SI**](http://www.felixcloutier.com/x86/CVTSS2SI.html) — Convert a scalar single-precision floating-point value to a doubleword integer.
|
||||
* [**CVTTSS2SI**](http://www.felixcloutier.com/x86/CVTTSS2SI.html) — Convert with truncation a scalar single-precision floating-point value to a scalar doubleword integer.
|
||||
|
||||
## SSE MXCSR management instructions
|
||||
|
||||
* [**LDMXCSR**](http://www.felixcloutier.com/x86/LDMXCSR.html) — Load MXCSR register.
|
||||
* [**STMXCSR**](http://www.felixcloutier.com/x86/STMXCSR.html) — Save MXCSR register state.
|
||||
|
||||
## SSE 64-bit integer instructions (MMX enhancements)
|
||||
|
||||
* [**PAVGB / PAVGW**](http://www.felixcloutier.com/x86/PAVGB:PAVGW.html) — Compute average of packed unsigned byte integers.
|
||||
* [**PEXTRW**](http://www.felixcloutier.com/x86/PEXTRW.html) — Extract word.
|
||||
* [**PINSRW**](http://www.felixcloutier.com/x86/PINSRW.html) — Insert word.
|
||||
* [**PMAXUB**](http://www.felixcloutier.com/x86/PMAXUB:PMAXUW.html) — Maximum of packed unsigned byte integers.
|
||||
* [**PMAXSW**](http://www.felixcloutier.com/x86/PMAXSB:PMAXSW:PMAXSD:PMAXSQ.html) — Maximum of packed signed word integers.
|
||||
* [**PMINUB**](http://www.felixcloutier.com/x86/PMINUB:PMINUW.html) — Minimum of packed unsigned byte integers.
|
||||
* [**PMINSW**](http://www.felixcloutier.com/x86/PMINSB:PMINSW.html) — Minimum of packed signed word integers.
|
||||
* [**PMOVMSKB**](http://www.felixcloutier.com/x86/PMOVMSKB.html) — Move byte mask.
|
||||
* [**PMULHUW**](http://www.felixcloutier.com/x86/PMULHUW.html) — Multiply packed unsigned integers and store high result.
|
||||
* [**PSADBW**](http://www.felixcloutier.com/x86/MPSADBW.html) — Compute sum of absolute differences.
|
||||
* [**PSHUFW**](http://www.felixcloutier.com/x86/PSHUFW.html) — Shuffle packed integer word in MMX register.
|
||||
|
||||
## SSE cacheability control, prefetch and ordering instructions
|
||||
|
||||
* [**MASKMOVQ**](http://www.felixcloutier.com/x86/MASKMOVQ.html) — Non-temporal store of selected bytes from an MMX register into memory.
|
||||
* [**MOVNTQ**](http://www.felixcloutier.com/x86/MOVNTQ.html) — Non-temporal store of quadword from an MMX register into memory.
|
||||
* [**MOVNTPS**](http://www.felixcloutier.com/x86/MOVNTPS.html) — Non-temporal store of four packed single-precision floating-point values from an XMM register into memory.
|
||||
* [**PREFETCHh**](http://www.felixcloutier.com/x86/PREFETCHh.html) — Load 32 or more of bytes from memory to a selected level of the processor’s cache hierarchy.
|
||||
* [**SFENCE**](http://www.felixcloutier.com/x86/SFENCE.html) — Serializes store operations.
|
||||
|
||||
## SSE2 overview
|
||||
|
||||
* Packed and scalar 128-bit double-precision floating-point instructions.
|
||||
* Additional 64-bit and 128-bit packed byte/word/doubleword/quadword integers instructions.
|
||||
* 128-bit versions of integer instructions introduced with MMX and SSE.
|
||||
* Additional cacheability-control and instruction-ordering instructions.
|
||||
|
||||
## SSE2 FP64 data movement instructions
|
||||
|
||||
* [**MOVAPD**](http://www.felixcloutier.com/x86/MOVAPD.html) — Move two aligned packed double-precision floating-point values between XMM registers or between and XMM register and memory.
|
||||
* [**MOVUPD**](http://www.felixcloutier.com/x86/MOVUPD.html) — Move two unaligned packed double-precision floating-point values between XMM registers or between and XMM register and memory.
|
||||
* [**MOVHPD**](http://www.felixcloutier.com/x86/MOVHPD.html) — Move high packed double-precision floating-point value to an from the high quadword of an XMM register and memory.
|
||||
* [**MOVLPD**](http://www.felixcloutier.com/x86/MOVLPD.html) — Move low packed single-precision floating-point value to an from the low quadword of an XMM register and memory.
|
||||
* [**MOVMSKPD**](http://www.felixcloutier.com/x86/MOVMSKPD.html) — Extract sign mask from two packed double-precision floating-point values.
|
||||
* [**MOVSD**](http://www.felixcloutier.com/x86/MOVSD.html) — Move scalar double-precision floating-point value between XMM registers or between an XMM register and memory.
|
||||
|
||||
## SSE2 FP64 packed arithmetic instructions
|
||||
|
||||
* [**ADDPD**](http://www.felixcloutier.com/x86/ADDPD.html) — Add packed double-precision floating-point values.
|
||||
* [**ADDSD**](http://www.felixcloutier.com/x86/ADDSD.html) — Add scalar double precision floating-point values.
|
||||
* [**SUBPD**](http://www.felixcloutier.com/x86/SUBPD.html) — Subtract packed double-precision floating-point values.
|
||||
* [**SUBSD**](http://www.felixcloutier.com/x86/SUBSD.html) — Subtract scalar double-precision floating-point values.
|
||||
* [**MULPD**](http://www.felixcloutier.com/x86/MULPD.html) — Multiply packed double-precision floating-point values.
|
||||
* [**MULSD**](http://www.felixcloutier.com/x86/MULSD.html) — Multiply scalar double-precision floating-point values.
|
||||
* [**DIVPD**](http://www.felixcloutier.com/x86/DIVPD.html) — Divide packed double-precision floating-point values.
|
||||
* [**DIVSD**](http://www.felixcloutier.com/x86/DIVSD.html) — Divide scalar double-precision floating-point values.
|
||||
* [**SQRTPD**](http://www.felixcloutier.com/x86/SQRTPD.html) — Compute packed square roots of packed double-precision floating-point values.
|
||||
* [**SQRTSD**](http://www.felixcloutier.com/x86/SQRTSD.html) — Compute scalar square root of scalar double-precision floating-point values.
|
||||
* [**MAXPD**](http://www.felixcloutier.com/x86/MAXPD.html) — Return maximum packed double-precision floating-point values.
|
||||
* [**MAXSD**](http://www.felixcloutier.com/x86/MAXSD.html) — Return maximum scalar double-precision floating-point values.
|
||||
* [**MINPD**](http://www.felixcloutier.com/x86/MINPD.html) — Return minimum packed double-precision floating-point values.
|
||||
* [**MINSD**](http://www.felixcloutier.com/x86/MINSD.html) — Return minimum scalar double-precision floating-point values.
|
||||
|
||||
## SSE2 FP64 logical instructions
|
||||
|
||||
* [**ANDPD**](http://www.felixcloutier.com/x86/ANDPD.html) — Perform bitwise logical AND of packed double-precision floating-point values.
|
||||
* [**ANDNPD**](http://www.felixcloutier.com/x86/ANDNPD.html) — Perform bitwise logical AND NOT of packed double-precision floating-point values.
|
||||
* [**ORPD**](http://www.felixcloutier.com/x86/ORPD.html) — Perform bitwise logical OR of packed double-precision floating-point values.
|
||||
* [**XORPD**](http://www.felixcloutier.com/x86/XORPD.html) — Perform bitwise logical XOR of packed double-precision floating-point values.
|
||||
|
||||
## SSE2 FP64 compare instructions
|
||||
|
||||
* [**CMPPD**](http://www.felixcloutier.com/x86/CMPPD.html) — Compare packed double-precision floating-point values.
|
||||
* [**CMPSD**](http://www.felixcloutier.com/x86/CMPSD.html) — Compare scalar double-precision floating-point values.
|
||||
* [**COMISD**](http://www.felixcloutier.com/x86/COMISD.html) — Perform ordered comparison of scalar double-precision floating-point values and set flags in `rflags` register.
|
||||
* [**UCOMISD**](http://www.felixcloutier.com/x86/UCOMISD.html) — Perform unordered comparison of scalar double-precision floating-point values and set flags in `rflags` register.
|
||||
|
||||
## SSE2 FP64 shuffle and unpack instructions
|
||||
|
||||
* [**SHUFPD**](http://www.felixcloutier.com/x86/SHUFPD.html) — Shuffles values in packed double-precision floating-point operands.
|
||||
* [**UNPCKHPD**](http://www.felixcloutier.com/x86/UNPCKHPD.html) — Unpacks and interleaves the high values from two packed double-precision floating-point operands.
|
||||
* [**UNPCKLPD**](http://www.felixcloutier.com/x86/UNPCKLPD.html) — Unpacks and interleaves the low values from two packed double-precision floating-point operands.
|
||||
|
||||
## SSE2 FP64 conversion instructions
|
||||
|
||||
* [**CVTPD2PI**](http://www.felixcloutier.com/x86/CVTPD2PI.html) — Convert packed double-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTTPD2PI**](http://www.felixcloutier.com/x86/CVTTPD2PI.html) — Convert with truncation packed double-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTPI2PD**](http://www.felixcloutier.com/x86/CVTPI2PD.html) — Convert packed doubleword integers to packed double-precision floating-point values.
|
||||
* [**CVTPD2DQ**](http://www.felixcloutier.com/x86/CVTPD2DQ.html) — Convert packed double-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTTPD2DQ**](http://www.felixcloutier.com/x86/CVTTPD2DQ.html) — Convert with truncation packed double-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTDQ2PD**](http://www.felixcloutier.com/x86/CVTDQ2PD.html) — Convert packed doubleword integers to packed double-precision floating-point values.
|
||||
* [**CVTPS2PD**](http://www.felixcloutier.com/x86/CVTPS2PD.html) — Convert packed single-precision floating-point values to packed double-precision floating-point values.
|
||||
* [**CVTPD2PS**](http://www.felixcloutier.com/x86/CVTPS2PD.html) — Convert packed double-precision floating-point values to packed single-precision floating-point values.
|
||||
* [**CVTSS2SD**](http://www.felixcloutier.com/x86/CVTSS2SD.html) — Convert scalar single-precision floating-point values to scalar double-precision floating-point values.
|
||||
* [**CVTSD2SS**](http://www.felixcloutier.com/x86/CVTSD2SS.html) — Convert scalar double-precision floating-point values to scalar single-precision floating-point values.
|
||||
* [**CVTSD2SI**](http://www.felixcloutier.com/x86/CVTSD2SI.html) — Convert scalar double-precision floating-point values to a doubleword integer.
|
||||
* [**CVTTSD2SI**](http://www.felixcloutier.com/x86/CVTTSD2SI.html) — Convert with truncation scalar double-precision floating-point values to scalar doubleword integers.
|
||||
* [**CVTSI2SD**](http://www.felixcloutier.com/x86/CVTSI2SD.html) — Convert doubleword integer to scalar double-precision floating-point value.
|
||||
|
||||
## SSE2 FP32 instructions (SSE enhancements)
|
||||
|
||||
* [**CVTDQ2PS**](http://www.felixcloutier.com/x86/CVTDQ2PS.html) — Convert packed doubleword integers to packed single-precision floating-point values.
|
||||
* [**CVTPS2DQ**](http://www.felixcloutier.com/x86/CVTPS2DQ.html) — Convert packed single-precision floating-point values to packed doubleword integers.
|
||||
* [**CVTTPS2DQ**](http://www.felixcloutier.com/x86/CVTTPS2DQ.html) — Convert with truncation packed single-precision floating-point values to packed doubleword integers.
|
||||
|
||||
## SSE2 integer instructions
|
||||
|
||||
* [**MOVDQA**](http://www.felixcloutier.com/x86/MOVDQA:VMOVDQA32:VMOVDQA64.html) — Move aligned double quadword.
|
||||
* [**MOVDQU**](http://www.felixcloutier.com/x86/MOVDQU:VMOVDQU8:VMOVDQU16:VMOVDQU32:VMOVDQU64.html) — Move unaligned double quadword.
|
||||
* [**MOVQ2DQ**](http://www.felixcloutier.com/x86/MOVQ2DQ.html) — Move quadword integer from MMX to XMM registers.
|
||||
* [**MOVDQ2Q**](http://www.felixcloutier.com/x86/MOVDQ2Q.html) — Move quadword integer from XMM to MMX registers.
|
||||
* [**PMULUDQ**](http://www.felixcloutier.com/x86/PMULUDQ.html) — Multiply packed unsigned doubleword integers.
|
||||
* [**PADDQ**](http://www.felixcloutier.com/x86/PADDB:PADDW:PADDD:PADDQ.html) — Add packed quadword integers.
|
||||
* [**PSUBQ**](http://www.felixcloutier.com/x86/PSUBQ.html) — Subtract packed quadword integers.
|
||||
* [**PSHUFLW**](http://www.felixcloutier.com/x86/PSHUFLW.html) — Shuffle packed low words.
|
||||
* [**PSHUFHW**](http://www.felixcloutier.com/x86/PSHUFHW.html) — Shuffle packed high words.
|
||||
* [**PSHUFD**](http://www.felixcloutier.com/x86/PSHUFD.html) — Shuffle packed doublewords.
|
||||
* [**PSLLDQ**](http://www.felixcloutier.com/x86/PSLLDQ.html) — Shift double quadword left logical.
|
||||
* [**PSRLDQ**](http://www.felixcloutier.com/x86/PSRLDQ.html) — Shift double quadword right logical.
|
||||
* [**PUNPCKHQDQ**](http://www.felixcloutier.com/x86/PUNPCKHBW:PUNPCKHWD:PUNPCKHDQ:PUNPCKHQDQ.html) — Unpack high quadwords.
|
||||
* [**PUNPCKLQDQ**](http://www.felixcloutier.com/x86/PUNPCKLBW:PUNPCKLWD:PUNPCKLDQ:PUNPCKLQDQ.html) — Unpack low quadwords.
|
||||
|
||||
## SSE2 cacheability control and ordering instructions
|
||||
|
||||
* [**CLFLUSH**](http://www.felixcloutier.com/x86/CLFLUSH.html) — Flush cacheline.
|
||||
* [**LFENCE**](http://www.felixcloutier.com/x86/LFENCE.html) — Serializes load operations.
|
||||
* [**MFENCE**](http://www.felixcloutier.com/x86/MFENCE.html) — Serializes load and store operations.
|
||||
* [**PAUSE**](http://www.felixcloutier.com/x86/PAUSE.html) — Improves the performance of “spin-wait loops”.
|
||||
* [**MASKMOVDQU**](http://www.felixcloutier.com/x86/MASKMOVDQU.html) — Non-temporal store of selected bytes from an XMM register into memory.
|
||||
* [**MOVNTPD**](http://www.felixcloutier.com/x86/MOVNTPD.html) — Non-temporal store of two packed double-precision floating-point values from an XMM register into memory.
|
||||
* [**MOVNTDQ**](http://www.felixcloutier.com/x86/MOVNTDQ.html) — Non-temporal store of double quadword from an XMM register into memory.
|
||||
* [**MOVNTI**](http://www.felixcloutier.com/x86/MOVNTI.html) — Non-temporal store of a doubleword from a general-purpose register into memory.
|
||||
|
||||
## References
|
||||
|
||||
* 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/
|
||||
|
||||
## Instruction tables
|
||||
|
||||
* http://www.agner.org/optimize/instruction_tables.pdf
|
||||
|
||||
## Examples
|
||||
|
||||
* 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
|
||||
|
||||
## Utils
|
||||
|
||||
* 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
|
||||
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
|
||||
@@ -1,16 +0,0 @@
|
||||
global _start
|
||||
|
||||
section .data
|
||||
text_message: db "Welcome to NASM!", 0xA
|
||||
|
||||
section .text
|
||||
_start:
|
||||
mov rax, 1 ; write
|
||||
mov rdi, 1 ; stdout
|
||||
mov rsi, text_message
|
||||
mov rdx, 17 ; длина строки в байтах
|
||||
syscall
|
||||
|
||||
mov rax, 60 ; exit
|
||||
mov rdi, 1 ; код возврата = 0
|
||||
syscall
|
||||
31
nasm.code-workspace
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "casm"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,11 +5,11 @@
|
||||
"label": "asm64",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${fileDirname}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/${fileBasenameNoExtension};",
|
||||
"nasm -F dwarf -g -f elf64 -i ${fileDirname} -o $rawfilename.o ${file};",
|
||||
"ld -m elf_x86_64 -o $rawfilename $rawfilename.o;"
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"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": {
|
||||
@@ -27,17 +27,16 @@
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"label": "asm64+gcc",
|
||||
"type": "shell",
|
||||
"command": [
|
||||
"builddir=${fileDirname}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/${fileBasenameNoExtension};",
|
||||
"nasm -F dwarf -g -f elf64 -i ${fileDirname} -o $rawfilename.o ${file};",
|
||||
"gcc -m64 -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"builddir=${workspaceFolder}/build;",
|
||||
"mkdir -p $builddir;",
|
||||
"rawfilename=$builddir/10_to_2;",
|
||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/10_to_2.asm;",
|
||||
"gcc -o $rawfilename $rawfilename.o;"
|
||||
],
|
||||
"problemMatcher": {
|
||||
"pattern": {
|
||||
"regexp": "error"
|
||||
@@ -49,9 +48,6 @@
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||