# Khái niệm về thanh ghi.
## Thanh Ghi (Registers).
-- Thanh ghi (Registers) là một bộ nhớ dung lượng nhỏ và rất nhanh được sử dụng để lưu trữ tạm thời dữ liệu và lệnh trong quá trình sử lí.
### * _Đặc điểm của thanh ghi_.
-- Nằm trong CPU, tốc độ rất nhanh( nhanh nhất trong RAM và bộ nhớ ngoài).
-- Dung lượng của thanh ghi rất nhỏ( Chỉ vài chục cho đến vài trăm byte).
-- Mỗi thanh ghi có chức năng riêng nhằm phục vụ những tác vụ khác nhau như lưu trữ tạm thời dữ liệu, tính toán, điều khiển...
### * _Phân loại thanh ghi_.
1) Thanh ghi dữ liệu(Data Registers).
→ Lưu trữ dữ liệu đang được CPU xử lí.
2) Thanh ghi địa chỉ( Address Registers).
→ Giữ địa chỉ ô chứa dữ liệu hoặc lệnh.
3) Thanh ghi lệnh( Instruction Registers - IR).
→ Giữ lệnh hiện tại mà CPU đang xử lí.
4) Thanh ghi bộ đếm chương trình( Program Counter - PC).
→ Ghi nhớ địa chỉ mà chương trình sẽ thực hiện tiếp theo.
5) Thanh ghi trạng thái( Status Registers / Flag Registers).
→ Lưu các cờ trạng thái(kết quả âm, kết quả 0, tràn,...).
# Khái niệm về lệnh ( Instructions).
## Lệnh ( Instructions).
-- Lệnh là chỉ thị cơ bản nhất mà CPU có thể hiểu và thực thi được. Ví dụ như việc "nạp dữ liệu vào RAM và thanh ghi" → "thực hiện lệnh được yêu cầu bên trong thanh ghi" → "Lưu dữ liệu vào RAM".
-- Mỗi instruction tương ứng với một thao tác ở mức, và thường được thực viết bởi ngôn ngữ `Assembly`.
# Khái niệm về Stack ( ngăn xếp).
## Stack ( ngăn xếp)
-- Stack ( ngăn xếp) là một cấu trúc dữ liệu tuyến tính hoạt động theo quy luật LIFO ( Last in - First out / được hiểu như phần tử cuối nhập và sẽ là phần tử đầu tiên xuất ra).
-- Stack là 1 danh sách với 2 thao tác : `Thêm vào 1 phần tử ở cuối danh sách ` và ` Loại bỏ phần tử ở cuối danh sách`, phần tử cuối cùng của danh sách được gọi là `TOP`. ( Có thể tưởng tượng như việc sắp xếp 1 chồng sách là thực hiện lệnh `PUSH`, và việc lấy từ từ quyển sách từ trên xuống là lệnh `POP`.)

