Сложение двух чисел

This commit is contained in:
Пытков Роман
2025-09-22 00:53:15 +03:00
parent 6ef051900a
commit 444a2da5a8
7 changed files with 230 additions and 39 deletions

View File

@@ -30,7 +30,7 @@
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
},
]
}
]

View File

@@ -4,7 +4,7 @@ extern a
section .text
main:
push rbx ; сохранить rbx
push rbx
call scan ; прочитать первое число
mov rbx, rax ; сохранить первое число
call scan ; прочитать второе число
@@ -15,10 +15,9 @@ main:
push rax
call print ; напечатать
pop rax
pop rbx ; восстановить rbx
pop rbx
mov dword [rel a], eax ; записать значение 42 в переменную a
mov rdi, 0
mov rax, 60
syscall
mov rax, 0
ret

2
casm/input.txt Normal file
View File

@@ -0,0 +1,2 @@
10
20

55
docs/cdecl.md Normal file
View 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; остальные — на стек (справа налево).
- Аргументы с плавающей точкой/векторы: XMM0XMM7; остальные — на стек.
- Возврат значения: целые/указатели — 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, R8R11, XMM0XMM15, флаги RFLAGS.
- Callee-saved (надо сохранять и восстанавливать в функции): RBX, RSP, RBP, R12R15.
### Порядок передачи аргументов
- 16-й целочисленные/указательные: RDI, RSI, RDX, RCX, R8, R9.
- Вещественные/векторные: XMM0XMM7 (независимо от целочисленных).
- 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, R12R15.

View File

@@ -15,27 +15,7 @@
"request": "launch",
"program": "${workspaceFolder}/build/minimal",
"cwd": "${workspaceFolder}/build",
"preLaunchTask": "asm64",
},
{
"name": "by-gdb x64",
"type": "by-gdb",
"request": "launch",
"program": "${workspaceFolder}/build/minimal",
"cwd": "${workspaceFolder}/build",
"preLaunchTask": "asm64",
},
{
"name": "gdb x64",
"type": "gdb",
"request": "launch",
"program": "${workspaceFolder}/build/minimal",
"preLaunchTask": "asm64",
"preLaunchTask": "asm64+gcc"
}
]
}

View File

@@ -9,7 +9,7 @@
"mkdir -p $builddir;",
"rawfilename=$builddir/minimal;",
"nasm -gdwarf -f elf64 -o $rawfilename.o ${workspaceFolder}/minimal.asm;",
"gcc -g -o $rawfilename $rawfilename.o;"
"ld -g -o $rawfilename $rawfilename.o;"
],
"problemMatcher": {
"pattern": {
@@ -26,6 +26,28 @@
"kind": "build",
"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
},
}
]
}

View File

@@ -1,20 +1,153 @@
global _start
global main
section .data
buff times 256 db 0
buffend:
count dq 0
section .text
_start:
; Передаём два числа для суммирования
mov rsi, 12
mov rdi, 7
call calculate_sum
default rel
; Используем результат
main:
call read_text
lea rsi, [rel buff] ; поставить указатель на начало буфера
call read_num
mov rbx, rax
inc rsi
call read_num
mov rdi, rax
mov rsi, rbx
; Завершение программы
mov rax, 60
syscall
call calculate_sum
call print_num_to_buf
mov rdx, [count]
call write_text
; Функция складывает два значения, переданных в RDI и RSI
mov rax, 0
ret
; Функция складывает два значения
; 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