Сложение двух чисел
This commit is contained in:
2
casm/.vscode/launch.json
vendored
2
casm/.vscode/launch.json
vendored
@@ -30,7 +30,7 @@
|
|||||||
"description": "Enable pretty-printing for gdb",
|
"description": "Enable pretty-printing for gdb",
|
||||||
"text": "-enable-pretty-printing",
|
"text": "-enable-pretty-printing",
|
||||||
"ignoreFailures": true
|
"ignoreFailures": true
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ extern a
|
|||||||
|
|
||||||
section .text
|
section .text
|
||||||
main:
|
main:
|
||||||
push rbx ; сохранить rbx
|
push rbx
|
||||||
call scan ; прочитать первое число
|
call scan ; прочитать первое число
|
||||||
mov rbx, rax ; сохранить первое число
|
mov rbx, rax ; сохранить первое число
|
||||||
call scan ; прочитать второе число
|
call scan ; прочитать второе число
|
||||||
@@ -15,10 +15,9 @@ main:
|
|||||||
push rax
|
push rax
|
||||||
call print ; напечатать
|
call print ; напечатать
|
||||||
pop rax
|
pop rax
|
||||||
pop rbx ; восстановить rbx
|
pop rbx
|
||||||
|
|
||||||
mov dword [rel a], eax ; записать значение 42 в переменную a
|
mov dword [rel a], eax ; записать значение 42 в переменную a
|
||||||
|
|
||||||
mov rdi, 0
|
mov rax, 0
|
||||||
mov rax, 60
|
ret
|
||||||
syscall
|
|
||||||
2
casm/input.txt
Normal file
2
casm/input.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
10
|
||||||
|
20
|
||||||
55
docs/cdecl.md
Normal file
55
docs/cdecl.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
## 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.
|
||||||
|
|
||||||
|
### Порядок передачи аргументов
|
||||||
|
- 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), а функция заполняет его.
|
||||||
|
|
||||||
|
### Типичный каркас функции на NASM (Linux)
|
||||||
|
; extern printf
|
||||||
|
; global main
|
||||||
|
; section .data
|
||||||
|
; msg db "x=%d", 10, 0
|
||||||
|
; section .text
|
||||||
|
; main:
|
||||||
|
; push rbp
|
||||||
|
; mov rbp, rsp
|
||||||
|
; ; на входе rsp%16==8 → для вызова нужно выровнять до 16
|
||||||
|
; sub rsp, 8 ; выравнивание стека перед call
|
||||||
|
; lea rdi, [rel msg] ; 1-й аргумент (fmt) → RDI
|
||||||
|
; mov esi, 42 ; 2-й аргумент (int) → ESI
|
||||||
|
; xor eax, eax ; AL=0: нет XMM-аргументов (variadic правило)
|
||||||
|
; call printf
|
||||||
|
; add rsp, 8 ; вернуть выравнивание
|
||||||
|
; xor eax, eax ; return 0
|
||||||
|
; pop rbp
|
||||||
|
; ret
|
||||||
|
|
||||||
|
### Памятка при написании кода
|
||||||
|
- До call: выровняй стек (RSP%16==0), подготовь регистры аргументов, AL для varargs.
|
||||||
|
- После call: результат смотри в RAX/XMM0; считай, что caller-saved испорчены — сохрани важное заранее.
|
||||||
|
- В своей функции сохраняй/восстанавливай RBX, RSP, RBP, R12–R15.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
22
minimal/.vscode/launch.json
vendored
22
minimal/.vscode/launch.json
vendored
@@ -15,27 +15,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/build/minimal",
|
"program": "${workspaceFolder}/build/minimal",
|
||||||
"cwd": "${workspaceFolder}/build",
|
"cwd": "${workspaceFolder}/build",
|
||||||
"preLaunchTask": "asm64",
|
"preLaunchTask": "asm64+gcc"
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "by-gdb x64",
|
|
||||||
"type": "by-gdb",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}/build/minimal",
|
|
||||||
"cwd": "${workspaceFolder}/build",
|
|
||||||
"preLaunchTask": "asm64",
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gdb x64",
|
|
||||||
"type": "gdb",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}/build/minimal",
|
|
||||||
"preLaunchTask": "asm64",
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
24
minimal/.vscode/tasks.json
vendored
24
minimal/.vscode/tasks.json
vendored
@@ -9,7 +9,7 @@
|
|||||||
"mkdir -p $builddir;",
|
"mkdir -p $builddir;",
|
||||||
"rawfilename=$builddir/minimal;",
|
"rawfilename=$builddir/minimal;",
|
||||||
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
|
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
|
||||||
"gcc -g -o $rawfilename $rawfilename.o;"
|
"ld -g -o $rawfilename $rawfilename.o;"
|
||||||
],
|
],
|
||||||
"problemMatcher": {
|
"problemMatcher": {
|
||||||
"pattern": {
|
"pattern": {
|
||||||
@@ -26,6 +26,28 @@
|
|||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "asm64+gcc",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"builddir=${workspaceFolder}/build;",
|
||||||
|
"mkdir -p $builddir;",
|
||||||
|
"rawfilename=$builddir/minimal;",
|
||||||
|
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
|
||||||
|
"gcc -o $rawfilename $rawfilename.o;"
|
||||||
|
],
|
||||||
|
"problemMatcher": {
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"focus": true,
|
||||||
|
"panel": "dedicated",
|
||||||
|
"reveal": "silent",
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,153 @@
|
|||||||
global _start
|
global main
|
||||||
|
|
||||||
|
section .data
|
||||||
|
buff times 256 db 0
|
||||||
|
buffend:
|
||||||
|
count dq 0
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
_start:
|
default rel
|
||||||
; Передаём два числа для суммирования
|
|
||||||
mov rsi, 12
|
|
||||||
mov rdi, 7
|
|
||||||
call calculate_sum
|
|
||||||
|
|
||||||
; Используем результат
|
main:
|
||||||
|
call read_text
|
||||||
|
|
||||||
|
lea rsi, [rel buff] ; поставить указатель на начало буфера
|
||||||
|
call read_num
|
||||||
mov rbx, rax
|
mov rbx, rax
|
||||||
|
inc rsi
|
||||||
|
call read_num
|
||||||
|
mov rdi, rax
|
||||||
|
mov rsi, rbx
|
||||||
|
|
||||||
; Завершение программы
|
call calculate_sum
|
||||||
mov rax, 60
|
call print_num_to_buf
|
||||||
syscall
|
|
||||||
|
mov rdx, [count]
|
||||||
|
call write_text
|
||||||
|
|
||||||
; Функция складывает два значения, переданных в RDI и RSI
|
mov rax, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Функция складывает два значения
|
||||||
|
; In: RDI, RSI
|
||||||
|
; Out: RAX
|
||||||
calculate_sum:
|
calculate_sum:
|
||||||
lea rax, [rdi + rsi]
|
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
|
ret
|
||||||
Reference in New Issue
Block a user