### **ƯU ĐIỂM**.
-- Tốc độ truy xuất dữ liệu nhanh ( do hoạt động với 2 thao tác cơ bản là `PUSH` và `POP` nên độ phức tạp thấp → dữ liệu được truy xuất nhanh ).
-- Dễ sử dụng và ứng dụng hiệu quả ( Thực hiện dưới quy tắc LIFO trực quan, khả năng lưu trữ ngắn hiệu quả ví dụ như nút `UNDO` và `REDO`).\
-- Stack tự động dọn dẹp đối tượng ( khi hàm được gọi, các dự liệu biến cục bộ được lưu vào ngăn xếp, sau đó được giải phóng sau khi chương trình kết thúc).
### **NHƯỢC ĐIỂM**.
-- Bộ nhớ có giới hạn ( Bộ nhớ của stack có giới hạn nhất định khi khởi tạo chương trình, nếu cố tình đẩy quá nhiều dữ liệu vào sẽ gây tình trạng `Stack OverFlow`, có thể gây sập chương trình).
-- Không thể truy xuất dữ liệu ngẫu nhiên ( Do hoạt động với quy tắc LIFO, nên stack chỉ có thể lấy dữ liệu từ phía trên xuống, không thể lấy trực tiếp phần dữ liệu ở giữa ra được).
-- Vòng đời dữ liệu ngắn ( Dữ liệu được lưu trên stack như biến cục bộ sẽ bị xóa ngay khi hàm kết thúc).
-- Bộ nhớ biến có thể bị ghi đè, việc này dẫn đến hành vi không xác định của hàm hoặc chương trình.
## Heap - bổ sung thêm kiến thức liên quan.
### Khái niệm về heap.
-- Heap là là 1 vùng bộ nhớ lớn nằm trên RAM, RAM có bao nhiêu thì heap có thể đạt dung lượng lớn bấy nhiêu. Heap dùng để khởi tạo bộ nhớ động, cho phép người lập trình chủ động thay đổi dữ liệu, chỉnh sửa và hủy khi chương trình đang chạy.
### ƯU ĐIỂM.
-- Heap có bộ nhớ lớn và linh hoạt ( Bộ nhớ của heap lớn hơn của stack rất nhiều, phù hợp cho việc lưu trữ các đối tượng phức tạp, mảng lớn hoặc dữ liệu chưa xác định được trong lúc code).
-- Vòng đời dữ liệu lâu ( Heap sẽ lưu trữ dữ liệu cho đến khi người lập trình `delete`. Điều này giúp dữ liệu có thể chia sẻ qua nhiều hàm với nhau và đi qua nhiều )
--Cho phép truy cập các biến toàn cục ( có thể truy cập vào dữ liệu bất kì trong bộ nhớ mà không cần phải rút như các LIFO hoạt động).
### NHƯỢC ĐIỂM.
-- Heap có tốc độ truy xuất dữ liệu chậm hơn so vơi stack ( Vì phải tìm kiếm và kiểm tra khối bộ nhớ trống phù hợp).
-- Quản lí thủ công phức tạp ( việc quên giải phóng bộ nhớ có thể gây tình trạng rò rỉ bộ nhớ `memory leak` gây lãng phí tài nguyên máy, hoặc ngược lại nếu đã `delete` mà vẫn cố gắng truy cập vào dữ liệu đó thì tạo ra hiện tượng con trỏ lơ lửng `dangling pointer` gây crash chương trình và hỏng bảo mật).
-- Có khả năng bị phân mảnh dữ liệu ( các dữ liệu được thêm vào và giải phóng liên tục trong quá trình làm việc, heap có thể bị `phân mảnh` thành nhiều dữ liệu nhỏ không liền kề, hậu quả là không thể lưu được thêm dữ liệu dù dung lượng vẫn còn chỗ trống).
---
# Khái niệm về Endianness.
## TÍnh Endian.
-- Endianess là cách mà máy tính lưu trữ và truyền dữ liệu có nhiều byte (lớn hơn 1 byte).
-- Một dữ liệu có nhiều byte thì Endianness sẽ quyết định xem byte nào được lưu trước.
-- Endianness được chia làm 2 loại chính : `Big-Endian`
và `Littel- Endian`.
### Big-Endian.
-- Trong Big-Endian , byte có trọng số lớn nhất - có ý nghĩa nhất ( Most Significant Byte - MSB) sẽ được lưu trữ trước.
-- Việc sử dụng Big-Endian thuận tiện trong việc giải mã nhị phân và thập phân, do nó đọc từ trái qua phải.

- Ví dụ : `0x12345678` sẽ được dịch thành `12` `34` `56` `78`
Trong đó `12` là `MSB`.
Khi thay byte đầu tiên `1` lên thành `2` thì giá đó tăng hơn 200 triệu đơn vị
### Littel-Endian.
-- Trong Littel-Endian, byte có trọng số nhỏ nhất - có ý nghĩa nhỏ nhất ( Least Significant Byte - LSB) sẽ được lưu trữ trước.
-- Littel-Endian thường được sử dụng trong việc đọc địa chỉ, giải thuật toán do nó đọc dữ liệu ngược từ phải qua trái .

