Sunday, 14 February 2016

Đăng lại

* Bạn đã bao giờ chơi một game hack với những yếu tố hoàn toàn mới hay một bản dịch Việt ngữ? Bạn thắc mắc vì sao người ta có thể làm cho nhân vật Mario bay được, trong khi mặc định trong game bình thường là chạy bộ? Câu trả lời là Assembly, hay gọi tắt là Asm. Bài viết này cung cấp cho bạn đọc những kiến thức cơ bản nhất về Asm, thông qua game Fire Emblem 4 Seisen no Keifu trên máy Super Nintendō làm ví dụ. Bài viết cố gắng cô đọng, tóm tắt những kiến thức cần thiết nhất để người (đủ kiên nhẫn) đọc có thể làm theo và tự phát triển nền tảng cho riêng mình.
Hiện nay trên Net có rất nhiều tài liệu về Asm 65816 dành cho hệ máy SFC (SNES) bằng tiếng Nhật, tiếng Anh, tiếng Tây, tiếng Bồ.... nhưng chưa có một tài liệu nào bằng tiếng Việt. Có thể xem đây là tài liệu tiếng Việt đầu tiên về lãnh vực này. Nếu muốn tìm hiểu sâu hơn để hack/dịch game Snes thì có thể tham khảo thêm tài liệu trong các ngôn ngữ kể trên.

I. Cần chuẩn bị

Để theo được bài viết này, bạn cần chuẩn bị một số thứ tối thiểu như sau:

1. Kiến thức về hệ nhị phân (Binary, Bin) và hệ thập lục (Hexadecimal, Hex), kiến thức về bit và byte. Không nhắc lại ở đây vì những bài viết về hai hệ số này đã tràn ngập Google rồi, tôi không tốn thời gian gõ lại làm gì.
2. Lòng đam mê với game SNES. Cái này thì ai trả qua tuổi thơ với SNES thì mặc định là có (:V).
3. Một chút thời gian rãnh. Không nhiều lắm, nhưng đủ để đọc hết bài viết này và làm theo. Có thể chia thành nhiều kỳ, số kỳ không hạn chế, tùy vào độ rãnh háng của bạn.
4. Giả lập kiêm Debugger để chạy game SNES. Trên Net có kha khá, nhưng khuyên dùng Snes9X. Tải về tại đây (click vào).
5. Bản Mộc Đế 4 (Fire Emblem 4 Seisen no Keifu). Dùng được cho bản gốc tiếng Nhật và bản dịch tiếng Việt của Asm65816. Ở đây dùng bản tiếng Việt. Tải về tại đây (click vào)
6. Trình Hex Editor. Có vô vàn, nhưng tôi hay dùng Stirling vì sự tiện dụng của nó. Tải bản tiếng Việt của Stirling tại đây (click vào).
7. Lunar Address, tiện ích chuyển đổi giữa địa chỉ PC và địa chỉ SNES. Tải về tại đây (click vào).
8. Cuối cùng, à chưa phải cuối cùng. Một chút kiên nhẫn. Không cần nhiều lắm, chỉ cần đủ để đọc lại bài này từ đầu chí cuối sau khi đọc hết mà vẫn không hiểu gì.
9. Đây mới là cuối cùng. Cần một bộ óc bình thường như cân đường hộp sữa. Không cần cái đầu quá nhanh như Corei7 hay Qualcom 820, nhưng nếu nhanh được như vậy thì càng tốt. Chỉ cần làm được 5 phép tính cộng trừ nhân chia số có 2 chữ số trong thời gian 1 phút là đủ tiêu chuẩn.


II. Khái quát về Asm 65816

