HƯỚNG DẪN ROP TRÊN CASIO fx-580VN X

được biên soạn bởi @Bashamee

Tài liệu này tổng hợp kiến thức cơ bản về vi điều khiển nX-U16/100 và các kỹ thuật ROP (Return-Oriented Programming) để lập trình cho máy tính Casio fx-580VN X. Nội dung được trình bày dưới dạng tham khảo.


PHẦN 1: KIẾN THỨC CƠ BẢN

1.0 Hệ thập phân (dec), thập lục phân (hex) và nhị phân (bin)

Chuyển đổi cơ bản:

1.1. Các thanh ghi (Registers)

CPU giống như người thợ làm việc tại bàn, thanh ghi là những ô nhớ cực nhanh nằm ngay trong CPU.

1.1.1. Sơ đồ phân cấp thanh ghi

QR0 (8 byte)
├── XR0 (4 byte)
│   ├── ER0 (2 byte)
│   │   ├── R1
│   │   └── R0
│   └── ER2 (2 byte)
│       ├── R3
│       └── R2
└── XR4 (4 byte)
    ├── ER4 (2 byte)
    │   ├── R5
    │   └── R4
    └── ER6 (2 byte)
        ├── R7
        └── R6

QR8 (8 byte)
├── XR8 (4 byte)
│   ├── ER8 (2 byte)
│   │   ├── R9
│   │   └── R8
│   └── ER10 (2 byte)
│       ├── R11
│       └── R10
└── XR12 (4 byte)
    ├── ER12 (2 byte)
    │   ├── R13
    │   └── R12
    └── ER14 (2 byte)
        ├── R15
        └── R14

Thanh ghi điều khiển (quan trọng):

⚠️ KHÔNG CẦN THIẾT CHO NEWBIE: ELR1, ELR2, ELR3, PSW (Program Status Word) gồm các cờ C, Z, S, OV, MIE, HC, ELEVEL.

1.2. Biểu diễn số hex

1.3. Bộ nhớ

Các địa chỉ quan trọng thường dùng trong ROP:

1.4. Các lệnh cơ bản (assembly)

1.5. Ngăn xếp (stack)

Stack là vùng nhớ LIFO. SP trỏ đến đỉnh stack, stack giảm dần. Mỗi thao tác stack luôn theo word (2 byte). Các gadget ROP thường tận dụng việc thay đổi SP để điều khiển luồng.

*ĐỌC THÊM TRONG TÀI LIỆU nX-U16/100 Core|Cẩm nang hướng dẫn*


PHẦN 2: DISASSEMBLY, GADGET

2.1. Disassembly (disas) là gì?

File chứa mã assembly dịch ngược từ ROM, cho biết lệnh và địa chỉ của từng đoạn code. Từ đó tìm ra các gadget.

2.2. Gadget là gì?

Đoạn mã kết thúc bằng RT hoặc POP PC, cho phép nối tiếp nhiều gadget trên stack.

2.3. Cách gọi gadget trong code

Khi gọi một gadget, trong hex mất 4 byte. Giả sử địa chỉ gadget là 0xABCDE, viết: call ABCDE = DE BC ?A ?? (thường ?=3??=30).

Ví dụ: pop xr0 = call 17B34 = 34 7B 31 30.

2.4. Cách đọc file disassembly

Dòng có dạng: asm ; address | opcode

Ví dụ:

pop er2                    ; 18974 | F21E
pop pc                     ; 18976 | F28E

Để dịch địa chỉ thành mã hex: address = Z:YYXXH → code = XX YY ?Z ?? (?? thường là 3 và 30).

Ví dụ: address 18974 = 74 89 31 30, address 18976 = 76 89 31 30.

2.5. Quy ước gọi hàm (calling convention)

2.6. Đọc và ghi vào RAM

ST source, [ram] : ghi source vào ram. L source, [ram] : đọc từ ram vào source.

2.7. Lệnh nhảy B và BL

2.8. Ba lệnh đặc biệt: POP, POP PC, RT


PHẦN 3: CÁC HÀM PHỔ BIẾN

3.1. Hàm in chữ (printline)

Địa chỉ: 23DC8

Tham số: R0: linepos (1→31), R1: pad (thường 30), ER2: địa chỉ chuỗi.

Ví dụ: in "Xin chào" dòng 3 (linepos=21):

org 0xe9e0
setup:
    buffer_clear
    setlr
    setsfr
inchu:
    xr0 = 0x3021 , adr_of text
    printline
    render.ddd4
text:
    str "Xin~chào"
    0x00

Lưu ý: linepos: dòng 1: 01, dòng 2: 11, dòng 3: 21, dòng 4: 31. Printline font to (0E) tối đa 17 chữ.

Để chạy, compile asm sang hex, inject vào e9e0 và dùng launcher: | FD24 30 30 DA 7B 31 30 FE 03 E0 E9 30 D7 2E D7 32 89 31 30 30 30 74 1F 32 48 | rồi ấn [=].

3.2. Hàm in chữ font (smallprint)

Địa chỉ: 23DCC

