1 điểm bởi GN⁺ 2025-07-02 | 1 bình luận | Chia sẻ qua WhatsApp
  • Một số đoạn thùng xoay trong Donkey Kong Country 2 có lỗi trên trình giả lập SNES cũ ZSNES: thùng vẫn tiếp tục quay ngay cả khi đã nhả phím điều hướng, nguyên nhân là do chưa mô phỏng hành vi open bus
  • Trên SNES thật, khi đọc từ địa chỉ chưa được ánh xạ, hệ thống sẽ đọc lại giá trị cuối cùng còn trên bus dữ liệu, và DKC2 phụ thuộc vào đặc tính này khi đọc $2000/$2001 ở bank $B3
  • Routine gây ra vấn đề XOR hướng trước đó của thùng với hướng mới rồi thực hiện and $2000; trên phần cứng thật, phép đọc open bus 16-bit này trả về 0x2020, dùng để xác định việc đi qua ranh giới hướng
  • Nếu phép đọc open bus trả về 0 như trên ZSNES, kết quả AND sẽ luôn là 0 nên nhánh kết thúc xoay không bao giờ chạy, khiến thùng tiếp tục quay cho tới khi người chơi nhấn hướng ngược lại
  • Lệnh này nhiều khả năng lẽ ra phải là and #$2000; trong một bản revision ROM, nếu đổi opcode tại $33EDAC từ 0x2D sang 0x29 thì game sẽ hoạt động đúng ngay cả khi không có open bus

Hiện tượng thùng xoay không dừng trong ZSNES

  • Donkey Kong Country 2 có một lỗi cũ khiến thùng xoay ở một số màn chơi không hoạt động đúng trên ZSNES
  • Ở hành vi bình thường, người chơi chỉ có thể điều khiển việc xoay khi đang giữ phím trái hoặc phải lúc ở trong thùng
  • Trên ZSNES, chỉ cần nhấn trái hoặc phải một lần là thùng sẽ tiếp tục quay theo hướng đó; nhấn hướng ngược lại thì nó lại quay liên tục theo chiều ngược lại
  • Ở các đoạn phía sau, thùng được đặt phía trên gai hoặc các chướng ngại nguy hiểm, nên lỗi này làm độ khó của màn tăng cao hơn nhiều so với ý đồ của nhà phát triển
  • Có vẻ Anomie đã phát hiện cùng vấn đề này khoảng 20 năm trước và sửa trong Snes9x; khi đó bản sửa không phải là mô phỏng đầy đủ open bus mà là hard-code giá trị tại địa chỉ cụ thể mà game phụ thuộc vào
  • Trong ZSNES, lỗi này cuối cùng vẫn không được sửa, và bản phát hành cuối cùng của dự án là 2007

Open bus của SNES và cơ chế định địa chỉ 65816

  • Trên SNES, ngay cả khi đọc một địa chỉ bộ nhớ sai thì chương trình thường cũng không bị crash
  • Khi đọc từ địa chỉ chưa được ánh xạ, CPU sẽ đọc lại giá trị cuối cùng từng xuất hiện trên bus dữ liệu; đây là hành vi open bus
  • CPU chính của SNES là 65C816, tức 65816, nằm trong gói Ricoh 5A22 S-CPU
  • 65816 là bản mở rộng 16-bit của 6502; trên SNES nó dùng bus địa chỉ 24-bit nhưng phần lớn địa chỉ được tạo thành từ bank 8-bit và offset 16-bit
  • Nhiều lệnh nội bộ dùng địa chỉ 16-bit; instruction fetch liên quan đến program bank register, còn truy cập dữ liệu liên quan đến data bank register
  • Ở trạng thái thùng xoay của DKC2, game đọc $2000 và $2001 trong bank $B3; các địa chỉ này không được ánh xạ ở đâu trong bank $B3 nên trở thành phép đọc open bus

Truy cập bộ nhớ trong routine gây lỗi

  • Khi nhả phím trái hoặc phải trong lúc ở trong thùng xoay, routine chạy mỗi frame sẽ thực hiện phép đọc open bus
  • Trạng thái ban đầu là data bank register bằng $B3, direct page bằng $0000, và cờ M/X đều clear nên thanh ghi và truy cập bộ nhớ đều ở chế độ 16-bit
  • Routine này dùng nhiều địa chỉ trong phạm vi $0000-$2001
    • $0EE6: hướng hiện tại của thùng
    • $0E0A: lượng xoay mỗi frame
    • $0032: vị trí có vẻ là biến tạm
  • Trong các bank $00-$3F và $80-$BF, vùng $0000-$1FFF được ánh xạ tới 8KB đầu tiên của 128KB WRAM của máy
  • $2000-$20FF là vùng chưa được ánh xạ, nên lệnh and $2000 sẽ trở thành phép đọc open bus