Asm là từ viết tắt của Assembly, tiếng Việt dịch là "hợp ngữ", gọi nôm na là ngôn ngữ máy. Asm là ngôn ngữ bậc thấp mà máy tính (các loại máy nói chung, không nói riêng về "máy vi tính") sử dụng và hiểu được. Nó khá đơn giản, chỉ gồm 2 con số là "0" và "1". Chả còn gì đơn giản hơn. Máy tính vốn dĩ tối dạ và ngu đần, không có trí tuệ. Nó không hiểu được tiếng người, nó không phân biệt được trai gái, không biết đâu là rượu đâu là nước,... Nói tóm lại, ngoài "0" và "1" ra thì nó chả biết gì nữa hết. Điểm cộng duy nhất của máy tính là mặc dù ngu đần nhưng lại tuy u ám không có trí tuệ nhưng lại nhanh trí. 
OK? Trí tuệ và nhanh trí là hai khái niệm hoàn toàn khác nhau. Máy tính có thể đếm, cộng trừ nhân chia hàng triệu con số trong nháy mắt, nhanh hơn bất kỳ một con người nhanh trí nào. Nhưng vì không thông minh nên nó cứ ngồi đợi con người ra lệnh, giao việc cho làm. Nếu máy tính mà thông minh thì chắc giờ này tôi đang làm việc nhà cho nó rồi, đâu rãnh mà ngồi đây viết linh ta cho các bạn đọc linh tinh.
Quay trở lại vấn đề, máy tính rất đơn giản. Còn con người thì đẳng cấp hơn nhiều, và cũng phức tạp hơn nhiều. Thế giới của máy tính chỉ có "0" và "1" thì thế giới con người còn có gái gú, rượu chè, hút sách, lừa lọc, giết hại nhau,..... Khi đã quen thuộc với những cái siêu phức tạp như vậy thì dường như chúng ta gặp rất nhiều khó khăn trong việc giao tiếp với thứ đơn giản như cái máy tính.
Cũng giống như hệ thống ngôn ngữ của người lớn thì phức tạp, có thể phát âm rất nhiều âm tiết tinh vi. Trong khi cổ họng của trẻ sơ sinh chỉ có vài từ "ă", "ú" và "â". Vì vậy mà đại đa số người lớn chúng ta không hiểu bọn con nít mới đẻ chúng nói gì, mà phải cần qua người phiên dịch là mẹ chúng. Bà mẹ này vừa hiểu được chúng, vừa hiểu được chúng ta nên trở thành cây cầu nối trong giao tiếp giữa hai bên. Ngôn ngữ Assembly, hay ASM cũng vậy. Nó vừa để con người có thể hiểu được, vừa để máy tính hiểu được.

Ngôn ngữ mà ngày xưa các cụ dùng để giao việc cho máy SNES có tên mã là 65816, 65C816. Gọi túm lại là Asm 65816. Đây là hệ máy 16 bit, vì có Register có độ rộng 16 bit. Nếu muốn hack/dịch game SNES thì kiểu gì cũng phải biết qua ngôn ngữ này. Những khái niệm cơ bản nhất về nó sẽ lần lượt xuất hiện trong bài này.

Điều đầu tiên cần phải biết là CPU 65816 thực hiện tuần tự các mệnh lệnh mà người ta viết sẵn theo thứ tự từ trên xuống, từ trước đến sau.





....


....


Nó sẽ thực hiện lần lượt đúng như thứ tự trên, rất đơn giản đúng không?

Đầu tiên, mở FE4 bằng Stirling, ta sẽ thấy vô vàn con số ở hệ thập lục.



Những con số này chính các dòng lệnh để CPU thực thi. Mỗi một số thập lục ứng với một mệnh lệnh, và chúng đều có ý nghĩa riêng. CPU của máy 65816 (Snes) có tới 256 lệnh. Một số lệnh chủ yếu sẽ được giới thiệu bên dưới đây.



III. Khái niệm cơ bản

Dưới đây là những khái niệm cơ bản nhất mà bạn cần phải biết khi bước chân vào thế giới 65816.

1. Endian

Endian là thuật ngữ chỉ trật tự sắp xếp byte trong bộ nhớ của máy tính. Có 2 kiểu phổ biến là Big endian  Little endian. Đối với kiểu Big endian thì dữ liệu được xếp theo trật tự thuận, chẳng hạn một chuỗi gồm 4 byte 03 6A 7F 45 thì sẽ được xếp là 03 6A 7F 45. Còn đối với kiểu Little endian thì trật tự này được xếp ngược là 45 7F 6A 03. Các hệ máy console cũ như NES, SNES, PlayStation, Megadrive... thì sử dụng kiểu Little endian, còn các hệ console đời mới như PlayStation 3, Xbox 360... đều sử dụng kiểu Big endian. Xác định kiểu endian là một khâu quan trọng trong việc hack dữ liệu.

2. Register

Có thể nói, linh hồn của một phần cứng chính là các Register. Chữ "Regist" mang nghĩa "duy trì". Register là thứ duy trì một cái gì đó. Trong ngôn ngữ Asm thì Register duy trì các giá trị, cụ thể là các biến số.
Phần cứng 65816 có nhiều Register, trong số đó hay gặp là: A (Accumulator), X và Y (Index Register), DB (Data bank) và P (Status Register, hay còn gọi là Flag). Ngoài ra còn có PC (Program Counter) Register giữ địa chỉ của lệnh hiện tại, D (Direc page) Register, S (Stack) Register. Trong số này thì chỉ có A, X, Y là có thể chuyển đổi giữa 2 chế độ: 8 bit và 16 bit để giữ byte bằng cách set và reset các bit trong P Register. P là Register 8 bit, theo thứ tự là:

n: Negative
v: Overflow
m: Memory/Accumulator Select
x: Index Register select
d: Decimal
i: Interrupt
z: Zero
c: Carry

Từng bit khi được set (trạng thái "1") hay reset (trạng thái "0") thì sẽ cho tác dụng khác nhau. Chẳng hạn, nếu bit thứ 5 của P Flag được set (1) thì A Register là 8 bit, còn nếu là reset (0) thì A có chứa được giá trị 16 bit. Tương tự, X và Y Register chứa được giá trị 8 hay 16 bit phụ thuộc vào trạng thái của bit thứ 4 của P Flag.


