Đọc tại nguồn: http://yugisokubodai.blogspot.com/2017/09/asm1.html (Tham khảo video trên và xem phần hướng dẫn bên dưới) Hướng dẫn lập trình cho máy Snes bằng ngôn ngữ Assembly 65816 Bài 1: chuẩn bị môi trường + format 1.1. Mở đầu Spoiler Trong các ngôn ngữ bậc cao như C, C++ hay đối với các loại CPU cao cấp như x86, bài vỡ lòng luôn là viết dòng chữ "Hello World" ra màn hình.Việc in dòng chữ này ra màn hình rất đơn giản, chỉ gồm 1 đến vài dòng code là có thể thực hiện được. Thực ra thì quá trình in dòng chữ "Hello World" ra ngoài màn hình không hề đơn giản như nhiều người lầm tưởng. Đây là ưu điểm và cũng chính là nhược điểm của ngôn ngữ bậc cao. Nó giúp đơn giản hóa mọi việc phức tạp, nhưng cũng chính vì thế mà người học ở giai đoạn đầu không thể hiểu sâu vào bản chất của vấn đề. Để in được dòng chữ "Hello World" thì máy tính phải trải qua nhiều công đoạn khác nhau, nhưng phần lớn các công đoạn đều do máy tự thực hiện nhờ bộ thư viện có sẵn, nên người học tưởng chừng như nó chỉ thực hiện mỗi một vài lệnh do họ viết ra. Chính vì vậy người học gần như không thể hiểu được những gì mình đang làm trong giai đoạn đầu. Ngược lại, các loại CPU đời cũ như dòng 65816 dùng cho máy Snes của hãng Western Design Center (WDC) sản xuất vào thập niên 1980 không có những tính năng "tự động" như x86 sau này. Người dùng phải tự làm hết mọi công việc và máy (CPU) chỉ làm nhiệm vụ đơn giản là làm theo những chỉ thị (dòng code) của người dùng. Để đạt hiệu quả cao đối với CPU hiệu năng thấp, người ta buộc phải dùng đến ngôn ngữ Assembly, thứ ngôn ngữ gần với ngôn ngữ nhị phân nhất, thứ duy nhất CPU có thể "hiểu" được. Vì vậy mà việc lập trình cho máy Snes, cũng như các hệ máy console đời cũ khác luôn được cho là cực nhọc và khó khăn hơn khi lập trình bằng ngôn ngữ bậc cao như C hay VB cho CPU x86. Nhưng cũng chính vì phải làm mọi việc nên người học dễ dàng hình dung được sâu sắc hơn về cấu trúc, kiến trúc cũng như cách làm việc của CPU đối tượng. Loạt bài này hướng dẫn các kỹ thuật lập trình cho máy SNES (hay còn gọi là SFC ở Nhật) từ những kiến thức căn bản đến nâng cao. Vì những lý do nêu trên, nên bài vỡ lòng không phải là bài viết dòng chữ "Hello World" ra màn hình. Để làm được việc này thì trước đó cần format để máy Snes có thể hoạt động đúng chức năng. 1.2. Những thứ cần chuẩn bị: Spoiler - Phần mềm soạn thảo văn bản bất kỳ. Có thể dùng Notepad có sẵn trong Windows. Khuyên dùng EmEditor hoặc Notepad +. Mọi nội dung code sẽ được gõ vào đây. - Trình biên dịch opcode 65816 thành mã nhị phân để CPU 65816 hiểu được. Có nhiều loại, khuyên dùng xkas. - Phần mềm Hex editor bất kỳ. - Phần mềm Lunar Address để chuyển đổi địa chỉ Snes sang địa chỉ PC. Nếu thông thạo cách chuyển đổi thì có thể bỏ qua phần mềm này. - Phần mềm Tile Editor bất kỳ. Chủ yếu để tạo dữ liệu đồ họa. - Kiến thức căn bản về ngôn ngữ 65816. Người viết tự đọc sách mà có. Tài liệu ngày nay không hề thiếu. - Kiến thức căn bản về phần cứng của máy Snes, chủ yếu là hiểu về các Register, Vram, SPC,.... Tài liệu không hề thiếu. - Phần mềm Debugger bất kỳ. Khuyên dùng Geiger's Snes9x vì tiện lợi. Chủ yếu để thực thi và kiểm tra code, phát hiện chỗ sai. Tất cả những thứ trên đều có thể tìm dễ dàng qua trang world wide web http://wwww.GOOGLE.com 1.3. Chuẩn bị môi trường: Spoiler - Tạo một Folder với tên bất kỳ - Đặt xkas vào Folder này - Tạo file bất kỳ, có thể đổi thành định dạng máy Snes (giả lập/Debugger) đọc được là .smc hoặc .sfc. Có thể để bất cứ định dạng gì. - Tham khảo cách dùng xkas trong file readme. (tạo file .bat có nội dung "xkas.exe -o sample.smc code.asm", khi thực thi thì xkas sẽ biến mọi dòng code trong code.asm thành mã nhị phân vào trong sample.smc) - Tùy vào phiên bản xkas mà cách khai báo trong code.asm có thể khác nhau. Luôn phải khai báo Rom đối tượng là LoRom hay HiRom. Ở đây ví dụ với LoRom. 1.4. Header: Spoiler Nhiều người quen thuộc với Rom Snes cho rằng header là $200 (512) byte đầu tiên của Rom. Thực ra không phải. Đó chỉ là phần header vô thưởng vô phạt mà các công cụ dump (trích xuất) từ Catridge thành file để chạy trên giả lập. Có thể dùng Hex editor xóa đi $200 (512) byte này. Header thực sự của Rom Snes luôn nằm ở một vị trí cố định trong Rom do hãng Nintendō quy định. Khi máy Snes nhận Rom, đầu tiên nó sẽ tìm đến địa chỉ này và đọc thông tin tại đây. Đó là lý do vì sao máy Snes (hay giả lập Snes) cho biết chính xác tên, loại Rom, checksum của từng Rom khác nhau. Header thật sự của Rom nằm ở địa chỉ $FFB0 (tương đương với $7FB0 địa chỉ PC đối với LoRom và $FFB0 địa chỉ PC đối với HiRom). Địa chỉ PC là địa chỉ ta nhận thấy được khi mở Rom bằng Hex Editor trên PC. Thành phần của Header theo thứ tự lần lượt như sau, bắt đầu từ $FFB0: - 2 byte mã sản xuất do Nintendō quy định. Trường hợp là Rom tự viết thì có thể điền 2 số thập lục bất kỳ. - 4 byte mã game. Có thể điền 4 chữ cái bất kỳ. - 7 byte cố định $00. - 1 byte mô tả kích thước Ram mở rộng. Điền $00 nếu Rom chỉ dùng phần Ram sẵn có của máy Snes như phần lớn trường hợp. - 1 byte mô tả phiên bản đặc biệt. Phần này dành cho các mục đích đặc biệt như bảo vệ bản quyền. Thường để $00. Các số $01, $03, $05, $06, $07 tương đương với kích thước Ram mở rộng 16Kbit, 64Kbit, 256Kbit, 512Kbit, 1Mbit. - 1 bye mô tả loại Catridge (Rom) để phân biệt Catridge này với Catridge khác cùng loại. Thường để $00. - 21 byte mô tả tên Rom (game) chẳng hạn Final_Fantasy_VIII_FIN hay FIRE_EMBLEM_FINAL_VER. - 1 byte mô tả Map mode, chính là tốc độ của CPU. $20 cho mode 20, 2.68 MHZ (tốc đồ thường, còn gọi là Slow Rom), $35 cho mode 25 (3.58 MHZ hay còn gọi là HiRom),... - 1 byte mô tả kích thước Rom. $09 cho Rom có kích thước 3 ~4 M Bit, $0A cho kích thước 5~8 M Bit, $0B cho kích thước 9~16 M Bit, $0C cho kích thước 17~32 M Bit và $0D cho kích thước 33~64 M Bit. - 1 byte cho kích thước Sram. $00 không dùng Sram, $01 cho 16K Bit, $03 cho 64K Bit, $05 cho 256K Bit, $06 cho 512K Bit, $07 cho 1M Bit. - 1 byte mô tả thị trường bán của Rom. $00 là Nhật, $01 là Bắc Mỹ, $02 là EU,.... - 1 byte cố định là $33. - 1 byte miêu tả số phiên bản của Rom. - 2 byte kiểm tra bù: chính là phép toán NOT của checksum. - 2 byte checksum. Tổng số byte trong Rom và AND với FFFF. Luôn có 2 byte kiểm tra bù + 2 byte checksum = FFFF. 1.5. Vector: Spoiler Vector là các Pointer chỉ đến các bộ ngắt (Interrupt). Chẳng hạn Vector Reset luôn nhảy đến địa chỉ bắt đầy định dạng Snes khi ta bật nguồn, load Rom hay sau khi nhấn nút Reset. Còn Vector NMI nhảy đến các địa chỉ thực hiện chức năng do người dùng định nghĩa mỗi khi tia laser của màn hình quét từ trên xuống dưới, từ trái qua phải hết 224 dòng (kích thước màn hình Snes chuẩn: 256x224). Mọi vector phải nằm trong địa chỉ $00:FFE0-$00:FFFF (tiếp nối ngay sau phần Header). Mọi vector phải chỉ đến vùng dữ liệu trong bank $00. Máy Snes có 2 loại Vector: Native vector và Emulation vector. Mỗi loại gồm lần lượt: - 4 byte Vector chưa dùng - 2 byte Vector Coprocessor empowerment: dùng với các loại CPU phụ đi kèm. - 2 byte Vector Program break: vector này nhảy tới địa chỉ xử lý do người dùng định nghĩa khi code BRK được thực thi. Vector này dành cho chức năng Debug16 và không mấy khi dùng đến trong thực tế. - 2 byte Vector Abort: vector này hủy bỏ lệnh thực thi, bảo toàn trạng thái như trước khi thực thi. - 2 byte Vector NMI: vector này nhảy đến địa chỉ thực thi chức năng riêng biệt do người dùng định nghĩa vào mỗi kỳ V-blank. Chẳng hạn tại mỗi kỳ V-blank (kết thúc chu kỳ quét của tia laser đi hết chiều dọc màn hình) sẽ kiểm tra nút Pause có nhấn hay không, nếu có thì cho dừng mọi hình ảnh trên màn hình. - 2 byte Vector Reset: vector này nhảy đến địa chỉ thực thi code đầu tiên của Snes khi khởi động hoặc sau khi nhấn nút Reset. Chú ý là Cpu sẽ nhảy đến địa chỉ do Vector Reset ở chế độ Emulation chỉ định. - 2 byte Vector IRQ: vector này nhảy đến địa chỉ thực hiện chức năng do người dùng chỉ định vào mỗi chu kỳ nhất định. Chu kỳ này do người dùng định nghĩa qua các Register từ $4208 ~ $420A. Chu kỳ có thể bắt đầu vào đầu, giữa hay cuối mỗi dòng quét của tia laser trên màn hình, tùy vào giá trị của các Register này. 1.6. Format: Spoiler Các Register của Snes có đặc điểm là có thể ở trạng thái không ổn định (mang giá trị ngẫu nhiên) vào lúc đầu khi bật nguồn định. Điều này có thể dẫn tới nhiều hiện tượng không mong muốn như nhiễu hình, vỡ tiếng. Vì vậy cần phải định dạng các Vector này để tránh tình trạng trên. Quy trình gồm: - Tắt màn hình (ghi giá trị 80/8F vào Register $2100) - Cho các Register về giá trị $00. - Format càng nhiều Register càng tốt, nhưng ít nhất phải bảo đảm từ $2101~$2133 và từ $4200~$420D. Dưới đây là đoạn code đơn giản để format các Register tối thiểu này. Mã: format_routine: SEP #$20 REP #$10 LDA #$80 STA $2100 LDX #$0000 LDA #$00 repeat_format: STA $2101,x INX CPX #$0033 BNE repeat_format LDX #$0000 repeat_format2: STA $4200,x INX CPX #$000D BNE repeat_format2 RTL 1.7. Lặp bất tận: Spoiler Đối với các ngôn ngữ bậc cao, luôn có ký hiệu kết thúc chương trình để máy dừng thực thi mệnh lệnh, trở về trạng thái "thả lỏng". Về thực chất, trạng thái "thả lỏng" này chính là vòng lặp bất tận bằng cách nhảy đến chính nó. Tuy nhiên máy Snes không thể tự nhảy về trạng thái "thả lỏng" nếu không có sự can thiệp của người viết, dẫn đến crash chương trình sau khi thực hiện xong các code bên trên. Vì nó không hiểu cần phải làm gì tiếp theo nên ta cần phải hướng dẫn nó. abc: JMP abc hoặc def: BRA def đều cho kết quả nhảy đến chính nó, khiến máy luôn ở trạng thái hoạt động. Và đây cũng là trạng thái đúng khi kết thúc một chức năng, một chương trình. 1.8. Đổi màu màn hình: Spoiler Đây mới chính là "Hello World" của lập trình Snes. Việc đổi màu được thực hiện qua 2 Register: - $2121: ghi giá trị vào đây một lần. Register này chỉ thứ tự của slot màu của layer Background. - $2122: ghi giá trị vào đây hai lần. Tùy vào giá trị được ghi vào Register này mà màn hình sẽ có màu khác nhau. Ghi $00 hai lần vào $2122 sẽ cho màn hình màu đen, ghi $FF hai lần vào $2122 sẽ cho màn hình màu trắng. Tuy nhiên trước khi có thể thấy màu sắc màn hình thay đổi thì cần phải "bật" nó lên vì màn hình đã bị "tắt" trong quá trình Format. Register quản lý bật tắt màn hình là: - $2100. Các giá trị từ $00 đến $0F sẽ bật màn hình với 15 cấp độ sáng từ tối nhất ($00) đến sáng nhất ($0F). Bài sau sẽ nói kỹ hơn về màu sắc và layer Background.
Tuyệt lắm bạn ơi, mình có ấp ủ đưa những board game mình thích và những ý tưởng mình nhen nhúm bao lâu nay lên game nhưng không biết làm thế nào nữa, hiện đang học ngôn ngữ C/C++ (học IT năm 1) . Không biết bao giờ mới làm game được nữa :(
Cách đơn giản nhất là chép vào flash cart, rồi cắm cái băng đó vào máy Snes mà chơi. Còn dĩ nhiên vẫn có cách xuất ra băng Cartridge nhưng tốn tiền hơn, và không ai làm vậy nếu chỉ để chơi trên console.
Đã chế tạo thành công một chiếc console "N in 1" đầu tiên tại VN, từ cỗ máy HP SFF (small form factor). Với lòng yêu thích những hệ máy cổ điển, cộng với thói quen chơi con sò, nên mình không ưa giả lập game trên pc mà luôn muốn được chơi trên con sò thực sự. Vậy nên mình đã mày mò dựa theo hướng dẫn trên mạng, cộng thêm việc mua linh kiện và tự lắp ráp, sao cho cỗ máy dc tạo ra giống với máy điện tử nhất có thể (nhỏ gọn, xinh xắn). Kích cỡ chỉ ngang một con ps4. Mọi người có thể thấy phía trên là chiếc HP của mình, phía dưới là một PS4, kích cỡ khá tương đồng. Đặc biệt hơn, mình đã thử rủ bạn mình tới chơi nhà, và cố tình giới thiệu chiếc máy HP này là "PlayStation Ultra" do HP trực tiếp sản xuất với sồ lượng có hạn, bạn mình đã không thể nhận ra sự thực là nó chỉ là một chiếc máy tính đuộc cài một hệ điều hành được chỉnh sửa, cứ nghĩ nó là máy điện tử, và đã ra sức search google nhưng không tìm thấy thông tin. Cái nhìn cận cảnh. Với kích thước này, nó không khác một con console là mấy Được xây dựng trên hệ điều hành Lakka (một bản phân phối của Linux) nên máy boot thẳng vào giao diện XMB (tương tự PS3, một loại giao diện dành cho game console), đem đến trải nghiệm y hệt một máy điện tử thứ thiệt. Máy có khả năng reboot, tắt máy qua menu ở home screen. Phần setting chi tiết với các mục âm thanh, hình ảnh, điều chỉnh nút bấm. Người dùng có thể thiết lập cả phông nền cho home screen như trên một chiếc con sò thứ thiệt. Mình gắn 2 tay cầm "no-name" của Tàu, chơi khá tốt những game có 2 người. Những tay cầm PS2, PS3 đương nhiên tốt hơn nhưng mình đã hết tiền. Phần chính là game, đương nhiên rồi. Máy hỗ trợ tận răng NES, SNES nên không cần phải cài đặt bios, chỉ việc chép rom từ máy tính thông qua mạng Lan, và thế là chiến. Do máy có khả năng tự build playlist, nên menu game rất đẹp mắt. Hiện nay trong máy này mình đã cài đặt game của các hệ game xèng, gameboy color, NES, SNES, PS1, mỗi hệ khoảng 30 game, đủ để chơi tới mù mắt Giao diện như PS3 nhé. Mình đã thử cài đặt game cho PSP nhưng chạy rất giật do mình không đủ tiền build cấu hình tốt hơn. Mình cũng đã thử cài Sega game thì tuy chạy dc, nhưng vốn mình lúc nhỏ chẳng đụng gì tới các hệ máy này, nên cũng không hứng thú với game của chúng. Với cách này, ta có thể hồi sinh lại những tựa game cũ, được chơi trên những tay cầm thế hệ mới nhất. Thậm chí có thể tự viết game NES, SNES, copy vào hdd của máy, rồi chơi mà không cần phải build ra băng hay đĩa gì nữa cả.
Thấy nói không ưa giả lập game trên PC, rất hào hứng để đọc tiếp. Tưởng bạn build con Snes thật để chơi... thì hóa ra cũng chỉ là giả lập trên nền Lakka
Chỉ cần Raspberry Pi 3 và cài đặt hệ điều hành Retropie vào Thẻ MicroSD là đc cỗ máy giống của bạn :) thậm chí vô cùng rẻ tiền.... Tầm dưới 2 triệu (đã tính luôn gamepad). Kích thước của máy chỉ nằm gọn trong lòng bàn tay Dựa trên nền tảng đó thậm chí build đc 1 Handheld Console ( Dùng với Raspberry Pi Zero W ) . Cái này thì mắc hơn, tầm khoảng gần 4 triệu. Tên của nó là Free play zero ( Game Pie Advance ) Với Raspberry Pi Zero W thì hơi hạn chế "sức mạnh", do cấu hình của nó hơi thấp. Tui đang chờ phiên bản Freeplay CM3 cho Raspberry Pi CM3. Raspberry Pi CM3 có cấu hình mạnh hơn
Đọc tại nguồn: http://yugisokubodai.blogspot.com/2017/10/asm2.html (Tham khảo video trên và xem phần hướng dẫn bên dưới) Hướng dẫn lập trình cho máy Snes bằng ngôn ngữ Assembly 65816 Bài 2: màu sắc, bối cảnh 2.1. Màu sắc Spoiler Máy Snes có khả năng hiển thị cao nhất là 256 màu, mỗi màu là 15 bit (2 byte). Do vậy, bảng palette màu của Snes chiếm 512 byte cho 256 slot (ngăn, ô chứa) chứa màu. Định dạng màu của Snes là: 0BBBBBGG GGGRRRRR (B: Blue, màu lam; G: Green, màu lục; R:Red, màu đỏ) Bit thứ 15 luôn là 0. Như vậy có thể thấy mỗi màu cơ bản (lam, lục, đỏ) chỉ gồm 5 bit, có giá trị dao động từ thấp nhất (0 hay $00) là 0 đến cao nhất ( 11111 hay $1F) là 31. Màu RGB 24 bit (mỗi màu cơ bản chiếm 8 bit, hay 1 byte) thông thường có giá trị dao động từ 0 (0 hay $00) đến 255 (11111111 hay $FF). Như vậy, để chuyển đổi từ màu RGB 24 bit sang định dạng màu BGR 15 bit của Snes thì áp dụng công thức: R = R / 8 (vd: 17 / 8 = 2) G = G / 8 (vd: 16 / 8 = 2) B = B / 8 (vd: 14 / 8 = 1) ========= Màu Snes = B x 1024 + G x 32 + R Chẳng hạn, đổi màu trắng (255, 255, 255) 24 bit sang định dạng của Snes: R = 255 / 8 = 31 G = 255 / 8 = 31 B = 255 / 8 = 31 ================ Màu Snes = 31 x 1024 + 31 x 32 + 31 = 32767 Tương đương với giá trị $07FF. Đoạn code sau đổi màu nền thành màu trắng. STZ $2121 //slot màu 0, tức màu nền LDA #$FF STA $2122 LDA #$07 STA $2122 2.2. Bối cảnh (Background - BG): Spoiler Một khung hình Snes hiển thị gồm tối đa 4 lớp (Layer), mỗi lớp thể hiện một bối cảnh (Background) và 1 lớp thể hiện sprite. Chẳng hạn như hình ảnh trong game Mario dưới đây gồm: 3 lớp Background + 1 lớp sprite. Gồm Background 1 và Background 2 và Background 3 và cuối cùng là lớp sprite Snes có 7 cách thể hiện các lớp bối cảnh này, hay còn gọi là 7 mode thể hiện. Số lượng lớp Background, số lượng màu cho từng lớp khác nhau tùy thuộc vào mode hiển thị. Cụ thể: Mode # Màu BG 1 2 3 4 ======---=---=---=---= 0 4 4 4 4 1 16 16 4 - 2 16 16 - - 3 256 16 - - 4 256 4 - - 5 16 4 - - 6 16 - - - 7 256 - - - (Mode 0 có thể hiển thị cùng lúc 4 BG + 1 lớp sprite, mỗi BG thể hiện được 4 màu, Mode 1 thể hiện được cùng lúc 3 BG + 1 lớp sprite, trong đó 2 BG hiển thị 16 màu và 1 BG hiển thị 4 màu,...) Mode 7 là mode hiển thị "nổi tiếng" nhất của Snes, với khả năng cho hình ảnh 3D, xoay bối cảnh, phóng to, thu nhỏ... Mode hiển thị được đăng ký qua Register $2105. Đây là Register 8 bit với nội dung như sau: Bit 7 Kích thước Tile BG4 (0=8x8, 1=16x16) Bit 6 Kích thước Tile BG3 (0=8x8, 1=16x16) ; (BgMode5: 8x8 thực chất là 16x8) Bit 5 Kích thước Tile BG2 (0=8x8, 1=16x16) ; (BgMode6: cố định 16x8) Bit 4 Kích thước Tile BG1 (0=8x8, 1=16x16) ; (BgMode7: cố định 8x8) Bit 3 Mức độ ưu tiên của BG3 ở Mode 1 (0=thường, 1=ưu tiên) Bit 2-0 Chế độ BG, cụ thể như bên dưới Mode BG1 BG2 BG3 BG4 0 4 màu 4 màu 4 màu 4 màu 1 16 màu 16 màu 4 màu 2 16 màu 16 màu 3 256 màu 16 màu 4 256 màu 4 màu 5 16 màu 4 màu 6 16 màu 7 256 màu Chẳng hạn, muốn thể hiện màn hình ở Mode 1 với kích thước BG1 là 16x16 pixel cho mỗi Tile thì cần ghi giá trị 00010001 vào $2105 LDA #$11 STA $2105 2.3. Video Ram: Spoiler Video Ram (gọi tắt là Vram) là vùng Ram chứa mọi dữ liệu để hiển thị lên màn hình. Địa chỉ Vram thuộc kiểu Word, tức tính bằng 2 byte một. Vram được truy cập thông qua 2 Register: - $2116, $2117: Register 16 bit này giữ địa chỉ của dữ liệu trong Vram. - $2118, $2119: Register 16 bit này cho phép ghi dữ liệu vào Vram. Chẳng hạn: REP #$30 // đổi sang A, X, Y 16 bit LDX #$0200 STX $2116 LDA #$DEAD STX $2118 Sẽ ghi giá trị $ADDE vào địa chỉ Vram (200 x2) $400. Ngoài ra, cách ghi dữ liệu vào Vram còn được định nghĩa qua giá trị của bit 7, bit 3~bit 0 ở Register $2115. (Bit 6~ bit 4 không sử dụng) Bit 7: - 0: địa chỉ Vram sẽ gia tăng sau khi dữ liệu được ghi vào $2118. - 1: địa chỉ Vram sẽ gia tăng sau khi dữ liệu được ghi vào $2119. Bit 3~bit 0: 0 1 0 0 | gia tăng 8 trong 32 lần 1 0 0 0 | gia tăng 8 trong 64 lần 1 1 0 0 | gia tăng 8 trong 128 lần 0 0 0 0 | gia tăng 1 0 0 0 1 | gia tăng 32 0 0 1 0 | gia tăng 64 0 0 1 1 | gia tăng 128 Chẳng hạn, ta có chuỗi FF-03-2E-1A ghi vào địa chỉ $00 trong Vram: - Nếu bit 7=1, bit 3~bit 0 đều là 0 thì FF và 03 lần lượt được ghi vào $00 và $01, 2E và 1A lần lượt được ghi vào $02 và $03 - Nếu bit 7=1, bit 3~ bit 1 là 0001 thì FF và 03 được ghi vào $00 và $01, đến lượt 2E và 1A được ghi vào địa chỉ cách đó 32 byte. 32 tức $20, nhưng vì địa chỉ Vram là kiểu Word nên phải nhân đôi, tức 2 byte tiếp theo trong chuỗi là 2E và 1A sẽ được ghi vào địa chỉ $40. - Nết bit 7=0, bit 3~1 là 0000 thì đầu tiên, FF được ghi qua $2118 vào $00, sau đó địa chỉ Vram được tăng 1 ($01 x 2=$02) nên 2E, 03 được lần lượt được ghi vào $02, $03. Register $2118 chỉ có thể được truy cập (ghi) trong 2 khoảng thời gian duy nhất: Vblank và Forced blank. Khái niệm này sẽ được giải thích trong phần sau. 2.4. Tileset và Tilemap: Spoiler Có thể hình dung mọi thành phần đồ họa trên màn hình như các mảnh của trò ghép hình. Bản thân "dữ liệu" các mảnh ghép được gọi là Tileset (hay còn gọi là Character data), còn "bản đồ" bố trí vị trí các mảnh này để tạo nên hình thù mong muốn được gọi là Tilemap. Địa chỉ của Tileset của các BG được xác định qua Register $210B và $210C. - Register $210B: bit 7~4: địa chỉ Vram của Tileset của BG2, bit 3~0: địa chỉ Vram của Tileset của BG1 - Register $210C: bit 7~4: địa chỉ Vram của Tileset của BG4, bit 3~0: địa chỉ Vram của Tileset của BG3 Địa chỉ Vram của Tileset được tính bằng cách Left-shift (dời trái) 13 lần. Chẳng hạn: LDA #$03 STA $210B Sẽ cho địa chỉ Tileset của BG1 tại $6000 trong Vram. (03 <<13 = 24756, hay $6000). Địa chỉ byte của nó là $3000. Địa chỉ Tilemap của các BG trong Vram được xác định qua 4 Register từ $2107 ~ $210A (lần lượt từ BG1~BG5) theo định dạng: aaaaaass Trong đó 6 bit aaaaaa xác định địa chỉ, 2 bit ss chỉ kích thước Tilemap (số lượng Tile trong Tilemap). Cụ thể: 00=32x32 01=64x32 10=32x64 11=64x64 Địa chỉ Vram qua 6 bit được tính bằng cách left-shift (dời trái) 11 lần. Chẳng hạn: LDA #$20 STA $2107 Xác định Tilemap của BG1 có các tính chất sau: - $20 = 00100000, có 001000 = 08 << 11 = 16384 = $4000. Tức địa chỉ Tilemap của BG1 này bắt đầu tại $4000 trong Vram, hay $2000 tính theo địa chỉ byte. - $20 = 00100000, có 00 cho kích thước Tilemap là 32x32 Tile. Kích thước của mỗi Tile là bao nhiêu pixel phụ thuộc vào giá trị từ bit 7 ~ bit 4 của Register $2105 như đã nói ở phần trước. 2.5. Cụ thể về Tilemap: Spoiler Như đã đề cập, Tilemap chính là sơ đồ bố trí các mảng (mảnh) đồ họa để tạo nên hình ảnh trên màn hình. Mỗi mảng gồm 16 bit (2 byte) nên vì thế địa chỉ Vram là kiểu Word (2 byte). Nội dung các bit này là vhopppcc cccccccc Trong đó: - Bit v cho phép xoay mảng (mảnh) theo chiều đứng - Bit h cho phép xoay mảng (mảnh) theo chiều ngang - Bit o: mức độ ưu tiên hiển thị của mảng - 3 bit ppp: số thứ tự của slot (ngăn, ô chứa) màu. Tùy thuộc vào BG và Mode hiển thị, như đã đề cập ở phần trên. - 10 bit cc cccccccc còn lại: số thứ tự của mảng Chẳng hạn, nếu mảng có giá trị C002 (1100000000000010) trong Vram (do Snes dùng kiểu Little-endian nên hiển thị là 02C0) thì có các đặc tính: - Xoay ngược chiều ngang và dọc - Không được hiển thị ưu tiên (có thể bị Tile khác đè lên) - Có palette màu như cách tính slot bên dưới - Là Tile số 02 (0000000010) Số thứ tự của palette của Tile thay đổi tùy thuộc vào chế độ hiển thị của BG. Cụ thể: - Mode 0: vị trí của palette được tính bằng công thức ppp*4 + (BG#-1)*32 - Mode 1: vị trí của palette được tính: ppp*n màu - Mode 2: vị trí của palette được tính: ppp*16 - Mode 3: BG1 cố định 256 màu, BG 2 có vị trí của palette được tính: ppp*16 - Mode 4: BG1 cố định 256 màu, BG 2 có vị trí của palette được tính: ppp*4 - Mode 5: vị trí của palette được tính: ppp*n màu - Mode 6: duy nhất BG1 16 màu và vị trí của palette được tính: ppp*n màu - Mode 7: 256 màu. Như vậy, Tile có giá trị C402 (1100010000000010) đối với Mode 1, BG1 sẽ có palette màu bắt đầu từ slot số: 0001 = 1 * 16 = $10. Palette của mảng (Tile) mang giá trị C402 sẽ có 16 màu bắt đầu từ slot $10 đến slot $20. Trong mảng (Tile) đó, pixel nào có màu số mấy phụ thuộc vào giá trị dữ liệu trong Tileset, do người dùng tự quy định.
Học giỏi thuật toán, giải thuật mới quan trọng Ngôn ngữ chỉ là thứ cơ bản thôi Học xong C/C++ thì có thể đọc thêm sách học C# để làm việc với Unity engine => làm game là 1 việc đơn giản nếu giỏi giải thuật Còn cầm C++ thì dùng Engine Unreal 4, Crytek, có điều cộng đồng hỏi đáp sơ sài và làm việc với pointer của C++ thì rất dễ gây crash
Không nhớ thấy ở đâu nhưng bữa bác @asm65816 có post 1 game đi cảnh kiếm thuật của nhật thấy hay mà giờ ko nhớ game hệ máy nào.
Đọc tại nguồn: https://yugisokubodai.blogspot.com/2017/11/asmlap-trinh-snes-bai-3.html (Tham khảo video trên và xem phần hướng dẫn bên dưới) Hướng dẫn lập trình cho máy Snes bằng ngôn ngữ Assembly 65816 Bài 3: Truyền dữ liệu đồ họa bằng DMA 3.1. Khái quát về DMA Spoiler Trong bài trước đã đề cập đến Register cho phép viết dữ liệu vào Vram là $2118. Tuy nhiên cách chuyển dữ liệu đồ họa vào Vram thông qua cổng $2118 có khuyết điểm: - Mỗi lần chỉ ghi được 02 byte (16 bit) - Chỉ có thể ghi trong khi tắt màn hình (Forced blank) hoặc trong thời gian V(ertical) blank Do vậy, đối với những khối đồ họa có dung lượng lớn thì cần nhiều thời gian để cập nhật trong màn hình vì phải viết rất nhiều lần, ảnh hưởng tới hiệu suất của game. Để chuyển khối dữ liệu $400 (1024) byte vào Vram thì phải ghi $200 (512) lần, nếu mỗi lần ghi 02 byte. Để khắc phục nhược điểm này, người ta dùng đến cơ chế DMA để chuyển dữ liệu. DMA là viết tắt chữ "Direct Memory Access" (truy cập bộ nhớ trực tiếp). Đây là chức năng mà rất nhiều loại máy tính có, chứ không riêng gì máy Super Famicom. Chức năng này cho phép đọc dữ liệu trực tiếp từ bộ nhớ (Rom, Ram) thay vì cần CPU đọc, ghi nhiều lần. DMA là một chức năng quan trọng trong lập trình máy SFC (Snes). Khối dữ liệu $400 byte được đề cập bên trên được ghi vào Vram thông qua chức năng này chắc chỉ chiếm 1% của cái tích tắc. 3.2. Vblank: Spoiler Ở trên có đề cập đến Vblank. Vậy đó là gì? Giống như việc ghi qua cổng $2118, chức năng DMA chỉ hoạt động trong khi màn hình được tắt hẳn (Force blank) hoặc trong thời gian Vblank. Hình ảnh ta thấy trên màn hình Tivi thực chất là không liên tục, mà rời rạc. Sở dĩ mắt người thấy hình liền khối, liền mạch là do tính chất lưu ảnh của mắt. Hình ảnh được tạo ra bằng cách quét tia laser lên màn hình, từ trái sang phải, từ trên xuống dưới. Hình ảnh mà ta thấy là được "vẽ" trong thời gian tia laser đang quét. Khi tia laser được quét hết một đường ngang, màn hình sẽ tắt và tia laser chuyển xuống hàng dưới, bắt đầu quét từ trái sang phải và màn hình sáng. Quãng thời gian tắt màn hình này được gọi là Horizontal blank hay Hblank. Quá trình trên lặp lại cho đến khi tia laser quét hết hàng cuối cùng của màn hình. Lúc này màn hình sẽ tắt, tia laser lại nhảy trở về vị trí xuất phát đầu tiên ở góc trên, bên trái. Quãng thời gian tắt màn hình này gọi là Vertical blank hay Vblank. Spoiler Màn hình Snes có độ phân giải tiêu chuẩn là 255x224 điểm ảnh. Có nghĩa là khi quét hết 255 điểm ảnh theo đường ngang thì sẽ phát sinh 1 Hblank, và khi quét hết 224 đường như trên sẽ phát sinh 1 Vblank. Tức cứ 255 Hblank sẽ có 1 Vblank. Vblank là quãng thời gian an toàn để cập nhật hình ảnh lên màn hình, vì lúc này màn hình được tắt nên không có hình ảnh nào hiển thị. Cứ 1 giây có 60 lần Vblank, tần suất quá nhanh nên mắt người không thể nhìn thấy màn hình tắt, mà thấy hình ảnh liên tục. Nếu mỗi 1 Vblank, ta cập nhật 1 hình ảnh thì sẽ có được hình 60 khung hình trong 1 giây. Nói cách khác là về lý thuyết, máy SFC có thể dựng được hình ảnh 60 fps. 3.3. Các Register cho DMA: Spoiler Trước khi có thể sử dụng chức năng DMA, ta phải bật cho phép NMI hoạt động. DMA là bộ ngắt NMI duy nhất của máy SFC. Việc bật/tắt NMI được quản lý ở Register $4200, bit 7. Nếu bit 7 của Register này = 1 thì NMI được cho phép, nếu bit 7 = 0 sẽ vô hiệu hóa NMI. SEP #$20 LDA #$80 STA $4200 Khi 1 được ghi vào bit 7 của $4200 thì bit 7 của Register $4210 sẽ cho giá trị 1 nếu đang trong quãng thời gian Vblank, ngoài ra sẽ cho giá trị 0. Máy SFC có tất cả 8 kênh chuyển DMA, được kích hoạt qua 8 bit của Register $420B. Nếu giá trị 01 được ghi vào $420B sẽ kích hoạt kênh số 0, nếu giá trị 02 được ghi vào đây sẽ kích hoạt kênh số 1,.... Ngoài ra còn có những Register sau xác định điều kiện của DMA: - $43x0: bit 7 thiết lập chế độ đọc từ hay ghi vào Vram (0=ghi, 1=đọc); bit 6 chỉ dùng cho chức năng HDMA, sẽ đề cập trong bài sau; bit 5 không được dùng đến; bit 4~bit 3: tăng dần hoặc giảm dần địa chỉ dữ liệu khi đọc/ghi; bit 2~bit 0: chế độ chuyển. Đối với Vram thì dùng chế độ 01. - $43x1: cổng giao tiếp. Ghi vào Vram qua cổng $2118 nên trong trường hợp này phải ghi giá trị $18 vào $43x1. Đối với trường hợp ghi palette màu (cổng $2122) thì ghi giá trị $22. - $43x2, $43x3: địa chỉ dữ liệu nguồn (đọc từ Rom/Ram) - $43x4: bank của dữ liệu nguồn. - $43x5: kích thước khối dữ liệu. Lưu ý: x chỉ kênh số tương ứng với giá trị được ghi ở $420B. Ngoài ra cũng cần xác định vị trí cần ghi ở Vram thông qua $2116 như đã đề cập trong bài trước. Khi giá trị được ghi vào $420B thì DMA sẽ bắt đầu, nếu như đang ở trong quãng thời gian Vblank. Nếu không đang ở trong Vblank thì cần phải đợi. Ví dụ: cần chuyển khối dữ liệu có kích thước $800 byte ở địa chỉ $41:00FF trong Rom vào vị trí $6000 trong Vram. SEP #$20 //Set Register A = 8 bit REP #$10 //Reset Register X,Y= 16 bit LDA #$01 STA $4310 //chế độ ghi vào Vram qua kênh Dma 1 LDA #$18 STA $4311 //ghi cổng $2118 vào kênh Dma 1 LDX #$00FF STX $4312 //ghi địa chỉ dữ liệu vào kênh 1 LDA #$41 STA $4314 //ghi bank dữ liệu vào kênh 1 LDX #$0800 STX $4315 //ghi kích thước dữ liệu vào kênh 1 LDX #$3000 STX $2116 JSR đợi_vblank LDA #$02 STA $420B //chuyển DMA bằng kênh 1 đợi_vblank: LDA $4210 BIT #$80 //kiểm tra bit 7 của $4210 BEQ đợi_vblank //nếu bit 7 =0 (không đang trong vblank) thì lặp lại quá trình trên RTS
Đọc tại nguồn: http://yugisokubodai.blogspot.com/2017/11/asm4.html (Tham khảo video trên và xem phần hướng dẫn bên dưới) Hướng dẫn lập trình cho máy Snes bằng ngôn ngữ Assembly 65816 Bài 4: Hello World 4.1. Khái quát Hello World Spoiler Hầu hết những bài đầu tiên trong các chương trình, sách dạy ngôn ngữ lập trình đều là bài hiển thị dòng chữ "Hello World" ra màn hình. Vì vậy mà cụm từ này còn mang nghĩa là "vở lòng", "căn bản". Cụm từ này được cho là có xuất sứ từ cuốn sách "A Tutorial to the Language B" của Brian Wilson Kernighan, được xuất bản từ năm 1973. Tuy là bài mở đầu, vở lòng trong nhiều ngôn ngữ lập trình, nhưng viết một dòng chữ ra màn hình máy Snes không phải là bài đơn giản. Để hiển thị được một chữ cái ra màn hình Snes thì phải nắm được những kiến thức cơ bản về Register, chức năng của chúng và nắm được nhiều opcode của ngôn ngữ 65816. Chính vì vậy mà bài viết "Hello World" không thể xuất hiện đầu tiên trong loạt bài "Lập trình Snes" này. Tuy nhiên "Hello World" cũng không phải quá khó. Nó là ứng dụng kiến thức từ các bài trước, hiển thị bối cảnh (Background) ra màn hình. 4.2. Nhiều cách viết chữ Spoiler Về cơ bản, có 2 cách chính để hiển thị chữ ra màn hình là dùng BG hoặc dùng sprite. Tuy nhiên số lượng sprite tối đa được phép hiển thị trên màn hình khá ít (128) nên người ta thường dùng BG để viết chữ ra màn hình. Đối với cách dùng BG thì cũng có nhiều cách khác nhau. Thông thường, BG3 thường được chọn để viết chữ vì nó có chế độ hiển thị ưu tiên, cho phép đè lên trên các BG khác. Nhiều game hiển thị tất cả chữ trong khung hội thoại ra màn hình cùng một lúc. Những ví dụ cho kiểu viết chữ này là Super Mario, dòng Final Fanatsy, Super Robot Taisen,.... Một số khác viết từng câu ra màn hình. Câu dưới được viết tiếp hàng bên dưới sau khi câu trên được viết một khoảng thời gian. Ví dụ cho trường hợp này là Der Langrisser. Một số game khác viết từng chữ ra màn hình, lần lượt từ chữ này đến chữ khác, chẳng hạn như dòng Fire Emblem. 4.3. Cách viết tất cả chữ ra màn hình cùng lúc Spoiler Đối với những kiểu viết chữ khác nhau thì cách thức cũng khác nhau. Video bài này đề cập đến cách viết tất cả các chữ ra màn hình cùng lúc. Nguyên tắc của cách viết này là: - Chuyển dữ liệu đồ họa của bảng chữ cái (Font) vào Vram. - Ghi số thứ tự tương ứng của chữ trong bảng vào Tilemap. Chẳng hạn, nếu bảng chữ cái có thứ tự là A, B, C, D, E, F,... thì để hiển thị từ "DEADBEEF" thì cần ghi các giá trị 03, 04, 00, 03, 01, 04, 04, 05 vào Tilemap. Mọi thứ luôn được đếm từ số 0, không phải từ số 1. Có thể ghi vào Tilemap trực tiếp qua Register $2118 hoặc ghi vào Ram, sau đó chuyển vùng Ram này vào Vram bằng chức năng DMA. Có thể đổi màu chữ, xoay dọc, xoay ngang chữ bằng cách thay đổi byte định nghĩa chữ trong Vram. Có thể xuống hàng, chuyển hàng bằng cách thay đổi vị trí bắt đầu viết (Register $2116) vào Tilemap.