Tham số: R0: font size (08, 0a, 0e), R1: linepos, ER2: địa chỉ chuỗi.

Ví dụ:

org 0xe9e0
start:
    setlr; setsfr; buffer_clear
    xr0 = 0x110e , adr_of text1
    smallprint
    xr0 = 0x210a , adr_of text2
    smallprint
    render.ddd4
text1: str "~~Xin~chào"; 0x00
text2: str "~~Hello"; 0x00

3.3. Hàm vẽ bitmap (render_bitmap)

Địa chỉ: 09848

Tham số: XR0 = hex <x_pos> <y_pos> <width> <height>, ER0 = adr_of bitmap.

Ví dụ: vẽ hình vuông 8x8 tại (10,10):

org 0xe9e0
start:
    buffer_clear; setlr; setsfr
ve:
    xr0 = hex 0A 0A 08 08
    render_bitmap
    er0 = adr_of hinh
    render.ddd4
hinh:
    hex ff 81 81 81 81 81 81 ff

3.4. Hàm vẽ đường thẳng (line_draw)

Địa chỉ: 08E62

Tham số: XR0 = hex <x1> <y1> <x2> <y2>

Ví dụ:

org 0xe9e0
start:
    buffer_clear; setlr; setsfr
ve:
    xr0 = hex 00 00 c0 3f
    line_draw
    render.ddd4

3.5. Hàm chờ phím SHIFT (waitshift)

Địa chỉ: 23DDE

Ví dụ:

org 0xe9e0
start:
    buffer_clear; setlr; setsfr
print:
    waitshift
    xr0 = 0x3021 , adr_of text
    printline
    render.ddd4
text: str "Da~nhan~SHIFT"

3.6. Hàm tính toán (calc_func)

Địa chỉ: 17922

Tham số: XR0 = hex <addr_calc_ptr> <addr_result>

Ví dụ: tính 36+67:

tinh:
    xr0 = adr_of addr_calc_ptr , adr_of result
    calc_func
addr_calc_ptr:
    adr_of calc
calc:
    hex 33 36 a6 36 37 00
result:
    hex 00 00 00 00 00 00 00 00 00 00

3.7. Hàm so sánh bảng (cmp_ea) – quan trọng

Địa chỉ: 09C20

So sánh ER0 với bảng được trỏ bởi EA. Mỗi mục: 2 byte giá trị so sánh, 2 byte dữ liệu. Nếu khớp, EA trỏ đến dữ liệu; nếu không, EA đến mục cuối (00 00).

Ví dụ:

cmp:
    er0 = 0x3667
    ea = adr_of table
    cmp_ea
    qr0 = [ea], lea D002H, [ea] = qr0
table:
    0x3667
    0x1234
    0x0000
    0x5678

3.8. Các hàm đọc phím

3.8.1. getkey / getscancode_nodelay (không chờ)

Địa chỉ: 2F5EA

er0 = adr_of key
getkey
key: hex 00 00

3.8.2. getscancode (chờ)

Địa chỉ: 1F24E

er0 = adr_of key
getscancode
key: hex 00 00

3.8.3. Bảng keycode (KI/KO)

hex 80 01   # SHIFT
hex 80 02   # ALPHA
hex 40 04   # ←
hex 80 08   # →
hex 80 04   # ↑
hex 40 08   # ↓
hex 80 10   # MENU
hex 40 01   # OTPN
hex 40 02   # CALC
hex 40 10   # TÍCH PHÂN
hex 40 02   # X
hex 20 01   # PHÂN SỐ
hex 20 02   # √
hex 20 04   # x²
hex 20 08   # xˆ
hex 20 10   # log
hex 20 20   # IN
hex 10 01   # (-)
hex 10 02   # ĐỘ
hex 10 04   # x^-1
hex 10 08   # SIN
hex 10 10   # COS
hex 10 20   # TAN
hex 08 01   # STO
hex 08 02   # ENG
hex 08 04   # (
hex 08 08   # )
hex 08 10   # S<=>D
hex 08 20   # M+
hex 04 10   # AC
hex 02 10   # ÷
hex 01 10   # -
hex 04 40   # x10
hex 01 40   # =
hex 10 40   # 0
hex 01 01   # 1
hex 01 02   # 2
hex 01 04   # 3
hex 02 01   # 4
hex 02 02   # 5
hex 02 04   # 6
hex 04 01   # 7
hex 04 02   # 8
hex 04 04   # 9
hex 04 08   # DEL
hex 02 08   # ×
hex 01 08   # +
hex 08 40   # .
hex 02 40   # ANS

3.9. Hàm làm chậm (delay)

Địa chỉ: 09F3C

Tham số: ER0 = giá trị thời gian trễ (2 byte).

er0 = 0x1f40
delay

3.10. Các dạng so sánh (verify)

Cách gọi: xr0 = <địa chỉ 1>, <địa chỉ 2>; call <verify function>

Kết quả: ER0=00 01, ER2=01 00 (True) hoặc ER0=ER2=00 00 (False).