3. Addressing mode

Trong ngôn ngữ Asm, có nhiều cách để chỉ định địa chỉ, và tất cả những cách này đều được gọi chung là Addressing mode. Chẳng hạn, lệnh LDA #$20 có ý nghĩa khác với LDA $20.
Lệnh đầu tiên là tải giá trị $20 (thập lục) vào A Register, còn lệnh sau tải giá trị (biến số) tại địa chỉ $20 vào A Register. 

a. Immediate: kiểu định địa chỉ này được xác định với một giá trị cụ thể. Chẳng hạn

LDA #$FF ;tải giá trị $FF vào A
SEP #$30 ;đưa giá trị $30 vào P Flag

b. Direct: địa chỉ đích được hình thành bằng cách thêm một địa chỉ 8 bit vào Direct page Register (D). Chẳng hạn:

LDA $20 tải giá trị (biến số) tại địa chỉ $20
Nếu D = $1000 thì CPU sẽ đọc một byte (nếu A 8 bit) từ địa chỉ $1020.

c. Absolute: địa chỉ đích được hình thành bằng cách thêm một địa chỉ 16 bit vào DBR. Chẳng hạn:

DBR: $80
LDA $901C

Thì CPU sẽ tải một byte từ địa chỉ $88: 901C vào Accumulator (A).

d. Absolute Long: địa chỉ đích chính là địa chỉ được thể hiện trong lệnh.

LDA $808000 ; tải một byte từ địa chỉ $80:8000 vào A
LDA $FF9090 ; tải một byte từ địa chỉ $FF:9090 vào A

d. Accumulator: địa chỉ đích chính là A Register.

INC ; tăng giá trị của A lên 1

e. Direct Indirect Indexed: đây chính là nơi các Index Register (X và Y) vào cuộc. CPU sẽ tải 2 byte (16 bit) từ địa chỉ Direct page để tạo thành một địa chỉ nền kết hợp với DBR. Cuối cùng, giá trị của Y được thêm vào địa chỉ nền kia để tạo thành một địa chỉ 2 byte.


DBR: $80, D: $0020, Y: 0001
Memory dump (các giá trị trong bộ nhớ lúc này):
0030: 30 40 23 22 F4 22 23 1C
0038: 23 2D DD F4 FF FF FF FF

LDA ($10), Y

Sẽ cho địa chỉ sau:

Đầu tiên ta có $10 + giá trị tại D ($0020) là: $10 + $0020 = $0030
Nhìn vào bộ nhớ (Memory), lúc này ta thấy giá trị tại $30 và $31 là 30 và 40.
Do vậy địa chỉ sẽ là: $3040, nhưng vì SNES sử dụng kiểu Little endian nên địa chỉ đúng sẽ là $4030.
Kết hợp với DBR để có địa chỉ nền: $80:4030
Cuối cùng, tính thêm giá trị của Y vào: $80:4030 + $0001 = $80:4031

Như vậy, dòng lệnh trên

LDA ($10), Y

sẽ tải một byte từ địa chỉ $80:4031 vào trong A. Thường thì kiểu định địa chỉ này được dùng trong vòng lặp, khi Y được gia tăng mỗi lần để lấy một loạt dữ liệu trong Memory. Chẳng hạn


DBR: $80, D: $0020, Y: 0001
Memory dump:
0030: 30 40 23 22 F4 22 23 1C
0038: 23 2D DD F4 FF FF FF FF

LDA ($15), Y
INY

Địa chỉ DP = $0020 + $15 = $ 0035
Địa chỉ nền = DBR:<2 a="" byte="" ch="" t=""> = $80:2322
Địa chỉ đích = Địa chỉ nền + Y = $80:2323
INY: gia tăng giá trị của Y lên 1
Như vậy, 2 dòng lệnh trên sẽ liên tục tải các giá trị tại $80:2322, $80:2323, $80:2324,... vào A.

Thường thì giá trị tại D Register là 0, cho nên việc tính toán cũng đơn giản hơn.


f. Direct Indirect Indexed Long: kiểu xác định địa chỉ này về cơ bản thì giống kiểu e. Chỉ khác nhau ở chỗ nó sử dụng 3 byte tại địa chỉ DP thay vì 2 byte và không kết hợp với DBR. Ví dụ:

DBR: $80, D: $0020, Y: 0001
Memory dump:
0030: 30 40 23 22 F4 22 23 1C
0038: 23 2D DD F4 FF FF FF FF

LDA [$10], Y

Địa chỉ DP = $0020 + $10 = $0030
Địa chỉ nền = $23:4030.
Địa chỉ đích = địa chỉ nền + Y = $23:4030 + $0001 = $23:4031

g. Direct Indexed Indirect: sau khi tính địa chỉ Direct page, cộng với giá trị tại X. Sau đó lấy 2 byte tại địa chỉ này, kết hợp với DBR để tạo thành địa chỉ đích.

