River Raid trên Atari 2600: Khi một cart 4KB được tái tạo bằng JavaScript đến từng byte

Công nghệ05 tháng 10, 2026·6 phút đọc

Một lập trình viên đã viết lại toàn bộ River Raid — tựa game kinh điển trên Atari 2600 — bằng JavaScript, không dùng giả lập mà dịch từng lệnh 6502 một. Kết quả khớp với ROM gốc đến từng byte qua 45.000 khung hình so sánh, hé lộ nhiều chi tiết thú vị về cách các game Atari xưa vận hành.

River Raid trên Atari 2600: Khi một cart 4KB được tái tạo bằng JavaScript đến từng byte

River Raid trên Atari 2600: Khi một cart 4KB được tái tạo bằng JavaScript đến từng byte

Có những tựa game cũ không chỉ để chơi lại, mà để hiểu cách chúng được sinh ra. River Raid trên Atari 2600 là một trong số đó — và mới đây, một dự án đã đưa nó lên trình duyệt theo cách không ai ngờ tới.

Điểm đáng chú ý không nằm ở chuyện chơi được game trên web, mà ở chỗ bản port này không phải giả lập. Toàn bộ cartridge đã được viết lại bằng JavaScript, dịch từng lệnh 6502 một, thao tác trên đúng 128 byte RAM ở đúng địa chỉ như bản gốc.

Giao diện bản port River Raid trên trình duyệtGiao diện bản port River Raid trên trình duyệt

Một cart 4.096 byte chứa cả một dòng sông

Điều khiến River Raid trở thành huyền thoại là dòng sông trong game chưa bao giờ được lưu trữ. Toàn bộ cartridge chỉ có 4.096 byte, gồm cả code lẫn đồ họa, nên không thể nào nhét sẵn một con sông dài vô tận vào đó.

Thay vào đó, mỗi khối 32 dòng của dòng sông được sinh ra ngay trước khi cuộn vào màn hình, từ một bộ sinh số ngẫu nhiên 16 bit. Bộ sinh này luôn khởi động từ cùng một hạt giống — $A814 — trong mọi ván chơi.

Chính vì vậy, dòng sông không bao giờ kết thúc, và nó cũng giống hệt nhau ở mọi lần chơi. Không có hai ván River Raid nào khác nhau về địa hình, chỉ khác ở cách người chơi xử lý chúng.

Ba mươi hai khối tạo thành một đoạn, và mỗi đoạn kết thúc bằng một con đường cùng cây cầu. Đoạn lẻ chạy thẳng; đoạn chẵn uốn lượn và tách ra quanh các hòn đảo.

Càng lên cao, số kẻ địch càng nhiều và số trạm nhiên liệu càng ít — một cách tăng độ khó tinh tế mà không cần thêm bộ nhớ.

Bảng điểm của dòng sông

Mỗi thứ xuất hiện trên sông đều có vai trò và giá trị riêng:

  • Tàu chở dầu (30 điểm): di chuyển ngẫu nhiên rồi tuần tra, quay đầu khi chạm bờ. Nếu khúc sông quá hẹp cho tàu, cartridge tự động đặt một chiếc trực thăng vào đó.
  • Trực thăng (60 điểm): cánh quạt là hai hình dạng hoán đổi nhau mỗi khung hình.
  • Máy bay phản lực (100 điểm): xuất hiện từ đoạn ba, bay xuyên màn hình không dừng và vòng lại ở mép.
  • Trạm nhiên liệu (80 điểm): tiếp nhiên liệu khi bay qua, hoặc cho điểm nếu bắn hạ.
  • Cầu (500 điểm): bắn để mở đoạn tiếp theo. Đâm vào cầu mất một mạng.
  • Nhà (0 điểm): cảnh trí trên bờ và đảo — thứ duy nhất trên sông không đáng một điểm nào.

