Bài 5: lập palette màu


Trong bài trước, ta đã viết một chương trình đơn giản hiển thị màu lên màn hình. Bài này sẽ hướng dẫn cách đưa hình ảnh lên màn hình. Nhưng trước đó cần phải chọn màu palette. Game NES sử dụng 2 palette, 1 cho ảnh nền (BG) và 1 cho ảnh sprite. Mỗi palette là 16 byte, tương ứng với 16 màu sử dụng đồng thời trong số 64 màu mà NES có thể hiển thị.
Dưới đây là 16 màu cho BG và 16 màu cho ảnh sprite.


[​IMG]

Trên Google có nhiều phần mềm cho phép tạo file palette màu cho NES. Quy tắc màu trong palette là 4 màu liên tiếp nhau tạo thành một cụm, chẳng hạn cụm 0-1-2-3, cụm 4-5-6-7, cụm 8-9-10-11 và cụm 12-13-14-15. Trong số này thì các màu 0, 4, 8, 12 là màu ảnh nền.

Đọc palette

Dữ liệu màu palette được đọc vào các địa chỉ từ $3F00 và $3F10 trở đi trong VRAM (PPU). Để đọc vào địa chỉ này thì cần phải ghi địa chỉ này ($3F00) vào Register quản lý địa chỉ trong VRAM ở địa chỉ $2006.
Tuy nhiên Register của NES chỉ có 8 bit, trong khi địa chỉ cần ghi vào ($3F00) lại là 16 bit. Vậy cần phải ghi làm 2 lần như sau

LDA $2002 ; đọc trạng thái của PPU
LDA #$3F ; tải giá trị 3F (byte đầu của $3F10) vào Accumulator
STA $2006 ; ghi giá trị đang chứa trong Accumulator vào $2006
LDA #$10 ; tải giá trị 00 (byte cuối của $3F10) vào Accumulator
STA $2006 ; ghi giá trị đang chứa trong Accumulator vào $2006

Sau đó ta sẽ ghi giá trị palette (1byte) (nạp vào địa chỉ được chỉ định) vào Register quản lý địa chỉ thứ hai trong VRAM ở $2007. Mỗi lần ghi thì đối tượng sẽ tự động di chuyển đến những địa chỉ tiếp theo ($3F11, $3F12, $3F13) nên chỉ cần lặp lại STA cần thiết là được.

LDA #$32 ; nạp code màu xanh vào A
STA $2007 ; ghi giá trị màu xanh từ A vào PPU $3F10
LDA #$14 ; nạp code màu hồng vào A
STA $2007 ; ghi màu hồng vào PPU $3F11
LDA #$2A ; nạp code màu lục vào A
STA $2007 ; ghi màu lục vào PPU $3F12
LDA #$16 ; nạp màu đỏ vào A
STA $2007 ; ghi màu đỏ vào PPU $3F13

Ta có thể ghi lần lượt các giá trị màu vào PPU theo cách trên, nhưng mất nhiều thời gian. Dưới đây là cách thực hiện ngắn gọn hơn.

Truy cập Memory bằng Index Register

X và Y là các Index Register, có thể sử dụng chúng như dưới đây.

; giả định giá trị của X đang là 6
LDA $2002, X ; tải địa chỉ ($2002 + 6) vào A. Tức tải $2008 vào A.

; giả định giá trị của Y đang là 9
LDA $2000, Y ; tải địa chỉ ($2000 + 9) vào A. Tức $2009.

Nhờ chức năng của Index Register này mà ta có thể đọc dữ liệu từ palette lúc nãy theo thứ tự.

pallabel: .incbin "kage.pal" ; include file palette tên kage.pal mà ta đã tạo
LDA pallabel, X ; tải vào A giá trị (pallabel + X)

Trường hợp nếu không dùng phần mềm tạo palette, ta có thể dùng biến .db như sau:

pallabel:
.db $0F, $31, $32, $33, $0F, $35, $36, $37, $0F, $39, $3A, $3B, $0F, $3D, $3E, $0F ; BG
.db $0F, $1C, $15, $14, $0F, $02, $38, $3C, $0F, $1C, $15, $14, $0F, $02, $38, $3C ; sprite

Như vậy, pallabel có thể thay cho file kage.pal.
Rồi sau đó dùng vòng lặp để copy những byte màu này vào PPU. Index Register X được dùng để đếm vòng lặp được bao nhiêu lần rồi, cụ thể như dưới đây.

Code đọc palette

Dưới đây là chương trình tải palette.

; Chỉ định địa chỉ tải palette $3F00 vào Register $2006 trong VRAM
LDA #$3F
STA $2006
LDA #$00
STA $2006

LDX #$00 ; tải giá trị 0 vào X. Vòng lặp bắt đầu từ 0.
loadpal:
LDA pallabel, X ; tải địa chỉ (pallabel + X) vào A
STA $2007 ; ghi giá trị palette vào cổng $2007 trong VRAM
INX ; tăng giá trị của Register X lên 1
CPX #32 ; so sánh X với 32 (thập phân, là tổng số màu cho BG và sprite)
BNE loadpal ; nếu kết quả so sánh trên khác 32 thì sẽ nhảy đến label loadpal, tức lặp lại routine này
; nếu kết quả so sánh trên bằng 32 thì kết thúc

Giải thích: ta có label tên pallabel chứa toàn bộ 32 byte dữ liệu màu, 16 cho BG và 16 cho sprite. Có thể tạo ra file này bằng phần mềm chuyên dụng hay dùng biến .db như đề cập ở trên.
Mục tiêu của ta là ghi lần lượt các giá trị màu vào các địa chỉ $3F00 và từ $3F10 trở về sau. Nhưng đầu tiên cần chỉ định các địa chỉ này qua Register quản lý địa chỉ trong VRAM ở $2006 và $2007. Việc này thực hiện đơn giản bằng LDA và STA. Còn việc tải dữ liệu pallabel vào $3F10, $3F11, $3F12.... được thực hiện bằng vòng lặp dùng X Register và chức năng so sánh.

1 bình luận :

  1. Cảm ơn bạn đã chia sẻ bài viết rất hay và chi tiết
    ..........................
    Huyền Sport
    Võ Thuật
    bong88 l bong88

    ReplyDelete

 
Top