DBR: $80 D: $0020 X: $0004
Memory dump:
0020: FF 00 FF 09 33 33 09 88
0028: 08 76 66 36 D7 23 99 00

LDA ($02, X)


Địa chỉ DP = $02 + D = $0022
Sau đó kết hợp với X

Địa chỉ DP + X = $0026
2 bytes tại $0026 là (09 và 88), đảo ngược (Little endian) thành $8809, cuối cùng kết hợp với DBR:

DBR:$8809 = $80:8809
CPU sẽ tải giá trị tại địa chỉ $80:8809 vào A.

h. Direct Indexed với X: trong kiểu định địa chỉ này thì địa chỉ DP được cộng với giá trị tại X để tạo thành địa chỉ đích. Địa chỉ đích luôn nằm ở bank 0.


D: $0020 X: $0004

LDA $30, x

Địa chỉ DP = $30 + $0020 = $0050
Địa chỉ đích = địa chỉ DP + X = $00:0054

i. Direct Indexed với Y: giống kiểu h. nhưng khác nhau ở chỗ địa chỉ DP cộng với giá trị tại Y để hình thành địa chỉ đích.

j. Absolute Indexed với X: giá trị 2 byte được cộng với X rồi kết hợp với DBR để hình thành địa chỉ đích.

DBR: $80 X: $0001

LDA $8000, X
Địa chỉ đích = DBR:($8000 + X) = $80:8001

LDA $6988, X
Địa chỉ đích = DBR:($6988 + X) = $80:6989

k. Absolute Indexed với Y: giống kiểu j. nhưng thay vì cộng với X thì ở đây cộng với Y.

l. Absolute Long Indexed với X: địa chỉ đích được hình thành bằng cách cộng giá trị tại X với địa chỉ 3 byte.


X: $0001

LDA $808000, X ; tải một byte tại $80:8001 vào A
LDA $589112, X ; tải một byte tại $58:9113 vào A


m. Absolute Long Indexed với Y: giống kiểu l. nhưng thay vì cộng với X thì ở đây cộng với Y.



n. Program Counter Relative: kiểu định địa chỉ này chỉ được dùng trong các lệnh phân nhánh. Giá trị 8 bit được thêm vào PC (Progtam Counter) để tạo thành địa chỉ đích mà lệnh thực thi nhảy đến. Giá trị 8 bit này có phạm vi từ -128 cho đến 127. 

BRA $8005 ; phân nhánh sang địa chỉ $8005 trong phạm vi (-128 tới 127) 



o. Program Counter Relative Long: giống kiểu n. nhưng phạm vi trong khoảng (0 tới 65535). Chỉ có lệnh BRL và PER dùng kiểu này.

p. Absolute Indirect: địa chỉ đích được hình thành từ 2 byte.

Memory dump:
0000: 90 77 78 00 43 00 00 00
0008: 33 45 12 33 11 11 FF FF

JMP ($0008)
CPU sẽ lấy 2 byte tại $0008 là (33, 32), rồi nhảy sang địa chỉ $4533.


q. Absolute Indexed Indirect: giá trị 2 byte được cộng với X, sau đó 2 byte tại địa chỉ này được dùng để tạo nên địa chỉ đích.

X: 0001
Memory dump:
0000: 90 77 78 00 43 00 00 00
0008: 33 45 12 33 11 11 FF FF

JMP ($0008, X)
Ta có $0008 + X = $0009
Lấy 2 byte từ $0009 = 45 12
$1245 là địa chỉ mới.


r. Direct Indirect: 2 byte tại địa chỉ Direct Page được dùng để tạo thành địa chỉ 16 bit. Sau đó kết hợp với DBR để tạo thành địa chỉ đích 24 bit.


D: 0000 DBR: $80

Memory Dump:
0040: 50 00 80 00 22 23 33 44
0050: 60 21 21 21 22 55 55 66

LDA ($40)
Địa chỉ DP = $40 + D = $40
Địa chỉ 16 bit tại $40 = $0050
Địa chỉ đích = DBR:$0050 = $80:0050


s. Direct Indirect Long: 3 byte tại địa chỉ Direct page được dùng để tạo thành địa chỉ đích.

D: 0000 DBR: $80

Memory Dump:
0040: 50 00 80 00 22 23 33 44
0050: 60 21 21 21 22 55 55 66

LDA [$40]
Địa chỉ DP = $40 + D = $40
Địa chỉ đích = $80:0050


4. Memory mapping và địa chỉ RAM/ROM

Trong phần trước, ta đã gặp từ "bank".
Trong địa chỉ XX:YYZZ thì phần XX chính là bank.
ROM của SNES được tải trong phạm vi từ 00:0000 cho tới FF:FFFF.
Vị trí của ROM trong bộ nhớ khác nhau tùy thuộc vào chủng loại ROM (LoROM và HiROM).
Tham khảo thêm bài viết này (click vào).để biết thêm chi tiết.