* Ví dụ `0x12345678` sẽ được dịch thành `78` `56` `34` `12`
Trong đó `78` là `LSB`.
Khi thay byte đầu tiên từ `7` lên `8` thì giá trị chỉ tăng thêm 10 đơn vị.
---
# Calling Convention.
## Khái niệm.
-- Calling convention là tập hợp quy tắc chỉ định hàm nào được gọi.
-- Khi một hàm được gọi, 1 `stack` sẽ được phân bổ cho hàm đó. Khi hàm đó hoàn thành công việc thì `stack` được phân bổ đó sẽ được xóa và quyền điều khiển được truyền cho hàm gọi.
* Hàm gọi ( Caller): Hàm gọi hàm khác.
* Hàm được gọi (Callee): Hàm bị gọi để làm việc.
* Calling convention: Quy tắc để 2 cái trên nói chuyện với nhau.
```cpp=
// Hàm được gọi
int add(int a, int b) {
return a + b;
}
// Hàm gọi
int main() {
int x = add(3,6); // main gọi add → main là caller
return 0;
}
```
Calling Convention giống như 1 hợp đồng để chắc chắn rằng cả `caller` và `callee` giao tiếp được với nhau để :
1. Truyền đổi số: `caller` đặt giá trị đầu vào để `callee` nhận chúng.
2. Trả về giá trị (return value): `callee` đặt giá trị đầu ra để `caller` nhận lấy giá trị đó.
3. Quản lí stack: Lựa chọn ai sẽ là người dọn dẹp stack (`caller` hoặc `callee`).
4. Bảo toàn `register`: Xác định `register` nào cần được giữ nguyên giá trị ( được bảo toàn bởi `callee`), xác định `register` nào có thể thoải mái sử dụng.
VÍ DỤ:
Hàm C++ bên trên
| ASSEMBLY | Giải thích |
|:------------------:|:-----------------------------------------------------------------------------------------------:|
| push 6 | Đẩy số 6 lên stack |
| push 3 | Đẩy số 3 lên stack |
| call add | Chuyển quyền điều khiển đến hàm add. |
| mov [kết quả], eax | Lấy giá trị trả về thanh ghi EAX và lưu nó ( trả về EAX vì int là dữ liệu 32 bit) |
| add esp, 8 | Dọn stack: caller chịu trách nhiệm (3 và 6 mỗi cái 4byte, nên cần thêm 8byte vào esp để dọn).. |
## ASSEMBLY.
### Assembly.
-- Assembly là ngôn ngữ trung gian giữ ngôn ngữ máy tính và các ngôn ngữ bật cao hơn ( C++, Python, C, ...)
-- Cấu trúc của 1 lệnh asm : ` Lệnh ` = ` Động từ `+ `Danh từ`
| Lệnh | Tiếng Anh | Thực hiện | Ví dụ |
|:------:|:---------------:|:---------------------:|:---------------------------------------------------------:|
| `mov` | move | Sao chép | `mov rax, rbx` |
| `add` | add | Cộng 2 giá trị | `add rax, 36` |
| `sub` | subtract | Trừ | `sub rax, 18` |
| `mul` | multiply | Nhân | `mul rbx` |
| `div` | divide | Chia | `div rbx` |
| `push` | push onto stack | Đẩy dữ liệu vào stack | `push rax` |
| `pop` | pop form stack | Lấy dữ liệu stack | `pop rax` |
| `cmp` | compare | so sánh giá trị | `cmp rax, rbx` |
| `jmp` | jump | Nhảy đến địa chỉ khác | `jmp meomeo ...(code bỏ qua)....meomeo; (code chạy tiếp)` |
| `call` | call | Gọi hàm | `call akietcute` |
| `ret` | return | Hàm trả về | `ret` |
* QUY TRÌNH XÂY DỰNG FILE THỰC THI:
*Viết mã nguồn `(tên file).asm` ( ví dụ : `hello.asm`)
+ Cấu trúc chia thành 2 phần : `section .data` ( để chứa biến), `section .text` ( để chứa code).
+ Lúc bắt đầu phải khai báo ` _start ` và `global _start`.
*Dịch ngôn ngữ.
- Sử dụng trình biên dịch `nasm` để chuyển từ file `.asm` ( người đọc)thành file `.o` ( máy đọc).
- Lệnh : `as --64 hello.s -o hello.o`
- `nasm` : gọi trình biên dịch.
- `--64` : chỉ định kiến trúc 64bit
- `hello.s` : tên file.
- `-0 hello.o` : đổi file chỉ định thành <file>.o ( `hello.o`).
* Liên kết thành file thực thi : `ld hello.o -o hello`
- `ld` : trình liên kết
- `hello.o` : file cần chuyển
- `-o hello`: chỉ định file thực thi là `hello`
* Chạy chương trình : `./hello`
P/s: Lệnh `cat` ( Concatenate : nối) : Chức năng là đọc file và in ra.
-- `cat hello.s` : in file ra màn hình
-- `cat file1 file2` : in ra nội dung file 1 , sau đó là file 2.
-- `cat file1 file2 > file 3`: gộp nội dung file 1,2 vào file 3.
-- `cat file 1>>file2` : thêm file 1 vào cuối file 2
-- `cat >file` : tạo file mới
Một vài option cần biết :
| Lệnh | Ý nghĩa |
|:----:|:------------------------------------:|
| `-n` | Đánh số các dòng |
| `-b` | Đánh dấu dòng, bỏ qua dòng trống |
| `-s` | Gộp các dòng liên tiếp thành 1 dòng |
| `-E` | Hiện kí tự `$` ở cuối mỗi dòng |
| `-T` | Hiện kí tự khoảng trống tab thành ^I |
| `-A` | Hiện tất cả kí tự của `-E` và `-T` |
### RAX, RBX, RCX, RDX. ( Trong Windowx86)
* ***RAX***
-- RAX là 1 thanh ghi 64-bit dùng để lưu các giá trị tính toán, logic, kết quả trả về .
-- Trong kiến trúc x84-64, thanh ghi ghi này có kích thước 8 byte.
-- Trong RAX còn các các phần nhỏ là : EAX, AX (trong AX còn có AH, AL). 
* ***RBX***
-- RBX dùng để lưu tạm, truy cập bộ nhớ.