Đồng hồ nhiên liệu nằm dưới dòng sông, chạy từ E đến F. Nó cạn nhanh hơn vẻ ngoài, mỗi khung hình bay đều tiêu hao, và một tiếng cảnh báo vang lên khi kim xuống dưới mức một phần tư.

Người chơi bắt đầu với ba chiếc phi cơ và được thêm một chiếc mỗi 10.000 điểm, tối đa chín chiếc. Sau mỗi lần rơi, dòng sông tua lại đầu đoạn và diễn ra y hệt — vì cartridge đã lưu các số ngẫu nhiên tại đúng chỗ đó.

Máy bay chiến đấu trong River RaidMáy bay chiến đấu trong River Raid

Không phải giả lập, mà là dịch lại từng lệnh

Đây là phần khiến dự án khác biệt hoàn toàn với mọi bản chơi game Atari trên web.

Mọi thường trình của cartridge đều được viết lại bằng JavaScript, từng lệnh 6502 một, tác động lên cùng 128 byte RAM tại cùng địa chỉ. Để chứng minh điều đó, một lõi 6502 nhỏ chạy song song ROM thật bên cạnh bản port, và mỗi khung hình đều được đối chiếu từng byte.

Kết quả:

  • 0 sai lệch so với cartridge trong 45.000 khung hình chơi được so sánh
  • 300.000 trạng thái của bộ sinh dòng sông được kiểm tra, từ đoạn 1 đến đoạn 48
  • 31.840 pixel của khung hình mở đầu, giống hệt bản chụp từ Stella
  • 4.096 byte cho toàn bộ cartridge, cả code lẫn đồ họa

Con số 0 sai lệch ấy không phải chi tiết kỹ thuật khô khan. Nó có nghĩa là bản port tái tạo được hành vi phần cứng, không chỉ là hình ảnh — bao gồm cả những lỗi và điểm bất thường mà lập trình viên thời đó có lẽ chưa từng nghĩ tới.

Những bí mật chỉ đọc được trong ROM

Quá trình dịch lại đã phơi ra vài điều mà sách hướng dẫn chưa bao giờ nhắc đến:

  • Không có điểm số trong bộ nhớ. Sáu chữ số thực chất là con trỏ tới đồ họa chữ số, và điểm được cộng thẳng vào các con trỏ đó.
  • Va chạm chỉ đến từ chốt phần cứng của chip TIA, đọc một lần mỗi khối sông. Không hề có phép tính va chạm nào.
  • Đâm vào tàu, trực thăng hay máy bay vẫn được tính điểm khi bạn đang rơi.
  • Chạm vào ngôi nhà chạy đúng đoạn code như chạm vào trạm nhiên liệu.
  • Khi một khối mới vừa cuộn vào, va chạm ở sát mép trên bị ghi vào khối thứ bảy vốn không tồn tại, và cờ đó rơi vào byte vị trí của vật thể đầu tiên.

Bản port giữ nguyên tất cả — kể cả những điểm dị thường ấy. Bởi vì đó chính là game.

Vì sao điều này đáng quan tâm

Với người chơi Việt Nam lớn lên cùng những chiếc máy Atari giả lập hoặc các băng game cũ, dự án này là một cách nhìn lại thú vị. Nó cho thấy các lập trình viên thập niên 1980 đã làm được những gì trong giới hạn 4KB — nơi một bức ảnh chụp màn hình ngày nay còn lớn hơn cả toàn bộ tựa game.

Về mặt kỹ thuật, đây cũng là ví dụ điển hình cho sự khác biệt giữa giả lập và tái lập trình. Giả lập mô phỏng phần cứng để chạy phần mềm gốc; còn bản port này chạy lại logic gốc trên nền tảng mới — và chỉ được coi là thành công khi khớp đến từng byte.

Với những ai từng vật lộn với tối ưu hiệu năng hay lập trình nhúng, việc một cart từ năm 1982 vẫn còn đáng để mổ xẻ đến vậy là một lời nhắc nhở: giới hạn khắt khe thường sinh ra những giải pháp đẹp nhất.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