Nếu không biết ROM đối tượng thuộc loại gì thì có thể dùng Lunar Address, chọn Auto-Detect Type rồi chọn đường dẫn đến ROM để biết. FE4 là HiROM.



Phần trong khoanh đỏ là để phần mềm xác định giúp ta loại ROM, phần trong khoanh vàng cho biết ROM có $200 header hay không. Phần trong khoanh màu lam là địa chỉ khi xem bằng Hex editor, còn trong khoanh màu lục là địa chỉ tương ứng trong Memory của máy SNES.
Như trong hình thì địa chỉ $223AC khi mở bằng Hex editor thì trong bộ nhớ của SNES, địa chỉ đó là $C2:21AC ($21AC ở bank C2).


5. Giải thích về Debugger Snes9X

Các phiên bản cũ của Debugger này không hỗ trợ ROM có header. Khi chạy cần phải cắt bỏ header đi. Tuy nhiên các bản sau này vẫn chạy được ROM có header.



Khi bắt đầu nạp game thì sẽ xuất hiện cửa sổ Debug Console với các chức năng như sau:

Next Op Hiển thị mệnh lệnh tiếp theo được thi hành
Step Into Thi hành 1 lệnh tiếp theo. Khi có Sub routine thì sẽ nhảy đến Sub routine đó.
Step Over Thi hành 1 lệnh tiếp theo. Khi có Sub routine thì sẽ thi hành liên tục đến khi kết thúc Sub routine đó
Step Out Thi hành mệnh lệnh tiếp theo cho tới khi kết thúc Sub routine hiện tại
Skip Bỏ qua 1 lệnh tiếp theo 
Clear Text Xóa hết dữ liệu log hiển thị trên cửa sổ Debug Console
Disassemble Dis-assemble (phân tích ngược) từ địa chỉ bắt đầu/kết thúc đã chỉ định
Run Chạy game cho đến khi gặp phải Break point
Reset ROM Có gì khó hiểu?
Frame Adv Thi hành số frame chỉ định (*1)
Vector Info Hiển thị Vector của CPU và APU (bộ xử lý âm thanh) 
Sprite Status Hiển thị thông tin liên quan đến sprite mà EMU đang hiển thị
APU State APU
Sample Addr Hiển thị địa chỉ mẫu mà APU đang duy trì
Show Hex Gọi Hex Editor. Từ đây có thể xem được ROM, RAM, VRAM, ARAM và SRAM
Dump RAM Trích xuất RAM bắt đầu từ địa chỉ chỉ định, với số byte chỉ định
Dump Palette Hiển thị palette
What’s Used Hiển thị chức năng hiển thị mà ROM hiện đang sử dụng(VRAM, BG,..)
What’s Missing
Trace From Chỉ định địa chỉ bắt đầu ghi log
Reset Debug Xóa thông tin debug
BreakPoints Chỉ định Break point
Exec Break (dừng) ngay trước khi thực hiện mệnh lệnh ở địa chỉ được chỉ định
Read Break (dừng) ngay trước khi đọc từ địa chỉ được chỉ định
Write Break (dừng) ngay trước khi ghi vào địa chỉ được chỉ định
Logging Chọn xuất log: CPU, APU, SA1 hay Sound DSP
Special Tracing Chọn xuất log: DMA, HDMA, VRAM, DSP-1 hay Unknown Register
CPU Trace Options Tùy chọn khi xuất log
Trace Once Không xuất log đối với lệnh đã thực hiện một lần rồi
Split Tách file log
Tabbed Output Xuất với phân khu tab
Misc Options Tùy chọn khác
Tilde FF Chọn cho phép Turbo mode với phím "~" hay không
Alt Menu Behavior Không hiển thị thanh Menu của Emu
Auto Usage Map Usage Tự động xuất Map
Usage Maps Usage là một dạng log ghi lại các địa chỉ đã đọc và địa chỉ đã thi hành lệnh
Open Usage Khỏi giải thích
Merge Usage Khỏi giải thích
Save Usage Khỏi giải thích
Gen Offsets Xuất log Usage dưới dạng txt
Hex Editor Gọi Memory Editor. Cần cẩn thị vì đôi khi cuộn xuống địa chỉ bên dưới dễ bị đơ.
Viewing:
ROM: nếu là LoROM thì hiển thị phạm vi từ 80:8000~BF:FFFF, nếu là HiROM thì hiển thị từ C0:0000~FF:FFFF
RAM: thể hiện phạm vi của RAM từ 7E:0000~7F:FFFF
VRAM: thể hiện phạm vi của Video RAM từ 0000~FFFF
ARAM: thể hiện phạm vi của Audio RAM từ 0000~FFFF
Freeze: giữ cố định, không cho cập nhật các giá trị (biến số) trong phạm vi RAM đã chọn.
Save ROM: ghi lại mọi thay đổi sau khi chỉnh sửa vào ROM.
Open TBL : mở file table với định dạng .tbl. Tuy nhiên chỉ hỗ trợ kiểu encoding ascii chứ chưa hỗ trợ tiếng Nhật hay Unicode.

