đượ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.
0x. Ví dụ: hex 00 01 = 0x0100, hex 2e d7 = 0xd72e.Chuyển đổi cơ bản:
0x00 đến 0xFF (hex).0x0000 → 0xFFFF.0x, ví dụ 0xD730.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.
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):
[EA] và [EA+].0001 có 2 cách biểu diễn: hex 00 01 hoặc 0x0100.0x ngược lại với hex, địa chỉ viết là 0x, ví dụ: 0xe9e0, 0xd730.hex 2e d7 = 0xd72e, hex 30 d6 84 d1 = 0xd184d630.Các địa chỉ quan trọng thường dùng trong ROP:
0xD180 – 0xD246 : input buffer0xD248 – 0xD30E : history0xD522 – 0xD5E8 : undo0xDDD4 : screen buffer 10xE3D4 : screen buffer 20xE9E0 : vùng an toàn để lưu chương trình (backup)0xD730 : địa chỉ chạy chương trình (runtime)0xD111 : chế độ hiện tại0xD129 : kích thước font ở LineIO0xD137 : kích thước font (font_size)0xD139 : current_screen_buffer0xD113 : con trỏ nhấp nháy0xD138 : đảo ngược trắng đen (01: bình thường, 04: ngược)0xF033 : độ sáng màn hình0xF034 : ma trận0xF039 : cuộn màn hìnhMOV a, b : gán b vào a.ADD a, b : a = a + b.SUB a, b : a = a - b.CMP a, b : so sánh a và b, cập nhật cờ.L a, [b] : tải từ bộ nhớ tại địa chỉ b vào a.ST a, [b] : lưu a vào bộ nhớ tại địa chỉ b.PUSH r : giảm SP và lưu r vào stack.POP r : lấy dữ liệu từ stack vào r, tăng SP.B label : nhảy không điều kiện.BC cond, label : nhảy có điều kiện.BL label/reg : nhảy và lưu địa chỉ trả về vào LR.RT : trở về từ chương trình con (PC = LR).RTI : trở về từ ngắt.NOP : không làm gì.EI / DI : bật / tắt ngắt toàn cục.SC / RC / CPLC : đặt / xóa / đảo cờ C.TB Rn.bit, RB Rn.bit, SB Rn.bit.PUSH EA, POP EA, PUSH LR.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*
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.
Đ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.
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 và ??=30).
Ví dụ: pop xr0 = call 17B34 = 34 7B 31 30.
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.
PUSH LR, PUSH QR8, … và kết thúc bằng POP PC hoặc RT.ST source, [ram] : ghi source vào ram. L source, [ram] : đọc từ ram vào source.
B function : nhảy không lưu địa chỉ trở về.BL function : nhảy, lưu địa chỉ trở về vào LR.POP source : lấy 2/4/8 byte từ stack vào source.POP PC : lấy 2 byte từ stack vào PC (nhảy).RT : lấy địa chỉ từ LR vào PC (trở về).Đị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 [=].
Đị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
Đị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
Đị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
Đị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"
Đị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
Đị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
Địa chỉ: 2F5EA
er0 = adr_of key getkey key: hex 00 00
Địa chỉ: 1F24E
er0 = adr_of key getscancode key: hex 00 00
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
Địa chỉ: 09F3C
Tham số: ER0 = giá trị thời gian trễ (2 byte).
er0 = 0x1f40 delay
19536 : verify_eq (==)195C0 : verify_ne (!=)19516 : verify_gt (>)19526 : verify_lt (<)194F6 : verify_ge (>=)19506 : verify_le (<=)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).
loop:
qr0 = 0xd62e3030d184d630
call 0x203c8
sp = er6, pop er8
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.
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.
Gadget: [er8] += er2, pop xr8, rt (địa chỉ 09CA0)
er8 = 0xE9E0 er2 = 0x0001 [er8] += er2, pop xr8, rt 0x30303030 # pad
Để giảm: er2 = 0xFFFF.
Đị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: [...]
Dùng gadget [er0] = r2 (208B2) hoặc [er0] = er2 (139D8).
xr0 = 0xE9E0 , 0x41 , 0x30 [er0] = r2
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
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.
0x ngược lại với hex: hex 00 01 → 0x0100.org) luôn setlr, setsfr và buffer_clear (nếu cần).0xd730.0xE9E0).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