---
# System prepended metadata

title: TASK 0

---




# 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).
     &rarr; Lưu trữ dữ liệu đang được CPU xử lí.
2) Thanh ghi địa chỉ( Address Registers).
&rarr;  Giữ địa chỉ ô chứa dữ liệu hoặc lệnh.
3) Thanh ghi lệnh( Instruction Registers - IR).
&rarr; Giữ lệnh hiện tại mà CPU đang xử lí.
4) Thanh ghi bộ đếm chương trình( Program Counter - PC).
&rarr; 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).
&rarr; 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" &rarr; "thực hiện lệnh được yêu cầu bên trong thanh ghi" &rarr; "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`.)

![lifo](https://hackmd.io/_uploads/SJdwVJSaex.jpg)

### **Ư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 &rarr; 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.

![MSG](https://hackmd.io/_uploads/B1njjuPpxx.png)
 
- 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 .
![image](https://hackmd.io/_uploads/HJrRaKvTee.png)
* 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). ![image](https://hackmd.io/_uploads/HywzMLL0xx.png)
 
 * ***RBX***
 -- RBX dùng để lưu tạm, truy cập bộ nhớ.
 ![image](https://hackmd.io/_uploads/ry8eUIIRll.png)

* ***RCX***
-- RCX lưu tham số 1.
![image](https://hackmd.io/_uploads/ryfjLLI0xe.png)

* ***RDX***
--- RDX lưu tham số 2.
![image](https://hackmd.io/_uploads/BkTEoLURll.png)


> 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. 
    
&rarr; Tạo file asm
    ```
    touch hello.asm
    ```
&rarr; 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
    
```

&rarr; 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`
    
    
![image](https://hackmd.io/_uploads/S1x3_DqCex.png)

![image](https://hackmd.io/_uploads/HJflFD9Cge.png)


## 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
```
    
![image](https://hackmd.io/_uploads/rkmn1fMy-g.png)
![image](https://hackmd.io/_uploads/Hkg6kfGybl.png)


## 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

```
    
    
![image](https://hackmd.io/_uploads/BkCfeNryWl.png)
    
-- ***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ố
    
![image](https://hackmd.io/_uploads/SkxJppSy-x.png)
    
    SỐ ÂM EM CHỊU LUÔN