(*1): giả lập Snes9X có tốc độ khung 60 fps (1 frame là 0.016 giây), nhưng Debuger chỉ có tốc độ 30 fps.


IV. Phần thực chiến

1. Tìm cheat code

Hẳn là phần nhiều những người chơi game Snes đều đã một vài lần sử dụng cheat code. Bằng cách nhập một đoạn code vào SNES, người chơi có được những khả năng phi thường như bất tử, bắn không bao giờ hết đạn, chạy nhanh gấp 3 bình thường,...
Cũng có nhiều người căm ghét việc dùng cheat code. Tuy nhiên, để hiểu được cặn kẽ việc hack game thì cần phải hiểu rõ về cheat code. Hai yếu tố này có quan hệ mật thiết với nhau, như giải thích dưới đây.

Dữ liệu game luôn có 2 kiểu: kiểu cố định và kiểu biến động. Kiểu cố định là dữ liệu luôn không đổi, nằm trong ROM và kiểu biến động chính là biến số được lưu và cập nhật thường xuyên trong RAM. Đối với FE4, các dữ liệu như binh chủng, chỉ số ban đầu của nhân vật là kiểu cố định. Tuy nhiên, trong quá trình chơi thì nhân vật sẽ Level up, các chỉ số như HP, sức mạnh, phòng vệ được tăng dần. Đây chính là biến số.
Tất cả các game SNES đều lưu biến số trong RAM từ địa chỉ 7E:0000~7F:FFFF.
Chính vì vậy, ta hay thấy cheat code của game SNES đều có dạng 7EXXYYZZ hoặc 7FXXYYZZ, trong đó 7EXXYY là địa chỉ của biến số, còn ZZ là giá trị của biến.
Chẳng hạn, nhân vật có chỉ số STR là 15 ($0F). Địa chỉ quản lý chỉ số này nằm ở $7E1A00. Tại địa chỉ này, biến số sẽ là 0F. Bằng cách nhập code $7E1A001A thì máy Snes/giả lập Snes sẽ luôn giữ cho biến số tại $7E1A00 có giá trị $1A, tức nhân vật luôn có chỉ số STR là 26.
Như vậy hẳn bạn đã rõ nguyên lý của cheat code.
Làm cách nào để tìm được cheat code cho một game cụ thể? Có rất nhiều trang mạng đăng tải cheat code này. Hoặc bạn cũng có thể tự tìm bằng chính giả lập Snes9X. Càng biết nhiều cheat code tức là bạn càng nắm được nhiều địa chỉ biến số của game. Càng hiểu rõ các địa chỉ này thì việc hack game càng suông sẻ.

Bây giờ thử dùng Snes9X để tìm địa chỉ quản lý chỉ số kinh nghiệm (EXP) của nhân vật Siguld.
Chơi từ đầu, ta thấy Siguld có chỉ số EXP là 0, Level 5, chỉ số STR là 5, MGC là 0, SK là 11,...



Từ cửa sổ Snes9X, chọn Cheat --> Search for New cheats
Tích vào ô =(Equal to), ô Entered Value và gõ "0" (chỉ số EXP hiện tại) vào khung Enter a Value (nhập một giá trị) bên dưới rồi Search. Chương trình tìm được rất nhiều địa chỉ có giá trị "0" tại thời điểm hiện tại, trong số đó có địa chỉ quản lý EXP.



Chọn đánh một tên địch ở gần nhất.



Sau khi đánh chết địch, Siguld nhận được 22 EXP.



Lúc này lại chọn Cheat --> Search for New cheats để vào cửa sổ lúc nãy.
Gõ "22" (giá trị EXP hiện tại, sau khi hạ tên địch) vào ô Entered Value, click vào Search và sẽ được một số ít kết quả hơn lúc nãy. Kết quả lúc này là một loạt địa chỉ có biến số "0" lúc trước, nay đã trở thành "22".



Tiếp tục hạ một tên địch khác. Lúc này Siguld được 44 EXP. Làm tương tự bước tìm kiếm bên trên với giá trị "44" và được kết quả tại $7E2D4D.



Kết quả lần này chỉ có 1 địa chỉ duy nhất. Đây là địa chỉ chứa giá trị từ 0 --> 22 --> 44. Để kiểm chứng, có thể nhập địa chỉ này vào cửa sổ Cheat --> Game Genie, Pro-Action Replay Codes --> Cheat Entry and Editor. Nếu nhập 7E2D4D05 vào ô "Enter Cheat Code" và chỉ số EXP của Siguld luôn là 05, bất chấp sau khi hạ lính địch thì đích thị $7E2D4D chính là địa chỉ quản lý EXP.

Bằng cách dò tìm tương tự, ta có các địa chỉ quản lý các chỉ số khác của nhân vật này như sau.

