[PSX]Giải tích FF8

Thảo luận trong 'Thảo luận chung' bắt đầu bởi SPC700, 10/3/24.

  1. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    993
    Trang phân tích mọi khía cạnh kỹ thuật của Final Fantasy VIII (FF8) do Square Soft phát triển cho máy chơi game Sony PlayStation (PSX), được phát hành ra thị trường vào năm 1999. Phần này được biên soạn lại từ những ghi chú cá nhân trong quá trình debug bản PSX sau khi hoàn thiện bản dịch tiếng Việt cho phiên bản FF8 PC vào năm 2014.

    STONE BOAT - FINAL FANTASY VIII (google.com)

    Những phân tích trong trang này dựa trên đối tượng là Rom bản tiếng Nhật. Bản Âu-Mỹ có thể khác đôi chút, nhưng những nét chính thì vẫn giống vậy.

    1. Các kiểu thoại

    Phần thoại (text) xuất hiện trong các phiên bản Final Fantasy, không riêng gì FF8, đều nằm ở ba tình huống với cách gọi tên như bên dưới.

    • Battle: những đoạn text xuất hiện trong trận đấu, chẳng hạn như khi Quistis nhắc Squall nhấn L1 để đánh nổ bằng Gunblade ở đầu Disc 1, trong trận đánh đầu tiên.

    • World Map: những đoạn text xuất hiện khi nhân vật lang thang trên World Map, chẳng hạn như khi gặp event ném viên đá trên mặt hồ.

    • Menu: phần text xuất hiện khi truy cập Menu bằng cách nhấn nút tam giác (mặc định bản Âu-Mỹ là nút tròn), chẳng hạn như text giải thích Item, tên nhân vật,....

    • Field: là các địa điểm mà khi vào đó, góc Camera sẽ cố định và không xảy ra những trận đánh ngẫu nhiên. Balamb Garden, thành phố Timber, thành phố Galbadia, khu di tích Centra,... đều là các Field. FF8 có số lượng lời thoại nhiều kinh khủng, trong đó phần thoại xuất hiện trong các Field chiếm phần lớn. Phần text ở Field là đối tượng được quan tâm nhất trong các bản dịch ngôn ngữ khác.
    [​IMG]

    Khi mở Rom FF8 bằng các phần mềm chuyên đọc CD-Rom như CDMage thì sẽ thấy cả 4 disc của FF8 đều có cấu trúc giống nhau, gồm có 3 file như dưới đây.

    • FF8DISCZZZ.IMG

    • SLPS_018.8ZZZ

    • SYSTEM.CNF
    [​IMG]

    SYSTEM.CNF là file chứa các tham số liên quan tới lệnh khởi động máy PSX, còn SLPS_018.8ZZZ (ZZZ là 0 với Disc1, là 1 với Disc2,....) là file thực thi (exe) của máy PSX, chứa mã máy để thực thi game. Các lệnh chính của game đều được chứa trong file này. File FF8DISCZZZ.IMG (ZZZ là 1 với Disc1, là 2 với Disc2,...) là nơi chứa dữ liệu hình ảnh, các loại texture, dữ liệu video/âm thanh, các loại pointer, dữ liệu hội thoại và một phần lệnh thi hành.

    Dữ liệu text trong Menu và World Map được DMA từ CD-ROM (từ file FF8DISCZZZ.IMG) vào Ram ngay sau khi khởi động máy, nên máy có thể truy cập vào những đoạn text này vào bất cứ lúc nào khi người chơi nhấn nút bật Menu. Còn với dữ liệu text của Field và Battle chỉ được DMA vào Ram khi vào trận đánh hoặc vào Field tương ứng.

    Dữ liệu thoại của Menu, World Map, Battle đều ở dạng không nén, còn dữ liệu thoại của Field thì được nén theo kiểu lzss. Mỗi khối dữ liệu thoại trong Field bao gồm: góc camera trong field đó, các event điều khiển nhân vật qua lại/cử chỉ khi nói chuyện, đường đi trong field, pointer dẫn tới dữ liệu thoại, dữ liệu thoại, font chữ Kanji đặc biệt (không được dùng tới trong bản Âu-Mỹ).

    Mỗi khối thoại trong Field được đặt tên theo từng Field + vị trí trong Field. Những câu thoại được kích hoạt trong cùng một khu vực được xếp chung trong một khối dữ liệu. Chẳng hạn, lời thoại ở các khu vực trên tầng 2 của Balamb Garden, gồm phòng học, hành lang,... được xếp vào các khối dữ liệu có tên: bg2f_1, bg2f_11, bg2f_21, bg2f_4,... Trong đó "bg" là viết tắt của Balamb Garden. Lưu ý là những từ viết tắt này chủ yếu là của tiếng Nhật, ít khi là viết tắt của từ tiếng Anh. Chẳng hạn, các khối thoại xảy ra trong phòng y tế của Balamb Garden gồm có: bghoke_1, bghoke_2, bghoke_3 trong đó "hoke" là viết tắt của "hokenshitsu" (保健室 - phòng y tế) hay những khối thoại xảy ra ở khu vực nhà dân ở Timber gồm có: timin1, timin21, timin22.... Trong đó "ti" là viết tắt của "Timber", còn "min" là viết tắt của "minke" trong tiếng Nhật (民家-nhà dân). Trong một số ít trường hợp thì tên của khối dữ liệu được đặt theo tiếng Anh, chẳng hạn như tipub1 là những đoạn thoại xảy ra trong quán rượu ở Timber.

    [​IMG]

    Có thể dò tìm vị trí của từng khối dữ liệu bằng cách dùng Binary editor (Hex editor) để tìm những từ khóa (định dạng Ascii) như trên đối với file Ram dump, hoặc với FF8DISCZZZ.IMG. Tuy nhiên, đối với file IMG thì khối dữ liệu ở dạng đã nén (lzss) nên đôi khi sẽ không tìm thấy từ khóa cần thiết. Còn với file Ram dump, do ở dạng không nén nên kết quả dò tìm luôn trúng.
     
    king_dragontb and mio2006 like this.
  2. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    993
    2. Font chữ

    Mọi hình ảnh 2D trong game PSX đều ở dạng tim, kể cả font chữ. Tim là một định dạng hình ảnh của Sony, được dùng cho máy PSX, có cấu trúc như được đề cập ở phần bên dưới. Và dĩ nhiên, font chữ trong game cũng ở định dạng tim, độ sâu màu 4bpp. Tất cả các khối thoại trong game, dù là Battle, Menu, World Map hay Field cũng đều dùng chung một bộ font dạng tim có kích cỡ mỗi ký tự là 12 x 12 pixel. Tuy nhiên, một phần cục bộ trong Menu (như phần tên của G.F.) được trình bày bằng font 8 x 8 pixel.

    Cách chắc chắn nhất để tìm được font chữ trong game là dump Vram. Dùng debugger chơi tới đoạn có thoại, rồi dump Vram. Sau đó dùng công cụ Vram Viewer để xem vị trí của bộ font.

    [​IMG]

    Công cụ Vram Viwer cho ta biết địa chỉ của bộ font trong Vram của máy PSX bắt đầu tại $80780. Từ đây có hai cách để xác định được vị trí của nó trong CD-Rom. Thứ nhất là reset về đầu game rồi dùng chức năng trace log của debugger để tìm xem bộ font này được DMA vào Vram từ địa chỉ nào. Cách thứ hai, là cách đơn giản hơn: dùng Binary editor để xem dữ liệu ở địa chỉ $80780 trong file Vram, copy những dữ liệu này rồi tìm kiếm trong CD-Rom (file FF8DISCZZZ.IMG) .

    [​IMG]

    Dù là cách nào đi nữa thì kết quả cũng đều giống nhau. Khối dữ liệu font (tim) bắt đầu tại $BDE4 trong các file FF8DISCZZZ.IMG, và địa chỉ này là cố định cho cả 4 disc.

    Tuy nhiên, khối dữ liệu này không đơn thuần là file tim, nên các phần mềm dò định dạng tim như timviwer hay tim2view sẽ không dò được bộ font này trong Rom. Phần dữ liệu tim này chỉ là một thành phần của file "font.tdw" bắt đầu ở địa chỉ $BD80. Cấu trúc của file này như sau.

    • 00000008: pointer đến dữ liệu độ rộng của ký tự

    • 000001C4: pointer đến dữ liệu tim (font chữ)
    FF8 dùng kiểu font chữ có độ rộng biến thiên (proportional font/variable width font), rất thuận tiện cho việc dịch thuật sang các ngôn ngữ dùng mẫu tự La Tinh. Chỉ cần điều chỉnh các giá trị tại table này là ta có thể tùy chỉnh độ rộng của từng ký tự theo ý muốn.

    [​IMG]

    Phần dữ liệu thật (hình ảnh/tim) của font chữ bắt đầu ở $01C4, tương đương với $B800 + $01C4 = $B9C4 trong file FF8DISCZZZ.IMG. Cấu trúc của file tim gồm các thành phần được sắp xếp theo thứ tự như bên dưới.

    • Header #1: gồm các pointer dẫn tới dữ liệu CLUT (Color Look UP Table, pallet màu của máy PSX)

    • Dữ liệu CLUT

    • Header #2: gồm các pointer dẫn tới dữ liệu tim

    • Dữ liệu tim
    Cụ thể, ở header #1:

    • 00000010: chèn 4 byte, có giá trị 0x10: đây là số ID cố định đánh dấu mở đầu file tim

    • 4 byte cho biết định dạng của tim, có cấu trúc bit:
    "xxxx-xxxx xxxx-xxxx xxxx-xxxx xxxx-abbb"

    Trong đó các bit x là zero, bit a cho biết file tim đó có dùng CLUT hay không. Bit a set là file tim có CLUT, bit a reset là không có CLUT. Các bit bbb có ý nghĩa:

    000: tim có độ sâu màu 4bpp (16 màu)

    001: 8 bpp (64 màu)

    010: 16 bpp (256 màu)

    111: định dạng hỗn hợp

    Như vậy, nếu cụm bit này có giá trị 0x08 thì có nghĩa là file tim này có dùng CLUT, định dạng 8 bpp, nếu giá trị là 0x09 thì là file tim dạng 16 bpp và có dùng CLUT,...

    • 4 byte thể hiện kích thước của khối CLUT + 0x0C byte (vì phần header chiếm mất 12 byte)

    • 2 byte báo tọa độ X của khối CLUT trong Vram

    • 2 byte báo tọa độ Y của khối CLUT trong Vram

    • 2 byte báo số màu trong mỗi CLUT. Giá trị này luôn cố định là 16 (0x10)

    • 2 byte báo số lượng CLUT có trong file tim
    Ta cũng có thể tìm được tọa độ X-Y của khối CLUT bằng công cụ Vram Viwer.

    Liền sau phần header #1 là phần dữ liệu CLUT. Nếu thay đổi những giá trị ở đây thì màu sắc của chữ trong game cũng thay đổi. Ngay sau khi kết thúc phần dữ liệu CLUT là tới phần header #2, gồm những thông tin:

    • 4 byte kích thước của khối tim + 0x0C byte

    • 2 byte tọa độ X của khối tim trong Vram

    • 2 byte tọa độ Y của khối tim trong Vram

    • 2 byte độ rộng của hình ảnh tim (phải nhân với 4 để ra kích thước thật)

    • 2 byte chiều cao của hình ảnh tim
    Phần khai báo tọa độ X-Y ở đây chính là giá trị tọa độ X-Y mà ta có thể xác nhận bằng Vram Viewer. Và ngay sau phần header #2 là phần dữ liệu thật của tim. Bằng cách dùng Binary editor, ta có thể cắt riêng những thông tin trên để tạo thành một file tim riêng biệt, và rồi sau đó dùng những công cụ như timviewer để chuyển đổi font từ định dạng tim sang dạng hình ảnh bmp/png, chỉnh sửa thành mẫu tự tiếng Việt, rồi chuyển lại thành dạng tim, chèn vào Rom.

    [​IMG]
     
    giahuypro thích bài này.
  3. giahuypro

    giahuypro You Must Construct Additional Pylons GVN Veteran

    Tham gia ngày:
    18/6/07
    Bài viết:
    8,640
    Nơi ở:
    Đà Nẵng
    ông anh đỉnh vl
     
  4. SPC700

    SPC700 Legend of Zelda

    Tham gia ngày:
    1/10/20
    Bài viết:
    993
    3. Độ rộng font chữ

    Thường thì các game tiếng Nhật không dùng tới chức năng chỉnh độ rộng cho từng ký tự, mà tất cả ký tự đều có độ rộng cố định. Kiểu font này được gọi là "monospace font" hoặc "fixed-width font". Còn với các văn bản dùng mẫu tự La Tinh thì người ta thường dùng kiểu font có độ rộng biến thiên theo từng ký tự, được gọi là "proportional font" hoặc "variable width font". Với kiểu thứ hai thì độ rộng của chữ "i" hẹp hơn của chữ "a", tạo nên tính thẩm mỹ của văn bản. Thật khó chịu khi gặp văn bản dùng ký tự La Tinh như tiếng Việt, tiếng Anh mà lại dùng kiểu fixed-width. Đối với giới dịch game thì việc chuyển từ kiểu fixed-width của tiếng Nhật sang kiểu variable width là một tiêu chí lớn. Việc này tuy không khó nhưng cũng đòi hỏi một nền tảng kiến thức vững chắc về phần cứng, phần mềm cũng như cách thức hoạt động của bộ font mới có thể làm được.

    [​IMG]
    So sánh tính thẩm mỹ giữa kiểu variable width và kiểu fixed width

    Nhưng may thay, FF8 không dùng font kiểu fixed-width mà dùng kiểu font có độ rộng biến thiên. Dữ liệu độ rộng của từng ký tự được tích hợp luôn trong file tim thể hiện font. Phần dưới đây phân tích cách CPU xử lý dữ liệu độ rộng của từng ký tự ở màn hình đầu tiên của game, ngay sau logo của Square Soft.

    [​IMG]

    Đầu tiên, khối dữ liệu gồm cả độ rộng font chữ và dữ liệu tim của font chữ được DMA từ CD-ROM vào địa chỉ Ram $801B0000, sau đó khối dữ liệu tại địa chỉ này được copy vào địa chỉ Ram $800821F8 qua routine nằm trong file exe (SLPS_018.8ZZZ).

    org $8002C4C8

    LW v0, 0x0000(a2)

    LW v1, 0x0004(a2)

    LW a0, 0x0008(a2)

    LW a1, 0x000C(a2)

    SW v0, 0x0000(a3)

    SW v1, 0x0004(a3)

    SW a0, 0x0008(a3)

    SW a1, 0x000C(a3)

    ADDIU a2, a2, 0x0010

    BNE a2, t0, 0x8002C4C8

    ADDIU a3, a3, 0x0010

    Trong đó Register $a2 chứa địa chỉ nguồn ($801B0000) còn Register $a3 chứa địa chỉ đích ($800821F8), còn t0 là điểm kết thúc của chuỗi dữ liệu. Toàn bộ quá trình đọc text và xử lý độ rộng font chữ được trình bày qua routine bên dưới.

    org $801F1804

    LBU s0, 0x00(s4) //đọc text vào s0

    ADDIU v0, r0, 0x02 //v0=2

    BNE v0, s0, 801F1820

    ADDIU s4, s4, 0x01 //địa chỉ text + 1 byte

    LW s3, 0x18(sp)

    J 0x801F1804 //xử lý code xuống dòng khi giá trị text = 0x02

    ADDIU s5, s5, 0x0D //s5: tổng chiều cao (pixel) của ký tự

    801F1820:

    ADDIU v0, r0, 0x05

    BNE s0, v0, 801F18AC

    SLTI v0, s0, 0x19 //set v0 nếu giá trị text (s0) nhỏ hơn 0x19

    LUI v1, 0xE100 //phần này trở đi biến hình ảnh chữ cái thành con trỏ (giá trị text = 0x05)

    ORI v1, v1, 0x041F


    org $801F18AC

    BNE v0, r0, 801F1920

    SLTI v0, s3, 0x0181 //set v0 nếu tổng độ rộng ký tự trong chuỗi nhỏ hơn 0x0181 pixel

    BEQ v0, r0, 801F1920 //rẽ nhánh tới $801F1920 nếu tổng độ rộng không nhỏ hơn 0x0181 pixel

    SLTI v0, s0, 0x20 //set v0 nếu giá trị text nhỏ hơn 0x20 (control code)

    BNE v0, r0, 801F18CC

    SLL v0, s0, 0x03 //v0 = s0 <<3

    J 0x801F81E4

    ADDIU s0, s0, 0xFFE0 //s0 = s0 -20 do ký tự Kana có giá trị từ 0x20 trở lên

    SUBU v0, v0, s0 //v0 = s0<<3 - s0

    SLL s0, v0, 0x05 //s0 = v0<<5

    LBU v0, 0x00(s4) //đọc byte tiếp theo của control code

    ADDIU s4, s4, 0x01

    ADDU s0, s0, v0

    ADDIU s0, s0, 0xEAE0

    801F81E4:

    SLL v0, s5, 0x10 //v0 = tổng pixel chiều cao <<16

    ANDI v1, s3, 0xFFFF //s3: tổng pixel độ rộng của chuỗi

    ORA v0, v0, v1

    ADDU a0, s2, r0

    ADDU a1, s1, r0

    LW a3, 0x5C(sp)

    ADDU a2, s0, r0 //chuyển giá trị text sang a2

    JAL 801F16D8

    SW v0, 0x10(sp) //ghi tổng pixel vào 0x10(sp)

    ADDU s2, v0, r0

    ADDIU s1, s1, 0x14

    JAL 8002E3EC

    ADDU a0, s0, r0

    J 801F1804 //đọc byte tiếp theo trong chuỗi

    ADDU s3, s3, v0


    org $801F6D8

    ADDU t0, a0, r0

    ADDIU v0, r0, 0x04 //v0=4

    SB v0, 0x03(a1)

    SLL v1, a1, 0x08

    SWL t0, 0x2(a1)

    ADDU t0, v0, r0

    ANDI v0, a2, 0x01 //giá trị text AND với 0x1 để biết đó là Hiragana hay Katakana

    BEQ v0, r0, 801F1700

    ADDIU a0, r0, 0x3812 //lấy hình ảnh Hiragana

    ADDIU a0, r0, 0x3852

    801F1700:

    SRL v1, a3, 0x03

    ANDI a3, a3, 0x07

    ADDU v0, a0, v0

    BEQ v1, r0, 801F1728

    SH v0, 0x0E(a1)

    LUI v0, 0x8008

    LW a3, 0x26B4(v0)

    J 801F1734

    LUI v0, 0x303C

    801F1728:

    LUI v0, 0x8008

    LW a3, 0x26B0(v0)

    LUI v0, 0x30C3

    ORI v0, v0, 0x0C31

    SRA a0, a2, 0x01

    MULT a0, v0

    LUI v0, 0x0C

    ORI v0, v0, 0x0C

    SW v0, 0x10(a1)

    SW a3, 0x04(a1)

    LW v0, 0x10(sp)

    NOP

    SW v0, 0x08(a1)

    SRA v0, a2, 0x1F

    MFHI t1

    SRA v1, t1, 0x02

    SUBU v1, v1, v0

    SLL v0, v1, 0x02

    ADDU v0, v0, v1

    SLL v0, v0, 0x02

    ADDU v0, v0, v1

    SUBU a0, a0, v0

    SLL v1, v1, 0x08

    OR a0, a0, v1

    SLL v0, a0, 0x01

    ADDU v0, a0, v0

    SLL v0, v0, 0x02

    SH v0, 0x0C(a1)

    JR ra

    ADDU v0, t0, r0


    org $8002E3EC

    ANDI v0, a0, 0x400

    BEQ v0, r0, 8002E404

    LUI v0, 0x8008

    ANDI a0, a0, 0x03FF

    J 8002E40C

    ADDIU v1, v0, 0x23BC

    8002E404:

    LUI v0, 0x8008

    ADDIU v1, v0, 0x21F8 //v1= 800821F8, địa chỉ khối dữ liệu độ rộng

    SRA v0, a0, 0x01 //giá trị text chia đôi vì độ rộng 1 ký tự = giá trị 1 nibble trong byte

    ADDU v0, v1, v0 //địa chỉ độ rộng = 800821F8 + giá trị nibble của text

    LBU v1, 0x00(v0) //đọc độ rộng

    ANDI v0, a0, 0x01 //xác định độ rộng là của ký tự Hiragana hay Katakana

    BEQ v0, r0, 8002E428

    NOP

    SRL v1, v1, 0x04

    8002E428:

    JR ra

    ANDI v0, v1, 0x0F //bỏ 1 nibble



    4. Font phụ
    Ngoài bộ font chính có kích cỡ 12 x 12 pixel được dùng trong phần thoại của Field, Battle, World Map và một số phần ở Menu thì FF8 còn sử dụng một bộ font phụ có kích cỡ 8 x 8 pixel trong một số phần của Menu. Tên của G.F. trong Menu kết nối (Junction) là một trong số những phần được thể hiện bằng bộ font phụ này.

    [​IMG]
    [​IMG]

    Bộ font phụ này cũng chính là hình ảnh tim đầu tiên ta tìm được trong CD nếu scan bằng những công cụ như tim2view. Và cũng như bộ font chính, bộ font phụ là kiểu hình ảnh 4 bpp, trong đó gồm 1 bit plane cho kiểu chữ Hiragana và 1 bit plane cho kiểu Katakana.

    [​IMG]

    Bằng cách chỉnh sửa nội dung của bộ font phụ này thành ký tự La Tinh thì ta có thể biến tên G.F. và những phần khác trong Menu thành tiếng Việt/Anh. Nhưng trước hết, có một số yếu tố khác ngoài hình ảnh của font chữ mà ta cần phải quan tâm. Khi CPU đọc chuỗi ký tự trong Menu thì nó xử lý qua các giai đoạn như bên dưới (ví dụ về các chuỗi ký tự "ジャンクション", "はずす", "さいきょう", "アビリティ").

    1) Giai đoạn 1: đọc ID của chuỗi text, lấy địa chỉ text

    org $801F1050

    SLL a1, a1, 0x01 //a1: ID của chuỗi ký tự (00:ジャンクション, 01:はずす,...), ID này dẫn tới pointer

    ADDU v1, a0, a1 //a0: địa chỉ bắt đầu của khối text, gồm cả pointer

    ADDIU v1, v1, 0x02 //Pointer dẫn tới các chuỗi ký tự bắt đầu ở byte #02 trong khối dữ liệu

    LHU v0, 0x00(v1) //đọc giá trị pointer

    NOP

    BNE v0, r0, 801F1070 //nếu pointer khác zero thì thoát khỏi routine này

    ADDU v0, a0, v0 //cộng giá trị pointer vào địa chỉ bắt đầu khối dữ liệu để được địa chỉ bắt đầu của chuỗi text

    ADDIU v0, r0, r0 //v0 = zero

    801F1070:

    JR ra

    NOP


    2) Giai đoạn 2: đọc chuỗi text, căn chỉnh vị trí chuỗi text

    Sau khi thoát khỏi routine trên, CPU sẽ đọc từng ký tự trong chuỗi cho đến khi gặp control code báo kết thúc chuỗi (0x00). Mục đích của phần xử lý này là để đếm xem chuỗi đó có bao nhiêu ký tự, rồi từ đó tính vị trí trình bày chuỗi tiếp theo lên màn hình ở giai đoạn sau đó.

    org $801F61C0

    ADDU a0, v0, r0 //a0 = v0 = địa chỉ bắt đầu của chuỗi text

    ADDU v1, r0, r0 //v1 = zero: bộ đếm ký tự

    801F61C8:

    LBU v0, 0x00(a0) //đọc byte tại địa chỉ text

    NOP

    BEQ v0, r0, 801F61E0 //xử lý kết thúc chuỗi ở $801F61E0

    ADDIU a0, a0, 0x01 //tăng địa chỉ bắt đầu text thêm 1 byte

    J 801F61C8 //lặp lại chu trình đọc text

    ADDIU v1, v1, 0x01 //tăng giá trị của bộ đếm ký tự

    801F61E0:

    SLL v1, v1, 0x03 //giá trị bộ đếm x 8, vì mỗi ký tự chiếm 8 pixel chiều ngang

    ADDIU v1, v1, 0x0C //cộng thêm 12 pixel khoảng cách giữa các chuỗi

    ADDU s2, s2, v1 //s2: số pixel khoảng cách giữa các chuỗi ký tự

    SH s2, 0x00(s0) //ghi số pixel khoảng cách vào địa chỉ ở s0

    ADDIU s0, s0, 0x02 //tăng 2 byte vào địa chỉ s0

    J 801F6198 //xử lý chuỗi ký tự tiếp theo

    ADDIU s3, s3, 0x01

    Từ đoạn xử lý trên thì ta dễ dàng chỉnh sửa được khoảng cách giữa các ký tự trong chuỗi, cũng như khoảng cách giữa các chuỗi ký tự với nhau.

    [​IMG]

    3) Giai đoạn 3: xuất ký tự ra màn hình

    Sau giai đoạn tính toán vị trí text thì CPU sẽ đọc lại chuỗi text lần nữa rồi xử lý những gì được xuất ra màn hình ở bước tiếp theo.

    org $8002C5A8

    LBU v1, 0x00(t3) //t3: địa chỉ bắt đầu của chuỗi text

    NOP

    BEQ v1, r0, 8002C6C0 //nếu ký tự là 00 thì kết thúc đọc chuỗi

    ADDIU t3, t3, 0x01 //tăng địa chỉ text lên 1 byte

    SLTIU v0, v1, 0x19 //set v0 nếu giá trị text nhỏ hơn 0x19

    BNE v0, r0, 8002C5A8 //v1 nhỏ hơn 0x19 thì rẽ đến 8002C5A8

    SLTIU v0, v1, 0x20 //set v0 nếu giá trị text nhỏ hơn 0x20

    BEQ v0, r0, 8002C5E8 //nếu là ký tự thông thường (0x20 trở lên) thì rẽ đến 8002C5E8

    NOP

    ADDIU v1, v1, 0xFFE8 //giá trị control code trừ 0x17

    SLL v0, v1, 0x03 //v0 = v1 <<3

    SUBU v0, v0, v1

    SLL v1, v0, 0x05

    LBU v0, 0x00(t3)

    ADDIU t3, t3, 0x01

    ADDU v1, v1, v0

    8002C5E8:

    ADDIU v1, v1, 0xE0 //v1 = giá trị text + 0xE0

    SLTIU v0, v1, 0x0200 //set v0 nếu v1 nhỏ hơn 512

    BEQ v0, r0, 8002C6B8 //nếu v1 (tọa độ ký tự) lớn hơn 512 pixel thì đến 8002C6B8, còn ký tự Kana thông thường luôn nhỏ hơn 512

    LUI v0, 0x8005

    ADDIU t0, v0, 0x28A8 //t0 = 800528A8: table chứa tọa độ từng ký tự trong Vram

    SLL v0, v1, 0x02 //v0 = tọa độ ký tự x 4

    ADDU v0, v0, t0 //v0 = địa chỉ chứa tọa độ của ký tự tương ứng

    LW v0, 0x04(v0) //v0 = pointer dẫn tới từng ký tự

    ADDU t2, a1, r0 //t2 = a1

    SRL t1, v0, 0x10 //t1 = pointer >>16 để lấy byte thứ #3

    ANDI v0, v0, 0xFFFF //giữ 2 byte cuối trong tọa độ ký tự

    ADDU t0, v0, t0 //t0 = tọa độ trên + 800528A8: địa chỉ table

    SLL v0, s2, 0x07 //v0 = s2 x 128 để đổi CLUT màu

    BLEZ t1, 8002C6B4 // rẽ đến 8002C6B4 nếu tọa độ ký tự >>16 không lớn hơn zero

    ADDIU v0, v0, 0x0002 //CLUT + 2

    SLL t4, v0, 0x10 // t4 = (CLUT + 2) <<16

    ADDIU a1, a1, 0x000A

    LW v0, 0x00(t0) //v0 = tọa độ dấu trọc âm/bán trọc âm và phần Kana

    NOOP

    Qua phần xử lý trên thì ta có thể chỉnh sửa giá trị byte nào chỉ đến ký tự nào trong bảng font phụ bằng cách sửa đổi các giá trị ở table $800528A8.
     
    oneapple and Harry Kane like this.
  5. oneapple

    oneapple Mayor of SimCity GVN Veteran

    Tham gia ngày:
    19/12/05
    Bài viết:
    4,479
    Nơi ở:
    Xưởng JAV
    Đỉnh cao
     

Chia sẻ trang này