Cách 0x2020 được dùng để xác định thời điểm dừng xoay

  • Routine cộng lượng xoay vào hướng hiện tại để tạo ra hướng mới rồi lưu nó vào biến tạm
  • Sau đó nó XOR hướng cũ với hướng mới, áp dụng and $2000 lên kết quả, rồi kiểm tra xem kết quả có bằng 0 hay không
  • Trên phần cứng SNES thật, phép đọc open bus 16-bit của and $2000 luôn trả về 0x2020
    • Mã máy của and $20002D 00 20
    • 65816 dùng bus dữ liệu 8-bit nên thực hiện phép đọc 16-bit bằng hai lần đọc 8-bit
    • Trong trường hợp này, byte được đọc cuối cùng là high byte của địa chỉ, tức 0x20, nên cả hai lần đều trả về 0x20
  • Vì vậy, về mặt chức năng hành vi thực tế tương đương với and #$2020
  • Nếu kết quả AND bằng 0, game sẽ lưu nguyên hướng mới và tiếp tục xoay ở frame tiếp theo
  • Nếu khác 0, game sẽ đặt lượng xoay về 0, cộng 0x1000 vào hướng mới, rồi mask bằng 0xE000 để căn nó về hướng gần nhất là bội số của 0x2000

Vì sao trên ZSNES nó quay mãi

  • Giá trị hướng của thùng có vẻ dùng thang đo trong đó 0x0000 là hướng xuống dưới và 0x4000 là hướng sang trái
  • Thùng được phân tích có lượng xoay theo chiều kim đồng hồ là 0x0300, còn ngược chiều kim đồng hồ là 0xFD00, và cần hơn 85 frame một chút để quay đủ 360 độ
  • Với 60fps, thời gian này ngắn hơn 1,5 giây một chút
  • Trong phép AND với 0x2020, thay đổi thực sự có ý nghĩa là bit 13, tức 0x2000
  • Trong giá trị hướng, thay đổi ở 0x2000 tương ứng với một bước chuyển giữa các hướng cơ bản hoặc chéo
  • Trên phần cứng bình thường, khi nhả phím điều hướng, thùng sẽ tiếp tục quay tới hướng cơ bản hoặc chéo kế tiếp rồi dừng, đồng thời căn chính xác vào hướng đó
  • Nếu phép đọc open bus luôn trả về 0 thì kết quả AND cũng luôn là 0, khiến đoạn mã kết thúc xoay không bao giờ được thực thi

Khả năng là lỗi gõ và kết quả vá ROM

  • and $2000 là lệnh dùng absolute addressing, và về mặt logic nhiều khả năng phải là and #$2000 với immediate addressing
  • Trên phần cứng thật, do open bus trả về 0x2020 nên and $2000 tình cờ vẫn hoạt động đúng như mong muốn
  • Sự trùng hợp này phụ thuộc vào điều kiện là 6 bit thấp của lượng xoay mỗi frame luôn bằng 0, khiến 0x2020 về mặt chức năng tương đương với 0x2000
  • Opcode sai được thực thi tại offset $EDAC của bank $B3; trong bản revision được phân tích, nó được ánh xạ tới $33EDAC trong ROM 4MB của game
  • Nếu đổi byte này từ 0x2D sang 0x29 thì thùng xoay sẽ hoạt động bình thường ngay cả khi phép đọc open bus luôn trả về 0
  • Vị trí chính xác trong ROM có thể khác ở các revision khác của game
  • Trên gần như mọi trình giả lập SNES ngoài ZSNES đời cũ, game đều hoạt động bình thường, nên trường hợp này gần hơn với một phân tích truy vết hành vi phụ thuộc phần cứng hơn là một bản vá mang tính thực dụng