Max HP: 7E2D40
STR: 7E2D41
MGC: 7E2D42
SK: 7E2D43
SPD: 7E2D44
DEFEND: 7E2D45
MGC DEFEND: 7E2D46
LUCK: 7E2D47
LEVEL: 7E2D49 
EXP: 7E2D4D

Có thể tham khảo thêm các địa chỉ khác tại trang này (click vào)


2. Hack với kiến thức ASM

Như vậy ta đã tìm được cheat code cho các chỉ số của nhân vật. Tuy nhiên, việc sử dụng cheat code có hạn chế là chỉ sử dụng được một giá trị tại một thời điểm, và ngoài việc giữ cố định giá trị thì nó không còn tác dụng gì khác. Nếu muốn sau khi đánh địch, Siguld nhận được XX EXP, hoặc sau khi đánh địch, không tăng EXP mà lại Level up hoặc tăng các chỉ số khác thì cheat code không làm được. Lúc này cần đến kiến thức ASM để giải quyết.

Reset game, chơi lại từ đầu và tích vào ô Logging, bỏ tích ô Trace Once khi bắt đầu đánh địch.



Khi đánh địch xong, bỏ tích ở ô Logging và Debuger sẽ ghi lại log (lịch sử các câu lệnh đã thực thi) vào thư mục Logs. Mở file log bằng Text editor, tìm kiếm "7E:2D4D" (địa chỉ quản lý EXP mà ta đã biết từ trước) trong file log, sẽ tìm thấy kết quả 

$84/AA48 BD 09 00 LDA $0009,x[$7E:2D49] A:2D40 X:2D40 Y:4EB5 P:envmxdizc
$84/AA4B 29 FF 00 AND #$00FF A:0205 X:2D40 Y:4EB5 P:envmxdizc
$84/AA4E C9 1E 00 CMP #$001E A:0005 X:2D40 Y:4EB5 P:envmxdizc
$84/AA51 B0 5A BCS $5A [$AAAD] A:0005 X:2D40 Y:4EB5 P:eNvmxdizc
$84/AA53 E2 20 SEP #$20 A:0005 X:2D40 Y:4EB5 P:eNvmxdizc
$84/AA55 BD 0D 00 LDA $000D,x[$7E:2D4D] A:0005 X:2D40 Y:4EB5 P:eNvMxdizc
$84/AA58 18 CLC A:0000 X:2D40 Y:4EB5 P:envMxdiZc
$84/AA59 6F 74 05 00 ADC $000574[$00:0574] A:0000 X:2D40 Y:4EB5 P:envMxdiZc
$84/AA5D C9 64 CMP #$64 A:0016 X:2D40 Y:4EB5 P:envMxdizc
$84/AA5F B0 0D BCS $0D [$AA6E] A:0016 X:2D40 Y:4EB5 P:eNvMxdizc
$84/AA61 9D 0D 00 STA $000D,x[$7E:2D4D] A:0016 X:2D40 Y:4EB5 P:eNvMxdizc
$84/AA64 C2 20 REP #$20 A:0016 X:2D40 Y:4EB5 P:eNvMxdizc
$84/AA66 A9 00 00 LDA #$0000 A:0016 X:2D40 Y:4EB5 P:eNvmxdizc
$84/AA69 FA PLX A:0000 X:2D40 Y:4EB5 P:envmxdiZc
$84/AA6A 28 PLP A:0000 X:4EB5 Y:4EB5 P:envmxdizc
$84/AA6B AB PLB A:0000 X:4EB5 Y:4EB5 P:envmxdizc
$84/AA6C 18 CLC A:0000 X:4EB5 Y:4EB5 P:envmxdizc
$84/AA6D 6B RTL A:0000 X:4EB5 Y:4EB5 P:envmxdizc


Dòng đầu tiên
$84/AA48 BD 09 00 LDA $0009,x[$7E:2D49]
cho thấy tại địa chỉ $84:AA48 có câu lệnh LDA $0009, X
Dạng số thập lục của dòng lệnh này là BD 09 00.
Lúc này giá trị tại X đang là 2D40, nên câu lệnh kia có ý nghĩa là tải một byte từ địa chỉ $7E:2D49 vào Accumulator (A). $7E:2D49 chính là địa chỉ quản lý Level của Siguld. Khi bật chức năng Show Hex, chọn xem RAM ở địa chỉ $7E:2D49 thì ta thấy giá trị tại đây là 05, ứng với mức Level của Siguld là 5.



Những dòng tiếp theo

$84/AA4B 29 FF 00 AND #$00FF 
Dòng này cho biết giá trị (Level của Siguld) đã tải vào A sẽ được AND với giá trị $FF. AND là một phép tính logic theo bit. Có thể tham khảo thêm các trang mạng để biết chi tiết.

$84/AA4E C9 1E 00 CMP #$001E 
Tại địa chỉ $84:AA4E, so sánh kết quả giá trị Level của Siguld sau khi AND với 30 ($1E).

