---
# System prepended metadata

title: KCSC TASK 1

---

<center> <h1>
     TASK 1 <small>Mentor : lekiet</small>
    </h1></center>

# Thanh ghi

Trong Assembly, máy tính không có khái niệm các biến như ngôn ngữ bật cao. Nó sẽ thao tác qua các thanh ghi và bộ nhớ để xử lí dữ liệu.
![image](https://hackmd.io/_uploads/rJk7C0OE-l.png)
## Thanh ghi dữ liệu 
 ![image](https://hackmd.io/_uploads/HJ1ekkYN-l.png)
EAX, EBX, ECX, EDX là các thanh ghi dữ liệu 32bit ( thay E = R như RAX, RBX... là các thanh ghi 64bit sau này).
Không có hậu tố hay tiền tố sẽ là 16 bit
- AX ( A = Accumulator : Tích lũy) : Thanh ghi này được dùng trong nhập xuất, sử dụng trong hầu hết instruction số học.
- BX (B = Base): Thanh ghi này để lưu các địa chỉ 
- CX (C = Count) : Thanh ghi này để đếm vòng lặp.
- DX (D = Data) : Giống như 1 thanh ghi `temp` , dùng chung với AX để tính toán.

Với tiền tố `E` như EAX,EBX sẽ là lưu trữ 32 bit, `R` như RAX, RBX sẽ là 64 bit.
Hậu tố H và L tương ứng với `High` and `Low`. Với H sẽ là lấy 8 bit cao trong chuẩn 16 bit (`1101 1001` thì sẽ là `1101`). Còn L thì là 8 bit thấp  ( `1101 1001` sẽ là `1001`).
## Thanh ghi con trỏ
![image](https://hackmd.io/_uploads/SyeewkKVWl.png)

- SP ( Stack Pointer): Con trỏ ngăn xếp này lưu địa chỉ cuối cùng của phần tử lưu vào stack.
![image](https://hackmd.io/_uploads/HJUBaxF4Wx.png)

- BP ( Base Pointer) : Hỗ trợ biến tham chiếu tới chương trình con.
- IP ( Instruction Pointer): Trỏ đến địa chỉ tiếp theo của chương trình thực thi. Kết hợp thanh ghi `CS` ( Code Segment) có tác dụng lưu toàn bộ chương trình code, các phân đoạn mã với `IP` để có thể tạo logic câu lệnh tiếp theo.
## Thanh ghi chỉ số
![image](https://hackmd.io/_uploads/SJCMC7q4Wg.png)

- SI ( Source Index): Dùng lưu giá trị địa chỉ nguồn cho phép toán với sâu.
- DI ( Destination Indix): Dùng lưu giá trị đíchc đến cho phép tính toán với sâu.

Thường thì sẽ có mấy format đơn giản như 
```asm
mov si, user_input
mov di, buffer
copy + encode
cmp buffer, key
```
## Thanh ghi cờ 
Flags Register là các thanh ghi kết hợp với Pointer Register để điều khiển chương trình. Do là Assembly không có các kiểu lệnh `IF/ELSE, WHILE, DO/WHILE...` nên phải dùng kiểu kết hợp này so sánh, lệnh nhảy các thứ.

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

- CF : Cờ nhớ, nó nhớ 0 hoặc nhớ 1 dùng cho số không dấu, nó phản ánh việc tràn số không dấu ( Ví dụ `cmp a,b` thì nó là `a-b` nếu a>=b thì CF = 0, và ngược lại).
- PF : Bit chẵn lẻ, đếm số lượng bit 1 nếu chẵn thì PF = 1, và ngược lại ( `0010 1000` có 2 bit 1, nên là chẵn => PF = 1 ). Nó cũng thường dùng để kiểm tra lỗi 
- AF : Cờ điều chỉnh chứa giá trị nhớ khi chuyển từ bit có trọng số 3 lên bit có trọng số 4.
- ZF : Cờ không, khi kết quả của phép tính bằng 0 hoặc so sánh bằng nhau thì `ZF = 0`, ngược lại nếu kết quả khác 0 hay so sánh không bằng nhau thì `ZF =1`
- SF : Cờ dấu, nó dùng để xác định số là âm hay dương, dựa vào MSB ( bit ngoài cùng bên phải), nếu số âm thì SF = 1, dương SF = 0.
- OF : Cờ tràn bằng 1 khi kết quả phép tính ( Có dấu) lớn hơn đích đến của nó. Đại loại là khi phép toán sai như khi +1 vào 127 trong thanh ghi 8bit, 128> 127 tràn rồi thì OF = 1
- DF : Cờ hướng, xác định hướng cho việc xoay hoặc so sánh gì đó, 0 là trái, 1 là phải

- TF : Cờ bẩy dùng để debug, đưa cờ bẩy vào để có thể thực hiện chương trình từng bước .

- IF : Cờ ngắt được sử dụng khi cần ngắt ngoài, kiểu như nhập input thì ngắt chương trình chờ nhập rồi mới xử lí. IF = 1 thì ngắt, IF = 0 thì chạy.

# Instruction
Instruction (lệnh) trong Assembly là những lệnh bậc thấp mà máy có thể hiểu được, nó giống mấy lệnh trong thư viện C++ này nọ nhưng bậc thấp nhấ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 func ...(code bỏ qua)....func (code chạy tiếp)` |
| `dec`  |    decrease     |        giảm  1        |                      `dec rax`                       |
| `inc`  |    increase     |        tăng 1         |                      `inc rax `                      |
| `lea`       |    load effective address             |   Lấy địa chỉ, không lấy dữ liệu                    |  `lea eax, [rbp-4]`                                             |
| `call` |      call       |        Gọi hàm        |                  `call func1000041`                  |
| `ret`  |     return      |      Hàm trả về       |                        `ret`                         |

Assembler: Chuyển .asm thành .o (Object file)
 `nasm -f elf64 file.asm -o file.o`
Linker (ld): Chuyển .o thành file thực thi (Executable)
`ld file.o -o file
`

Phân loại ra 


| Column 1                           | Column 2                |
|:---------------------------------- |:----------------------- |
| Arithmetic ( tính toán)            | add, sub, imul, div     |
| Bitwise                            | xor, and, or, shl, sal  |
| Control                            | cmp, test, jmp, je, jne |
| Function                           | call, ret               |
| Data movement ( di chuyển dữ liệu) | mov, lea, push, pop     |

Lệnh nhảy còn nhiều loại ([Em tham khảo ở đây](https://www.philadelphia.edu.jo/academics/qhamarsheh/uploads/Lecture%2018%20Conditional%20Jumps%20Instructions.pdf))

![image](https://hackmd.io/_uploads/H1O4EicVZg.png)
1. Nhảy nếu bằng 0 / bằng nhau
2. Nhảy nếu `khác` 0 / `khác` nhau
3. Nhảy nếu cờ Carry được set
4. Nhảy nếu cờ Carry `không` được set
5. Nhảy nếu cờ Overflow được set 
6. Nhảy nếu cờ Overflow `không` được set
7. Nhảy nếu cờ Signed được set ( Số âm)
8. Nhảy nếu cờ Signed `không` được set ( >= 0)
9. Nhảy nếu cờ Parity được set
10. Nhảy nếu cờ Parity `không` được set


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

11. Nhảy nếu lớn hơn / Không ít hơn hoặc bằng ( như nhau mà nhỉ)
12. Nhảy nếu lớn hơn hoặc bằng / Không nhỏ hơn
13. Nhảy nếu `nhỏ` hơn / Không lớn hơn hoặc bằng
14. Nhảy nếu nhỏ hơn hoặc bằng / Không lớn hơn
15.   Phần sau là  `> , >=, <, < =`

# STACK
Stack là ngăn xếp, nó được thực hiện theo quy luật LIFO (Last In, First Out). Giống như việc đặt từng quyển sách thành chồng, rồi lấy ra từ trên xuống dưới.

Nó được quản lí bởi 1 thanh ghi là SP (Stack Pointer) trỏ đến đỉnh stack và BP ( Base Pointer) trỏ đến đáy stack để xác định các tham số, biến cục bộ của hàm.
## Cấu trúc
![image](https://hackmd.io/_uploads/rJrvhs5NZg.png)
- Đỉnh stack được trỏ bởi thanh ghi `ESP`, đó là nơi thực thi các lệnh như lấy `pop`  và đẩy `push`.
- Địa chỉ các ô nhớ trong stack càng lên cao càng giảm
- Khung ngăn xếp ( stack frame): Mỗi hàm sẽ tạo ra 1 bộ khung riêng để lưu các giá trị tham số, biến cục bộ, giá trị return các thứ

## Thao tác
`push` >> Đẩy dữ liệu vô stack : ESP sẽ giảm đi 4/8 bit đối với  hệ thống 32/64 bit. >>> Càng lên cao càng giảm địa chỉ, mỗi lần giảm 4-8 bit tùy vào 32bit hay 64bit
`pop` >> Lấy giá trị từ stack ra : ESP sẽ tăng 4/8 bit đối với hệ thống 32/64 bit. >>< Càng xuống thấp địa chỉ stack càng giảm.

- Khởi tạo stack : `push rbp` để đưa địa chỉ rbp vào stack, sau đó `mov rbp, rsp` để lưu địa chỉ trỏ đỉnh stack vào rbp >>> xác lập được phần đáy của stack, `tham chiếu cố định của hàm`.
- Dọn dẹp stack : Phục hồi ESP bằng cách `mov esp, rbp` do rbp lưu giá trị esp ban đầu. Phục hồi RBP bằng cách `pop rbp` vì lúc đầu mình push .
- Trở về hàm gọi : `ret` để return, sủi khỏi stack và về hàm gọi và chạy tiếp chương trình

# Endianness 
Gồm `LITTEL EDIAN` và `BIG EDIAN`

* Littel là đọc theo kiểu LSB ( Least Significant Byte - LSB)
![image](https://hackmd.io/_uploads/rkmcbk3V-x.png)
Ví dụ `0x12345678` sẽ được dịch thành `78` `56` `34` `12` 
Trong đó `78` là `LSB`.
![image](https://hackmd.io/_uploads/Hk-lM1hE-g.png)

* Big là đọc theo kiểu MSB ( Most Significant Byte - MSB)
![image](https://hackmd.io/_uploads/H1uwG1nVWx.png)
Ví dụ : `0x12345678` sẽ được dịch thành `12` `34` `56` `78`
Trong đó `12` là `MSB`

Trong kiến thức này thì chủ yếu quan tâm đến việc MÁY TÍNH sẽ đọc dữ liệu theo kiểu ngược từ dưới lên ( Littel Edian)

# Calling conention

Ở mức độ máy thì CPU không biết khái niệm về hàm, nó chỉ biết `nhảy (jmp), lưu địa chỉ, đọc ghi bộ nhớ, thanh ghi`
Để nhiều đoạn mã compiler khác nhau, module khác nhau có thể giao tiếp với nhau, gọi được nhau thì cần quy ước chung là calling convention. Nó là 1 
contract giữa `callee` và `calller`.

## cdecl
| Convention | Nền tảng | Truyền | Trả về | Người dọn    | Thanh ghi cần bảo toàn |
| ---------- | -------- | ------ | ------ | --- | -------- |
| cdecl  | Linux/Win x86 |  Stack  |  EAX  |  Caller | EBX, EDI ESI, EBP     |


Các tham số đường truyền vào stack, thứ tự từ PHẢI &rarr; TRÁi.
```c
func(a,b,c)

-Trong máy sẽ thực hiện :

>>> push c
>>> push b
>>> push a
>>> call func()
```
> Mục đích là để đưa a vào đầu stack, giúp dễ lấy tham số hơn.

Người dọn stack sẽ là `caller`, sau khi kết thúc lệnh call, người gọi cần dùng ESP ( stack pointer) để loại bỏ các tham số đã push vào.

Theo quy ước thì các thanh ghi EBX, EDI, ESP, EBP cần phải dọn sạch trước khi `ret`
```c
int sum(int a, int b) {
    return a + b;
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; --- Bên phía Người gọi (Caller) ---
push 2          ; Tham số b
push 1          ; Tham số a
call _sum       ; Gọi hàm
add esp, 8      ; DỌN STACK (2 tham số * 4 bytes)
; Lúc này kết quả nằm trong EAX

; --- Bên phía Hàm được gọi (Callee) ---
_sum:
    push ebp            ; Bảo toàn EBP
    mov ebp, esp        ; Thiết lập stack frame
    mov eax, [ebp + 8]  ; Lấy tham số a
    add eax, [ebp + 12] ; Cộng thêm b vào EAX (trả về)
    pop ebp             ; Khôi phục EBP
    ret                 ; Quay về (không dọn stack ở đây)
```

## stdcall

giống `cdecl` nhưng người dọn stack là `callee`

```a
_sum@8:              ; Tên hàm thường có thêm @ + số bytes tham số
    push ebp
    mov ebp, esp
    mov eax, [ebp+8]
    add eax, [ebp+12]
    pop ebp
    ret 8            ; QUAN TRỌNG: Tự cộng ESP thêm 8 để dọn dẹp 2 tham số
```

```a
push 2
push 1
call _sum@8
; Không có lệnh "add esp, 8" ở đây!
```
## fastcall

2 tham số đầu tiên sẽ được lưu vào thanh ghi, các tham số từ thứ 2 trở đi sẽ đưa vào stack.
Sử dụng thanh ghi vào để tăng tốc độ xử lí dữ liệu

| Convention | Nền tảng | Truyền | Trả về | Người dọn    | Thanh ghi cần bảo toàn |
| ---------- | -------- | ------ | ------ | --- | -------- |
| fastcall  | Win x86 | ECX > EDX > Stack  |  EAX  |  Callee | EBX, EDI ESI, EBP     |

Ví dụ như 1 hàm sum
```c
__fastcall void Sum(int a, int b, int c, int d)
```
main sẽ là cộng a b c d lại

asm nó như này
```c
push 4          ; Tham số 'd' (vào Stack)
push 3          ; Tham số 'c' (vào Stack)
mov edx, 2      ; Tham số 'b' (vào EDX)
mov ecx, 1      ; Tham số 'a' (vào ECX)
call Sum


Sum:
    push ebp
    mov ebp, esp
    ; Tính toán: EAX = ECX + EDX + [EBP+8] + [EBP+12]
    pop ebp
    ret 8       ; <--- CALLEE DỌN: Tự cộng ESP thêm 8 bytes (của c và d)
```

---
---
---
---
# CODE

# Code hello

<div style="border-left:5px solid #9C27B0; background:#F3E5F5; padding:10px;">
<b></b>

```c
section .text 
    global _start

_start:
    ;syscall viết
    mov rax, 1                    ; syscall gọi write
    mov rdi, 1                    ; 1 là out 
    mov rsi, hello_msg            ; Địa chỉ của chuỗi cần in
    mov rdx, hello_msg_len        ; Độ dài chuỗi (tính bằng byte)
    syscall                       ; Gọi syscall 
    ;syscall sủi
    mov rax, 60                   ; syscall gọi exit
    mov rdi, 0                    ; 0 = thành công
    syscall                       ; Sủi

section .data
    hello_msg: db "Hello new KCSC's member, Welcome to REVERSE TEAM", 10,\
                  "HERE IS YOUR FLAG: KCSC{Hello_World}"  ; db (Define Byte): Chuỗi và ký tự xuống dòng
    hello_msg_len: equ $ - hello_msg  ; equ: Hằng số tính bằng (Vị trí hiện tại - Vị trí bắt đầu)

```


![image](https://hackmd.io/_uploads/HJPmVu0VZl.png)
</div> ```

# Cộng 2 số bình thường

- Assembly không biết `int`, `unsigned int`, `long long` ,`unsigned long long`... là gì cả. Mọi thứ đều là byte trong bộ nhớ
- Khi nhập input vào, assembly quy ước nó là mã `ASCII`
 ![image](https://hackmd.io/_uploads/H1op7cR4-l.png)

Do đó nhập 1 từ bàn phím vào, thứ được lưu trong bộ nhớ sẽ là 48, hay 0x30. Để có số 1 thì phải `sub 48` để có được`SỐ 1`.

- Vậy để cộng 2 số, cần chuyển 2 số đó từ `kí tự` thành số bằng cách `trừ đi '0'`, sau đó cộng 2 giá trị lại, và lấy kết quả đó `cộng '0'` sẽ ra được kí tự mang giá trị tổng ( Có vẻ mình diễn đạt hơi khó hiểu rồi...)


 INPUT &rarr; ASCII ('1' '2' '3' '\n') &rarr; buffer &rarr; stoi &rarr; cộng &rarr; itos &rarr; in ra.
 
```asm=
section .data
    ;nhập số thứ nhất
    msg1 db "Nhap so thu 1: ", 0
    msg1_len equ $ - msg1      ; Độ dài chuỗi msg1

    ;nhập số thứ hai
    msg2 db "Nhap so thu 2: ", 0
    msg2_len equ $ - msg2      ; Độ dài chuỗi msg2

    ; kết quả
    msgkq db "Tong la: ", 0
    msgkq_len equ $ - msgkq    ; Độ dài chuỗi msgkq

section .bss
    num1 resb 30   ; xin 30 byte cho bộ nhớ , này là số 1
    num2 resb 30   ;  số 2
    buf  resb 30   ;  chứa chuỗi kết quả

section .text
    global _start

_start:
;thông báo nhập số đầu
    mov rax, 1          ; write
    mov rdi, 1          ; xuất
    mov rsi, msg1       ; địa chỉ chuỗi
    mov rdx, msg1_len   ; độ dài chuỗi
    syscall
; nhận input số đầu tiên
    mov rax, 0          ; read
    mov rdi, 0          ; vào
    mov rsi, num1       ; buffer nhận dữ liệu
    mov rdx, 30         ; số byte tối đa nhận
    syscall
    
;RAX = số byte đã đọc (bao gồm '\n') vì 
    dec rax                 ; **GIẢI THÍCH 1**
    mov byte [num1 + rax], 0
    
    mov rbx, num1       ; RBX trỏ tới chuỗi
    call _stoi          ; _stoi là hàm chuyển string thành int, tí làm sau
    mov r8, rax         ; Lưu giá trị số thứ nhất vào R8



; số thứ 2, comment cũng giống số 1 thoai
; write
    mov rax, 1
    mov rdi, 1
    mov rsi, msg2
    mov rdx, msg2_len
    syscall

;read
    mov rax, 0
    mov rdi, 0
    mov rsi, num2
    mov rdx, 30
    syscall

    dec rax
    mov byte [num2 + rax], 0
    
    mov rbx, num2
    call _stoi
    
; Giờ cộng 2 số lại
    add rax, r8         ;RAX = num1 + num2(dejavu r8 chứa num1, rax chứa num 2)
    
;----IN KẾT QUẢ----
    mov rbx, buf        ; RBX trỏ tới buf kết quả
    call _itos          ; Gọi hàm chuyển int -> string, tí nữa viết sau.
    
;in thông báo kết qủa
    mov rax, 1
    mov rdi, 1
    mov rsi, msgkq
    mov rdx, msgkq_len
    syscall

;in kết quả

mov rax, 1
    mov rdi, 1
    mov rsi, buf
    mov rdx, rcx        
    syscall

;Thoát
    mov rax, 60         ;  exit
    xor rdi, rdi        ;  0 là hoàn thành
    syscall

;----viết các lệnh call_

_stoi:    ;STRING TO INT       ***GIẢI THÍCH 2***
    xor rax, rax        ; reset thanh ghi
    xor rsi, rsi        ; reset luôn
.next:
    mov dl, [rbx + rsi] ; Lấy ký tự hiện tại
    cmp dl, 0           ; Nếu gặp NULL → kết thúc
    je .done            ; Tí tạo tí tạo

    imul rax, rax, 10   ; result = result * 10
    sub dl, '0'         ; ASCII '0'..'9' → số 0..9
    add rax, rdx        ; cộng chữ số vào kết quả
    inc rsi             ; sang ký tự tiếp theo
    jmp .next

.done:
    ret                 ; quay về địa chỉ đã call
    

;Khi này rax chứa kết quả đã cộng
_itos: ; INT TO STRING
    mov rcx, 0          ; RCX (counter) đếm số chữ số
; ***GIẢI THÍCH 3***
.convert:
    xor rdx, rdx        
    mov rbx, 10
    div rbx             ; RAX = RAX / 10, RDX = phần dư
    add dl, '0'         ; số → ASCII
    push rdx            ; lưu vô stack 
    inc rcx
    test rax, rax       ; Bật cờ ZF
    jnz .convert        ; còn chữ số thì tiếp tục

    mov rbx, buf        ; RBX trỏ lại buffer

.printloop:
    pop rdx             ; lấy chữ số theo thứ tự đúng
    mov [rbx], dl
    inc rbx
    loop .printloop     ; lặp RCX lần
    ret


```
KẾT QUẢ
![image](https://hackmd.io/_uploads/HkZUOhCE-x.png)
.
.
.
.
.
.
*Giải thích 1* :
   - Ví dụ input : '1' '2' '3' '\n'
       - num1 ( có 30 byte ô nhớ ) ban đầu là : `00 00 00 00 ... ` &rarr;  `31 32 33 0A 00 00 ...` Lúc này RAX đọc được 4 byte, ta `dec rax` để giảm còn 3.
       - `[num1 + rax]` là trỏ đến 1 ví trị trong mang thứ `rax` , khi này `num1 + 3` là trỏ đến `0A` ( tính từ 0 ), sửa đó `mov 0` vào patching giá trị `\n` thành `NULL`

.
.
.
.
.
.

*Giải thích 2* :  (tiếp tục với dữ liệu 1 2 3 đã chuyển đổi )
- Dèjavu là `mov rbx, num1`, rbx trỏ tới mảng num1 chứa chuỗi đầu tiên
- Reset thanh ghi để không dính giá trị rác
- Tạo vòng lặp duyệt từng kí tự, trừ nó với `'0'` để cho ra dạng int của chuỗi. Dùng thanh ghi RDX (Data) để lưu trữ cho tiện, sài DL (phần low) cho nó nhanh vì có 1 byte mà
    - dl = [num1 + 0] = 0x31  (Tức là '1' trong Ascii) 
     - cmp dl , 0  : So sánh với NULL, nếu NULL thì `je.done` là hàm kết thúc việc chuyển đổi , còn khác 0 thì chạy tiếp
     - Sau đó thì biến đổi như này 
     ![image](https://hackmd.io/_uploads/ryrxKi0N-l.png)
    - Mỗi lần biến đổi xong, tăng rsi lên 1, để trỏ từng kí tự trong mảng. 
    - Sau đó `jmp .next` kiểm tra xem NULL chưa, chưa NULL thì lặp tiếp.
    
.
.
.
.
.
.

*Giải thích 3*:

.convert
- Trước tiên thì reset thanh ghi `RDX`
- Giải ngược từng số + `'48'` vô để ra ACSII kí tự, đưa kí tự vào `stack`.
- `inc RCX` vì mỗi lần đưa vào stack là 1 kí tự, tăng số counter để tí lặp bấy nhiêu lần lại cho việc trả KẾT QUẢ.
- Bật cờ ZF, `rax AND rax` để kiểm tra, rax = 0 thì làm tiếp, còn không thì lặp `.convert`

Ví dụ kết quả là 1 2 3, lưu vào stack là 3 2 1 
![image](https://hackmd.io/_uploads/Sy9wLh04Wx.png)


.printloop:
- Trỏ rbx vô mảng buf ( mảng lưu kết quả).
- Như hình trên thì pop từng số từ `stack` `rdx` ra xong lưu vào từng ô nhớ của rbx ( trỏ tới buf).
- Tăng địa chỉ trỏ lên rồi lặp đến khi sạch `stack` và về `ret`



# CỘNG TRÀN BAO NHIÊU CŨNG ĐƯỢC

```a
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
    newline db 10

section .bss
    num1 resb 100   ; Tăng lên 100 byte ô để lưu
    num2 resb 100  
    buf  resb 101   

section .text
    global _start

_start:
    ; --- Nhập số thứ nhất ---
    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, 100
    syscall
    
    ; Lưu độ dài số 1 vào R12 và bỏ ký tự xuống dòng (\n)
    dec rax
    mov r12, rax 

    ; --- Nhập số thứ hai ---
    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, 100
    syscall

    ; Lưu độ dài số 2 vào R13 và bỏ ký tự xuống dòng (\n)
    dec rax
    mov r13, rax
    
    ; SET các thanh ghi
    mov rsi, r12
    dec rsi             ; RSI trỏ vào ký tự cuối cùng của num1
    mov rdi, r13
    dec rdi             ; RDI trỏ vào ký tự cuối cùng của num2
    mov r10, 0          ; R10 lưu số NHỚ 
    mov rcx, 0          ; RCX đếm số chữ số đã PUSH vào Stack
    
    .loop_add:
    xor rax, rax
    add rax, r10        ; Cộng số NHỚ từ hàng trước vào tổng hiện tại
    mov r10, 0          ; Reset số nhớ

    ; Lấy chữ số num1 nếu còn
    cmp rsi, 0
    jl .check_num2
    movzx rdx, byte [num1 + rsi]        ;movzx để an toàn số không dấu
    sub rdx, '0'        ; Chuyển ASCII sang số
    add rax, rdx
    dec rsi
    
    .check_num2:
    ; Lấy chữ số num2 nếu còn
    cmp rdi, 0
    jl .calc
    movzx rdx, byte [num2 + rdi]
    sub rdx, '0'        ; Chuyển ASCII sang số
    add rax, rdx
    dec rdi

.calc:
    ; BƯỚC QUAN TRỌNG: Chia 10 để lấy "Viết" và "Nhớ"
    mov rdx, 0
    mov rbx, 10
    div rbx             ; RAX = Thương (Nhớ), RDX = Dư (Viết)
    
    mov r10, rax        ; Lưu lại số Nhớ cho lượt sau
    add rdx, '0'        ; Chuyển số dư về ASCII
    push rdx            ; ĐƯA VÀO STACK ĐỂ ĐẢO CHIỀU
    inc rcx             ; Tăng biến đếm chữ số
    
    ; Điều kiện lặp: Còn số 1 HOẶC còn số 2 HOẶC còn số Nhớ
    cmp rsi, 0
    jge .loop_add
    cmp rdi, 0
    jge .loop_add
    cmp r10, 0
    jg .loop_add

    ; --- LẤY TỪ STACK RA VÀ LƯU VÀO BUFFER ---
    mov r14, rcx        ; Lưu tổng số chữ số vào R14 để in
    mov rbx, buf        ; Trỏ RBX vào buffer kết quả
.pop_loop:
    pop rax             ; Lấy chữ số ra (đúng thứ tự từ trái sang phải)
    mov [rbx], al
    inc rbx
    loop .pop_loop

    ; --- IN KẾT QUẢ ---
    ; In chữ "Tong la: "
    mov rax, 1
    mov rdi, 1
    mov rsi, msgkq
    mov rdx, msgkq_len
    syscall

    ; In chuỗi số trong buffer
    mov rax, 1
    mov rdi, 1
    mov rsi, buf
    mov rdx, r14        ; Độ dài là số lần đã Pop
    syscall

    ; In newline cho đẹp
    mov rax, 1
    mov rdi, 1
    mov rsi, newline
    mov rdx, 1
    syscall

    ; Thoát chương trình
    mov rax, 60
    xor rdi, rdi
    syscall
```

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