* ***RCX***
-- RCX lưu tham số 1.

* ***RDX***
--- RDX lưu tham số 2.

> Ví dụ :
```cpp=
int akietdeptrai(int a, int b) {
return a + b;
}
int main() {
int ketqua = akietdeptrai(5, 3);
return 0;
}
```
> Dịch :
```
Hàm main : (Dùng E...X là vì int thuộc 32-bit, dùng E..X sẽ nhanh hơn R..X)
MOV ECX , 5; (Tham số 1, RCX = 5)
MOV EDX , 3; (Tham số 2, RDX = 3)
CALL akietdeptrai ; (Gọi hàm akietdeptrai)
MOV ketqua,EAX ; (Giá trị trả về RAX)
Hàm akietdeptrai
RCX = a ;
RDX = b ;
MOV EAX , ECX; (Lưu vào RAX = a)
ADD EAX , EDX; (lưu vào RAX = a + b)
RET ; (Return, trả về RAX)
```
### Lệnh MOV ( Move).
-- `MOV` dùng để sao chép dữ liệu từ nguồn đến đích .
-- Cú pháp : MOV ` đích đến của giá trị `, ` nguồn lấy giá trị `
>[!Caution]
>`MOV` là sao chép dữ liệu chữ không phải duy chuyển. Dữ liệu vẫn vẫn ở đó, dữ liệu copy di chuyển.
>`MOV` không thể sao chép ô nhớ mà phải qua thanh ghi trung gian.
>`MOV` không tham chiếu dữ liệu ( thay đổi giá trị nguồn thì đích vẫn không thay đổi).
### Syscall (AMD64)
-- Syscall là cách chương trình giao tiếp với hệ điều hành (kernel).
-- Mọi hoạt động : `Nhập, xuất, lưu , ghi, đọc,...` đều không thể làm trực tiếp bằng code, mà phải nhờ Kernel làm giúp.
* QUY ƯỚC TRUYỀN THAM SỐ
-- Thanh ghi RAX sẽ hiểu được lệnh SYSCALL
-- Các thanh ghi :
| Thanh ghi | Ý nghĩa |
|:---------:|:---------:|
| RDI | Tham số 1 |
| RSI | Tham số 2 |
| RDX | Tham số 3 |
| R10 | Tham số 4 |
| R8 | Tham số 5 |
| R9 | Tham số 6 |
* Một số SYSCALL phổ biến.
| Mã RAX | Tên Syscall | Mô tả |
|:------:|:-----------:|:-----------------------------:|
| 0 | `read` | Đọc dữ liệu từ file |
| 1 | `write` | Ghi dữ liệu ra file |
| 2 | `open` | Mở file |
| 3 | `close` | Đóng file |
| 57 | `fork` | Tạo chương trình mới ( nhánh) |
| 59 | `execve` | Chạy chương trình khác |
| 60 | `exit` | Thoát |
# Thực hành.
## Viết Hello World.
→ Tạo file asm
```
touch hello.asm
```
→ code
`nano hello.asm` và code
- Format của lệnh print như sau :
```a
rax = 1 (số hiệu lệnh write)
rdi = 1 (file descriptor cho stdout)
rsi = <địa chỉ bộ đệm chứa dữ liệu>
rdx = <số byte cần ghi>
syscall (thực thi).
```
---BÀI LÀM---
``` =asm
section .text
global _start
_start:
mov rax, 1 ; Chọn syscall 1 : WRITE
mov rdi, 1 ; Tham số 1 là xuất
mov rsi, hello_msg ; Tham số 2 là địa chỉ chuỗi
mov rdx, hello_msg_len ; Tham số 3 là độ dãi chuỗi
syscall ; Gọi syscall dịch các hàm trên
mov rax, 60 ; Chọn syscall 60 : EXIT
mov rdi, 0 ; Mã thoát khi chương trình không có lỗi
syscall
section .data
hello_msg : db "Hello world", 10; define byte "Hello World" vào bộ nhớ
và xuống dòng là 10 trong mã ACII
hello_msg_len : equ $ - hello_msg; Tính toán độ dài dữ liệu hello_msg
và gán vào hello_msg_len
```
→ Sau khi code xong , tiến hành dịch sang ngôn ngữ máy
```
nasm --64 hello.asm -o hello.o
```
Tiếp đến là dịch thành file thực thi
`ld hello.o - o hello`
Cuối cùng là chạy
`./hello`
**KẾT QUẢ NHẬN ĐƯỢC LÀ :** `Hello World`