PHẦN 4: KỸ THUẬT XÂY DỰNG CHƯƠNG TRÌNH ROP

4.1. Kỹ thuật xây dựng vòng lặp (loop)

4.1.1. Loop – dùng QR0 và strcpy (phổ biến)

loop:
    qr0 = 0xd62e3030d184d630
    call 0x203c8
    sp = er6, pop er8

4.1.2. Loop ngắn – dùng memcpy_auto_jmp

loop:
    call 0x2045c     # pop xr4, pop xr12
    0xd730           # dest
    0x01fe           # len
    0xe9e0           # src
    0xd724           # return_addr (src - 0xC)
    call 0x2b2ba     # memcpy_auto_jmp

Lưu ý: Khi có loop, địa chỉ program là 0xd730. LUÔN DÙNG org 0xd730 THAY VÌ org 0xe9e0.

4.2. Offset – Cách tính địa chỉ chính xác

Offset là khoảng cách byte từ địa chỉ cơ sở. Vùng backup 0xE9E0 cách runtime 0xD730 + 4784 byte. Do đó, adr_of [+4784] bien trỏ vào bản sao backup. Dùng offset +4784 để thay đổi dữ liệu lâu dài.

4.3. Tăng/Giảm giá trị của một địa chỉ

Gadget: [er8] += er2, pop xr8, rt (địa chỉ 09CA0)

er8 = 0xE9E0
er2 = 0x0001
[er8] += er2, pop xr8, rt
0x30303030          # pad

Để giảm: er2 = 0xFFFF.

4.3.1. Scroll – Cuộn màn hình liên tục

Địa chỉ 0xF039 điều khiển cuộn. Kết hợp tăng/giảm + delay + loop.

scroll_up:
    er8 = 0xF039
    er2 = 0x0001
    [er8]+=er2,pop xr8
    0x30303030
    er0 = 0x0500
    delay
loop: [...]

4.4. Ghi giá trị vào một địa chỉ (set)

Dùng gadget [er0] = r2 (208B2) hoặc [er0] = er2 (139D8).

xr0 = 0xE9E0 , 0x41 , 0x30
[er0] = r2

4.5. Xử lý phím với getkey và cmp_ea

setup_key:
    er0 = adr_of key
    getkey
    setlr
    pop er0
key:
    hex 00 00
    ea = adr_of table
    cmp_ea
    er6 = [ea]
    sp = er6, pop er8
func_1: ...
table:
    0x0101
    adr_of [-2] func1
    0x0000
    adr_of [-2] loop

4.6. Các dạng so sánh (verify) kết hợp với cmp_ea

Tương tự như xử lý phím, dùng verify function để lấy kết quả True/False vào ER0, ER2, rồi dùng cmp_ea để rẽ nhánh.


PHẦN 5: CÁC LƯU Ý CẦN NHỚ

  1. 0x ngược lại với hex: hex 00 01 → 0x0100.
  2. Label không được đặt trùng, không có dấu hay dấu cách.
  3. Dấu cách lùi đầu dòng không ảnh hưởng khi compile.
  4. Đầu mỗi program (sau org) luôn setlr, setsfrbuffer_clear (nếu cần).
  5. Thứ tự trong program: [các thứ] → loop → table → dữ liệu.
  6. Khi có loop, địa chỉ program là 0xd730.
  7. Để thay đổi dữ liệu lâu dài qua các lần loop, dùng offset +4784 để trỏ vào vùng backup (0xE9E0).

MỤC LỤC


PHẦN 6: KẾT THÚC

Những kiến thức về thanh ghi, bộ nhớ, các gadget và kỹ thuật xây dựng chương trình trong tài liệu này được đúc kết từ tài nguyên có sẵn, quá trình nghiên cứu, tham khảo tài liệu chính thống "nX-U16/100 Core Cẩm nang hướng dẫn – Vi điều khiển CMOS 16‑bit" của LAPIS Semiconductor, cùng những trải nghiệm thực tế từ cộng đồng.

Hy vọng rằng những dòng chữ này sẽ là điểm khởi đầu vững chắc, giúp bạn tự tin tạo ra chương trình riêng trên chiếc máy tính casio của mình. Đừng ngại thử nghiệm, sai và sửa – đó chính là cách học nhanh nhất.

Lời cảm ơn đặc biệt gửi đến tất cả thành viên trong cộng đồng đã chia sẻ, thử nghiệm và đóng góp ý kiến để tài liệu được hoàn thiện hơn. Rất mong nhận được những phản hồi từ bạn để cùng nhau phát triển.

Chúc bạn thành công!

Tài liệu được biên soạn bởi: @Bashamee
Đóng góp: @mathfischist_91422 (big contribution), @ahbanh (aka luoi)
Tester: @phong2k11_4, @itssuperplayz (aka Mink), @hanhdo0230, @dunghackcasio, vuhoangquan19112, …
Nguồn tham khảo chính: nX-U16/100 Core Instruction Manual – LAPIS Semiconductor
Lần cập nhật cuối: 13:28 29/3/2026


HẾT