1 bình luận

 
GN⁺ 2025-07-02
Các ý kiến trên Hacker News
  • Tôi đã lãng phí rất nhiều thời gian trong đời vì những lỗi kiểu như khi viết hợp ngữ 6502 lại quên dấu # trước giá trị tức thời, khiến nó truy cập bộ nhớ
    Như trường hợp này, cũng có nhiều khi tình cờ nó vẫn chạy trong một số tình huống. Tệ hơn nữa là phụ thuộc vào RAM chưa được khởi tạo thay vì floating bus; do đặc tính của DRAM, trên máy của tôi hay trong emulator thì lúc nào cũng chạy tốt, nhưng trên máy của người khác dùng chip DRAM khác thì có thể hỏng. Thường thì bạn sẽ phát hiện ra chuyện đó trên máy của sự kiện, 15 phút trước khi trình diễn ở demoparty, khi nó không chạy

    • Tôi thắc mắc liệu có kiến trúc nào dùng CPU 6502 cùng với bộ nhớ động không. Theo kinh nghiệm hạn chế của tôi, có vẻ nền tảng đó luôn dùng RAM tĩnh
    • 6502 là ngôn ngữ hợp ngữ đầu tiên của tôi, và tôi luôn nghĩ tách bạch rằng LDA #2 là “nạp số 2 vào A”, còn LDA 2 là “nạp giá trị ở vị trí bộ nhớ 2 vào A”
    • Trong tình huống như thế này, đưa mã vào LLM thực ra có thể hữu ích. Những lỗi gõ nhầm hay sơ suất kiểu này có tác động lớn nhưng mắt người rất dễ bỏ qua, còn LLM thì tìm những thứ đó khá tốt
  • Open Bus trong tiêu đề được viết hoa, ban đầu tôi bắt đầu đọc vì tưởng đó là tên riêng của một giao thức bus hay tiêu chuẩn cũ nào đó mà mình chưa từng nghe
    Đọc rồi mới thấy nghĩa là bộ giải mã đường địa chỉ không kích hoạt bất kỳ thiết bị bộ nhớ nào tại địa chỉ được chỉ định $2000, nên bus ở trạng thái “mở”, không nối với thứ gì. Khá buồn cười là lỗi quên # của chế độ địa chỉ tức thời chỉ lộ ra khi emulator cũ không xử lý việc đọc bộ nhớ giống phần cứng thật. Nếu sửa bằng cách dùng địa chỉ tức thời thay vì địa chỉ tuyệt đối thì sẽ không đọc bộ nhớ nữa, nên thời gian thực thi cũng sẽ nhanh hơn. Trong khối mã đó có thể nhanh hơn khoảng 2µs, nhưng có lẽ chỉ có ý nghĩa trên bare metal, còn emulator thì nhiều khả năng dù sao cũng không đạt độ chính xác timing hoàn toàn

    • Rare có vài trường hợp game chạy ổn trong kiểm thử, nhưng nhiều năm sau khi lên kiến trúc mới thì bug ẩn mới lộ ra. Không có nghĩa là công ty khác không như vậy; chỉ là trong chủ đề này Rare là cái tên dễ viện dẫn
      Tôi biết Donkey Kong 64 có rò rỉ bộ nhớ, khiến game chết sau 8–9 giờ chơi liên tục, một khoảng thời gian phi thực tế theo tiêu chuẩn lúc đó. Trong quá trình phát triển không bắt được, nhưng nếu dùng save state của emulator và tiếp tục chơi thay vì dùng chức năng lưu trong game thì thời gian đó tích lũy khá nhanh. Tuy nhiên lịch sử chuyện này khá mơ hồ. Một số tài liệu cho rằng việc bán kèm Memory Pak là biện pháp phút chót để che bug, bằng cách đẩy thời gian crash từ 8–9 giờ lên 13–20 giờ, nhưng nghiên cứu gần đây có vẻ nghiêng về khả năng đó chỉ là trùng hợp và Rare hay Nintendo không phát hành game khi đã biết bug đó
    • Một số emulator SNES ở thời điểm này thực tế gần như hoàn hảo cả về timing [0]. Dù vậy, 2µs chắc sẽ không tạo ra khác biệt cảm nhận được trừ những trường hợp ngoại lệ
      [0] https://arstechnica.com/gaming/2021/06/how-snes-emulators-go...
  • Khi làm tính năng RunAhead của RetroArch và kiểm tra thời điểm save state không khớp, tôi từng thấy SNES Puyo Puyo dùng PPU open bus
    Sau khi load state, giá trị đọc từ PPU Open Bus thay đổi, nên log trace thực thi CPU không khớp

  • Không phải lúc nào tôi cũng mắc lỗi kiểu họ 6502, nhưng khi mắc thì thường là lỗi dùng địa chỉ bộ nhớ thay vì giá trị tức thời
    Đây là lỗi rất phổ biến và dễ mắc, và tôi tin rằng chính Chuck Peddle cũng đã rất hối hận về cú pháp thêm # cho giá trị tức thời như #$1234. Tôi đặt cho # hiển thị màu đỏ tươi trong IDE, và điều đó cũng giúp được phần nào. Ngay cả các vị thần hợp ngữ của Rare cũng dính vấn đề này

    • Ngày xưa tôi từng gặp vấn đề tương tự trong chế độ intel_syntax noprefix của GNU assembler
      Với các lệnh có thể nhận cả giá trị tức thời lẫn địa chỉ bộ nhớ, có một mơ hồ cú pháp khiến một hằng số tức thời có tên được tham chiếu phía trước có thể bị diễn giải thành tham chiếu symbol chưa biết. Kết quả là nó được assemble thành một lệnh mang địa chỉ bộ nhớ placeholder, nơi sẽ được điền bằng địa chỉ relocation của symbol khi link, chứ không phải giá trị tức thời như mong đợi. Debug rất khổ
    • Các tập lệnh như ARM trên thực tế khiến loại lỗi đó gần như không thể xảy ra. Khi xử lý bộ nhớ, bạn phải dùng lệnh khác
  • Tôi thích những bài kiểu này. Với mã hợp ngữ, tôi luôn có cảm giác mình chỉ theo được khoảng 60%, nên phần giải thích bằng văn xuôi bên cạnh thật sự rất hữu ích
    Nghe chuyện về các bug trong phần mềm cổ điển mà có thể chẳng ai hiểu, hoặc thậm chí đến giờ vẫn chưa ai nhận ra, cũng rất thú vị

    • Một trong những lý do khiến các hệ thống thời kỳ này hấp dẫn là chúng không có những cơ chế kiểm tra hiện đại mà ngày nay ta gần như mặc định có ở khắp nơi
      Ý tôi là những cơ chế vốn là bắt buộc với hệ thống có thể kết nối mạng, và nay đã rẻ đến mức cũng có thể đưa vào cả các kiến trúc nhúng hoàn toàn biệt lập. Trên NES gốc, nhiều thao tác đọc và ghi thực chất chỉ là toggle điện áp trên một đường dây nào đó, rồi mọi thứ tiếp theo cứ xảy ra theo cách của nó. Hiệu ứng mong muốn đạt được bằng cách toggle điện áp theo cách rất có kiểm soát, khớp chính xác với tín hiệu biểu thị khoảng blanking của CRT. Một số animation trong Super Mario Bros 3 đã toggle bộ multiplex RAM để chọn một trong nhiều bank dữ liệu sprite, khiến phần cứng đồ họa khi lấy sprite sẽ đọc từ một con chip hoàn toàn khác có ngoại hình hơi khác. Vì timing của TV rất quan trọng, phần mềm cho các vùng TV NTSC và PAL cũng phải phát hành riêng; hai hệ này có tần số quét khác nhau, và tần số quét đó chính là clock điều khiển logic render. Đúng là một thời kỳ hoang dã
  • Theo tôi biết, open bus chỉ thấy ở các hệ thống thời kỳ đầu dùng bus đồng bộ đơn giản
    Tôi hiểu rằng hầu hết các hệ thống khác, khi truy cập địa chỉ không tồn tại, sẽ trả về một giá trị hằng toàn 0 hoặc toàn 1. Đó là vì giao thức bus có handshake để master biết rằng không có phản hồi; theo thuật ngữ PCI thì tương ứng với “master abort”

  • Open bus đúng như tên gọi, nghĩa là các đường bus dữ liệu là mạch hở
    CPU đưa lên bus địa chỉ một địa chỉ chưa được ánh xạ hoặc chỉ cho ghi, và vì không có phần cứng nào trên bus phản hồi nên các đường bus không được điều khiển và rơi vào trạng thái lơ lửng. Về danh nghĩa, đây là hành vi không xác định ở mức phần cứng
    Để hiểu thực tế chuyện gì xảy ra, cần nhìn kỹ hơn một chút vào cấu trúc vật lý của bus dữ liệu. Có những dây dẫn dài mang tín hiệu quanh bo mạch chủ và cartridge, được tách khỏi mặt phẳng mass bởi một lớp nền cách điện mỏng. Nó trông giống như một tụ điện, và trên thực tế các kỹ sư mô tả cũng như mô hình hóa nó là điện dung ký sinh. Hiệu ứng này giới hạn tốc độ truyền dữ liệu tối đa của bus nên người ta cố giảm thiểu nó. Nhưng chính vì hiệu ứng này, khi bus không được điều khiển, nó có xu hướng giữ lại mức điện áp được điều khiển gần nhất. Nó hoạt động giống một ô DRAM nhỏ, tạo ra hiệu ứng mà bài viết nói là “đọc open bus trả về giá trị cuối cùng đi qua bus”
    Việc game vô tình phụ thuộc vào hiệu ứng open bus như DKC2 không phải là hiếm. Trên NES, thanh ghi cổng nối tiếp dùng để kết nối tay cầm chỉ điều khiển các bit thấp, còn các bit cao là open bus. Cũng có vài game đọc input tay cầm bằng lệnh LDA $4016 và kỳ vọng giá trị $40 hoặc $41. Ở đây số 4 là giá trị còn sót lại do open bus
    Cũng có các chiến thuật speedrun dựa vào hành vi open bus như một phần của khai thác làm hỏng bộ nhớ hoặc thực thi mã tùy ý. Ví dụ, credit warp trong Super Mario World đưa program counter vào vùng bộ nhớ chưa ánh xạ để nó chạy lang thang một lúc rồi cuối cùng đến RAM, sau đó thực thi payload được tạo bằng cách thao tác vị trí kẻ địch thật tinh vi [1]
    Tuy vậy, ngay cả với hành vi open bus thường có thể dự đoán được cũng có ngoại lệ. Cartridge không chuẩn có thể trả về giá trị mặc định cho bộ nhớ chưa ánh xạ, hoặc có thể chứa điện trở kéo lên/kéo xuống làm ảnh hưởng đến hành vi open bus. Cũng có tương tác thú vị với DMA. SNES hỗ trợ một tính năng gọi là HDMA, cho phép ứng dụng đặt lịch truyền dữ liệu từ CPU sang phần cứng đồ họa với thời điểm chính xác để upload dữ liệu hoặc thay đổi thiết lập giữa khung hình [2]. Việc truyền DMA này tạm dừng CPU để dùng bus cho quá trình truyền, và nếu truyền DMA chen vào giữa lúc thực thi lệnh, tức là sau khi đã đọc địa chỉ đích nhưng trước lần đọc open bus thực sự, nó có thể làm thay đổi hành vi đọc open bus
    Trường hợp biên cực kỳ đặc thù này có ảnh hưởng lớn đến exploit speedrun của Super Metroid [3]. Exploit này gây ra một memcpy vượt phạm vi, nhằm chuyển một khối dữ liệu lớn từ open bus vào RAM. Việc đọc open bus gần như luôn trả về 0, vì byte cuối của lệnh load liên quan là 0. Nhưng trong một số phòng nhất định có nhiều hiệu ứng đồ họa HDMA, có khả năng khá cao là một lần truyền DMA sẽ ảnh hưởng đến một trong các lần đọc đó; khi ấy một byte khác 0 sẽ chen vào vị trí quan trọng, khiến exploit không chạy đúng và bị crash. Vì vậy đã có một cuộc tranh luận nhẹ trong cộng đồng. Một số route và chiến thuật chỉ ổn định trên emulator và firmware không chuẩn. Người chơi dùng phần cứng gốc hoặc emulator rất chính xác có khả năng cao gặp crash, nhưng phần lớn emulator, bao gồm tất cả các bản tái phát hành chính thức của Nintendo, không giả lập trường hợp biên đặc thù trong đó truyền HDMA giữa chừng một lệnh làm thay đổi giá trị đọc open bus
    Ngoài ra, lượt hoàn thành TAS Super Metroid nhanh nhất hiện nay [4] cũng dựa vào tương tác HDMA này. Người ta tìm thấy một tình huống cố thực thi open bus dẫn đến crash, nhưng thông thường không thể điều khiển nó theo cách hữu ích. Bằng cách thao tác kẻ địch trong phòng để ảnh hưởng đến timing của CPU, họ có thể dùng HDMA đưa một lệnh hữu ích lên bus đúng thời điểm. Cuối cùng, họ khiến console thực thi input tay cầm như mã, đạt được thực thi mã tùy ý hoàn toàn
    [1]: https://youtu.be/vAHXK2wut_I
    [2]: https://youtu.be/K7gWmdgXPgk
    [3]: https://youtu.be/CnThmKhtfOs
    [4]: https://tasvideos.org/8214S

    • Đến đoạn nói rằng cần nhìn vào cấu trúc vật lý của bus dữ liệu, tôi lại muốn khen Ben Eater thêm lần nữa
      Nhờ loạt video của anh ấy về việc làm một máy tính breadboard với 6502, tôi mới thực sự hiểu được nội dung bài viết và phần giải thích vấn đề phần cứng. Tất nhiên, đó là mở rộng ví dụ bus cơ bản của anh ấy sang một máy thương mại. Nếu không thì có lẽ tôi gần như chẳng biết gì
      https://eater.net
  • Khi lập trình chip Parallax Propeller cũng có vấn đề phần nào tương tự
    Đáng ra phải dùng JMP #address để nhảy đến vị trí bộ nhớ được chỉ định, nhưng tôi cứ dùng JMP address, tức là nhảy đến địa chỉ đọc được từ vị trí bộ nhớ được chỉ định. Có lẽ vì assembler 6502 vẫn còn nằm trong trí nhớ cơ bắp
    Propeller: JMP #address
    6502: JMP address
    Propeller: JMP address
    6502: JMP (address)
    Tệ nhất là, giống như bài này, code Propeller có bug đôi khi vẫn chạy. Rồi đến một lúc nào đó nó dừng lại, và bạn mất hàng giờ để tìm hiểu vì sao

  • Đồ họa 3D render sẵn bằng SGI của DKC 1 là công nghệ tối tân vào thời đó. Vector Man trên Genesis cũng làm điều tương tự nhưng ít được chú ý hơn

    • Khoảng năm 1995 tôi là một đứa trẻ 11 tuổi, đúng nhóm đối tượng của DKC, và trò chơi đó thật sự gây chấn động
      Tôi không thể tin vào thứ mình đang nhìn thấy. Gần thời điểm phát hành có một cuộn băng video giới thiệu game và cho xem cả hậu trường phát triển; tôi nhớ có lẽ đó là tài liệu quảng bá nhận được sau khi đăng ký từ đâu đó như hộp ngũ cốc. Tôi đã xem cuộn băng đó rất nhiều lần. Tôi không sở hữu DKC, nhưng có thể chơi ở nhà bạn
    • Khi còn nhỏ, đồ họa đó khiến tôi cảm thấy có gì đó giả tạo. Vì ở một mức độ nào đó, tôi có thể cảm nhận được rằng nó không phải là “thật”
      Các tạp chí thời đó nói khá mơ hồ về chủ đề này, thường ngụ ý như thể sức mạnh của SNES đang render nhân vật và những thứ tương tự theo thời gian thực. Trong khi thực tế về bản chất nó gần với hoạt hình flipbook hơn
  • Khi chơi game bằng trình giả lập mà bị kẹt, cuối cùng người ta thường tự hỏi liệu đó có phải là lỗi trình giả lập không
    Nếu là vấn đề cụ thể này thì hẳn tôi đã nghĩ rằng trò chơi vốn được thiết kế như vậy và đơn giản là khó. Không hẳn liên quan trực tiếp, nhưng khi một trò chơi cảm thấy thật sự khó, tôi cũng nghĩ tương tự: “Có phải do độ trễ giả lập không?” Đào sâu vào vấn đề này, cuối cùng tôi đã tự làm một MiSTer FPGA

    • Chrono Trigger cũng từng có chuyện như thế
      Tôi nhớ có một đoạn sau khi bắt con chuột, phải nhập đồng thời bốn phím. Nhưng tín hiệu nhập qua USB chỉ truyền được 3 phím cùng lúc, nên để vượt qua thì phải bấm loạn bốn phím để rốt cuộc tất cả đều được ghi nhận trong một khoảng thời gian rất ngắn. Phải thử nhiều lần và cực kỳ bực bội
    • Tôi chỉ chơi DKC bằng ZSNES, và trước khi đọc bài viết thì hoàn toàn không biết đây là lỗi trình giả lập
      Như đã nói, tôi cứ nghĩ việc căn thời điểm bắn ra từ thùng để tạo đúng góc là thiết kế có chủ đích của game. Biết rằng đó là lỗi khiến tôi thật sự ngạc nhiên
    • Hồi nhỏ tôi chơi Bionic Commando rất nhiều
      Đến đầu những năm 2000, khi chạy lại bằng trình giả lập, nó khó hơn ký ức của tôi rất nhiều. Sau này mới biết có một lỗi giả lập khiến kẻ địch không biến mất dù đã cho nổ tung căn cứ, và Ladd vẫn bị đóng băng. Vì vậy để qua màn thì cần thêm khoảng 2 vạch máu nữa. Tôi từng phá đảo như vậy một lần để xem có làm được không, nhưng không bao giờ làm lại nữa