## Nhập n, tính số Fibo tại vị trí n
```a
section .data
msg db 'nhap n: ',0 ;msg là thông báo nhập
msg_len equ $ - msg
msg2 db 10, 'So fibo la : ',0 ;msg2 là thông báo output
msg2_len equ $ -msg2
section .bss
num1 resb 100 ;num1 là giá trị nhập vào
num2 resb 100 ;num2 là kết quả tính fib
section .text
global _start
_start:
;====In thông báo=====
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
;===Nhập chuỗi===
mov rax, 0
mov rdi, 0
mov rsi, num1
mov rdx, 15
syscall
dec rax ; Bỏ '\n'
mov rcx, rax ; chuỗi dài
mov rbx, num1
call _stoi ; chuỗi -> số
mov rcx, rax ; đưa n vào rcx
call _Fibo ; => RAX = Fibo (n)
call _itos ; số -> chuỗi
;=== In Kết Quả ===
mov rax, 1
mov rdi, 1
mov rsi, msg2
mov rdx, msg2_len
syscall
mov rax, 1
mov rdi, 1
mov rsi, num2
mov rdx, rbp
syscall
;=== Thoát code loz này===
mov rax, 60
mov rdi, 0
syscall
;=== Giờ viết lệnh gọi ===
; Hàm chuỗi sang số
; công thức là: n=n*10 + (a-48)
; dl là rdx low, byte cuối chạy cho nhanh
; rbx chứa num1
_stoi:
mov rax, 0
mov rsi, 0
.nextChar:
mov rdx, 10
mul rdx
mov dl, [rbx + rsi] ; dia chi rbx + rsi
sub dl, '0'
add rax, rdx
inc rsi
loop .nextChar
ret
;=== Hàm tính fibo ===
; Input : RCX = n
; Output: RAX = số fib
; Công thức : fib = a+b, a = b, b= fib, lặp lại
; rsi là biến tạm lưu giá trị a cũ
_Fibo:
cmp rcx, 1
jle .f1
cmp rcx, 2
jle .f1
mov rax, 1 ; F1 =1
mov rbx, 1 ; F2 =1
sub rcx, 2 ; trừ n-2 bước lặp
.loop:
mov rsi, rax
add rax, rbx ; Fib = F(n-1) + F(n-2)
mov rbx, rsi
dec rcx
jnz .loop ; jump nếu no zero
ret
.f1:
mov rax, 1 ; cho rax =1
ret
;===Hàm int -> string để in ===
; Chuyển số nguyên (RAX) thành chuỗi
; output : num2 chứa chuỗi và rbp là length
_itos:
mov rcx, 0 ; cho rcx = 0
.ketqua:
xor rdx, rdx ; cho rdx =0, lấy làm stack
mov rbx, 10
div rbx ; chia rax cho 10
add dl , '0' ; cộng 48 vào => ra chuỗi
push rdx ; ném vào stack
inc rcx
cmp rax ,0
jne .ketqua ; jump no equal
mov rbp, 0 ; chọn rbp cho việc lưu length
.vietketqua:
pop rdx ; lấy ra khỏi stack
mov [num2 + rbp], dl ; đưa vào vị trí num(rbp)
inc rbp
loop .vietketqua
ret
;Mắc khùng quá trời ơiiiii
```