$84/AA51 B0 5A BCS $5A [$AAAD]
Tại địa chỉ $84:AA51 cho biết, nếu kết quả của so sánh trên = 30 thì rẻ nhánh sang địa chỉ $84:AAAD. Nếu nhìn vào nội dung ở địa chỉ $84:AAAD thì ta biết đây là nơi thực hiện lệnh phụ, xử lý để Level của nhân vật không được tăng thêm.

$84/AA53 E2 20 SEP #$20 
Set bit 5 của P Flag thành 1, biến sức chứa của A Register thành 8 bit.

$84/AA55 BD 0D 00 LDA $000D,x[$7E:2D4D]
Tải một byte (8 bit) tại $7E:2D4D (địa chỉ quản lý EXP của Siguld) vào A.

$84/AA58 18 CLC 
Xóa Carry Flag. Luôn thực hiện lệnh này trước lệnh ADC.

$84/AA59 6F 74 05 00 ADC $000574[$00:0574] 
Thêm giá trị tại $000574 vào A. $00:0574 là địa chỉ quản lý biến số EXP mà nhân vật nhận được sau khi đánh địch. Số này sẽ biến động phụ thuộc vào quân địch mà bạn đánh. Trùm và lính địch Level càng cao thì số này càng lớn, tức EXP nhận được càng nhiều.

$84/AA5D C9 64 CMP #$64
So sánh kết quả trên với 100 ($64).

$84/AA5F B0 0D BCS $0D [$AA6E] 
Nếu kết quả so sánh trên bằng 100 thì nhảy đến địa chỉ $84:AA6E.

$84/AA61 9D 0D 00 STA $000D,x[$7E:2D4D]
Ghi giá trị trong A vào địa chỉ $7E:2D4D. Đây là địa chỉ quản lý EXP của Siguld.


Như vậy, qua 4 dòng lệnh đầu tiên thì ta hiểu: nếu Level của Siguld = 30 thì nhân vật này sẽ không được tăng EXP sau khi đánh địch. Mức thiết lập Max Level của FE4 là 30. Và tuy hạ cùng một tên địch, nhưng nếu Level của nhân vật càng cao thì EXP nhận được càng thấp.
Nếu EXP của nhân vật = 100 thì sẽ Level up và reset lại EXP = 0.

Nếu muốn sau khi đánh, nhân vật luôn nhận được 13 EXP, bất kể ở Level nào thì chỉ cần sửa dòng

ADC $000574

thành

ADC #$0D là được.

Dùng Lunar Address, chuyển đổi $84:AA59 sang địa chỉ PC, ta được $04AC59. Dùng Stirling để biên tập 6F 74 05 00 tại $04AC59 thành 69 0D EA EA thì dù sau khi đánh địch, Siguld luôn nhận được 13 EXP.




69 chính là mã thập lục của lệnh ADC với hằng số. EA chính là mã thập lục (hex) của lệnh NOP (không làm gì cả). Vì giá trị cũ là 6F 74 05 00 chiếm 4 byte, trong khi giá trị mới chỉ có 2 byte (69 0D) nên 2 byte sau phải được vô hiệu hóa bằng lệnh "không làm gì cả".
Nếu muốn Siguld luôn Level up sau mỗi lần đánh thì thay ADC #$0D (69 0D) thành ADC #$64 (69 64) là được. Lúc này EXP của Siguld luôn ở mức 100 nên chỉ cần sau trận đánh là được Level up.

Nếu muốn sau trận đánh, chỉ số MHP của Siguld trở thành bằng đúng với số EXP nhận được thì chỉ cần thay

STA $000D,X[$7E:2D4D]

thành

STA $0001,X[$7E:2D40]

là được. Lúc này giá trị EXP nhận được sẽ không được ghi vào địa chỉ quản lý EXP nữa mà ghi vào địa chỉ quản lý MHP ($7E:2D40). Cụ thể, tại $04AC61 ($84:AA61), thay 9D 0D 00 thành 9D 00 00 là được.



Tương tự, ta có thể biến số EXP nhận được thành các chỉ số khác. 

Trên đây chỉ là một vài ứng dụng nhỏ với kiến thức ASM. Với ASM, bạn có thể làm mọi điều trong game, những điều mà không một cheat code hay một chương trình nào khác có thể làm được. Bạn hoàn toàn có thể biến nó thành một game hoàn toàn khác với ASM. Bạn có thể khiến số lần sử dụng của vũ khí tăng lên sau mỗi lần đánh, thay vì giảm xuống, hay HP tăng lên khi bị trúng đòn. Những điều phi lý/phi phàm/phi thường này chỉ có thể thực hiện khi đã nắm rõ ASM, cho nên việc chuyển ngôn ngữ của game sang một thứ tiếng nào đó chỉ là chuyện nhỏ khi bạn đã thấu rõ ngôn ngữ lập trình này.

Bài viết của Asm65816 © 2016
Đăng tại http://gokuraku-shujo.blogspot.com/p/game.html