## In tổng 2 số
```asm
section .data
msg1 db "Nhap so thu 1: ", 0
msg1_len equ $ - msg1
msg2 db "Nhap so thu 2: ", 0
msg2_len equ $ - msg2
msgkq db "Tong la: ", 0
msgkq_len equ $ - msgkq
section .bss
num1 resb 16
num2 resb 16
buf resb 16
section .text
global _start
_start:
; ==== nhập số 1 ====
mov rax, 1
mov rdi, 1
mov rsi, msg1
mov rdx, msg1_len
syscall
mov rax, 0
mov rdi, 0
mov rsi, num1
mov rdx, 16
syscall
dec rax
mov byte [num1 + rax], 0 ; xóa '\n' => thêm null
mov rbx, num1
call _stoi
mov r8, rax ; lưu số 1
; ==== nhập số 2 ====
mov rax, 1
mov rdi, 1
mov rsi, msg2
mov rdx, msg2_len
syscall
mov rax, 0
mov rdi, 0
mov rsi, num2
mov rdx, 16
syscall
dec rax
mov byte [num2 + rax], 0 ; xóa '\n'
mov rbx, num2
call _stoi
add rax, r8 ; rax = num1 + num2
; ==== in kết quả ====
mov rbx, buf
call _itos
mov rax, 1
mov rdi, 1
mov rsi, msgkq
mov rdx, msgkq_len
syscall
mov rax, 1
mov rdi, 1
mov rsi, buf
mov rdx, rcx ; độ dài chuỗi trong rcx
syscall
; ==== thoát ====
mov rax, 60
xor rdi, rdi
syscall
; === CHUYỂN CHUỖI → SỐ ===
_stoi:
xor rax, rax
xor rsi, rsi
.next:
mov dl, [rbx + rsi]
cmp dl, 0
je .done
imul rax, rax, 10
sub dl, '0'
add rax, rdx
inc rsi
jmp .next
.done:
ret
; === CHUYỂN SỐ → CHUỖI ===
_itos:
mov rcx, 0
.convert:
xor rdx, rdx
mov rbx, 10
div rbx
add dl, '0'
push rdx
inc rcx
test rax, rax
jnz .convert
mov rbx, buf
.printloop:
pop rdx
mov [rbx], dl
inc rbx
loop .printloop
ret
```

-- ***Phần data*** :
+ msg1 : Thông báo nhập số 1
+ msg2 : Thông báo nhập số 2
+ msgkq: Thông báo in kết quả
+ msg..._len : Lưu độ dài
+ num1 : Lưu số đầu
+ num2 : Lưu số thứ 2
+ buf : output string chứa kết quả phép cộng
-- ***Phần text:***
+ Gọi syscall in thông báo nhập số đầu
+ Đọc số đầu từ người dùng
+ Chuyển string -> int để tính toán, mov vào thanh ghi r8
+ Gọi syscall in thông báo nhập số tiếp theo
+ Đọc số tiếp theo từ người dùng
+ Chuyển từ string -> int để tính toán, add vào thanh ghi rax
+ Dịch từ int sang string để in
+ Kết thúc chương trình
-- ***Phần Label***
+ _stoi: String to Int
+ _itos : Int to String
## Nhân 2 số
```=asm
section .data
msg1 db "Nhap so thu 1: ", 0
msg1_len equ $ - msg1
msg2 db "Nhap so thu 2: ", 0
msg2_len equ $ - msg2
msgkq db "Tich la: ", 0
msgkq_len equ $ - msgkq
section .bss
num1 resb 16
num2 resb 16
buf resb 16
section .text
global _start
_start:
; ==== nhập số 1 ====
mov rax, 1
mov rdi, 1
mov rsi, msg1
mov rdx, msg1_len
syscall
mov rax, 0
mov rdi, 0
mov rsi, num1
mov rdx, 16
syscall
dec rax
mov byte [num1 + rax], 0 ; xóa '\n' => thêm null
mov rbx, num1
call _stoi
mov r8, rax ; lưu số 1
; ==== nhập số 2 ====
mov rax, 1
mov rdi, 1
mov rsi, msg2
mov rdx, msg2_len
syscall
mov rax, 0
mov rdi, 0
mov rsi, num2
mov rdx, 16
syscall
dec rax
mov byte [num2 + rax], 0 ; xóa '\n'
mov rbx, num2
call _stoi
mul r8 ; rax = num1 * num2
; ==== in kết quả ====
mov rbx, buf
call _itos
mov rax, 1
mov rdi, 1
mov rsi, msgkq
mov rdx, msgkq_len
syscall
mov rax, 1
mov rdi, 1
mov rsi, buf
mov rdx, rcx ; độ dài chuỗi trong rcx
syscall
; ==== thoát ====
mov rax, 60
xor rdi, rdi
syscall
; === CHUYỂN CHUỖI → SỐ ===
_stoi:
xor rax, rax
xor rsi, rsi
.next:
mov dl, [rbx + rsi]
cmp dl, 0
je .done
imul rax, rax, 10
sub dl, '0'
add rax, rdx
inc rsi
jmp .next
.done:
ret
; === CHUYỂN SỐ → CHUỖI ===
_itos:
mov rcx, 0
.convert:
xor rdx, rdx
mov rbx, 10
div rbx
add dl, '0'
push rdx
inc rcx
test rax, rax
jnz .convert
mov rbx, buf
.printloop:
pop rdx
mov [rbx], dl
inc rbx
loop .printloop
ret
```
Giống i chang cái cộng, thay vào đó sau khi `_stoi num2` thì ta sẽ dùng mul để nhân 2 số

SỐ ÂM EM CHỊU LUÔN