2 คะแนน โดย GN⁺ 2024-06-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • การคูณ floating-point ของ VU บน PS2 มีความคลาดเคลื่อนของการคำนวณ 1 บิต ทำให้ในบางค่า 1 * X อาจให้ผลไม่เท่ากับ X
  • ตามคู่มือนักพัฒนา VU นั้น X * 1 รับประกันความถูกต้อง แต่ 1 * X ไม่มีการรับประกันแบบเดียวกัน และความต่างนี้จึงกลายเป็นสัญญาณสำหรับ การตรวจจับอีมูเลเตอร์
  • ตัวอย่างใช้ค่า 129.5f ซึ่งค้นพบด้วยการ brute-force จากค่าที่ทำให้เกิดปัญหา เพื่อตรวจสอบความต่างของพฤติกรรมระหว่าง PS2 จริงกับอีมูเลเตอร์
  • การติดตั้งใช้งานมีโครงสร้างเรียบง่าย โดยคูณ 129.5f กับ 1 ในโหมด VU0 macro แล้วตรวจเพียงว่าค่าอินพุตเดิมต่างจากผลลัพธ์หรือไม่
  • ปัจจุบัน PCSX2, Play!, DobieStation และ hps2x64 ยังไม่จำลองพฤติกรรมนี้ และความยากในการตรวจจับถูกประเมินไว้ที่ 1/5

ความคลาดเคลื่อน 1 บิตที่เกิดใน VU multiplication ของ PS2

  • วิธีนี้เป็นหัวข้อที่สองในซีรีส์การตรวจจับอีมูเลเตอร์ PS2 และสามารถใช้ได้กับ VU1, VU0 micro mode และ VU0 macro mode
  • ตัวอย่างนี้ใช้ VU0 macro mode เพื่อให้การติดตั้งใช้งานง่ายขึ้น
    • ใช้ VU0 เสมือนเป็น coprocessor จึงสั่งทำงานได้โดยตรงจาก EE CPU
    • ไม่จำเป็นต้องจัดการโปรแกรม VU แยกต่างหาก
  • ในคู่มือนักพัฒนา VU มีหมายเหตุว่าคำสั่งคูณอย่าง MUL, MULi มี ความคลาดเคลื่อนของการคำนวณ 1 บิต
    • 1 * X อาจให้ผลต่างจากค่าเดิม X
    • หากใช้ VF[fs] เป็นตัวถูกคูณ จะรับประกันความถูกต้องของผลลัพธ์ในรูปแบบ X * 1
  • ยังไม่ทราบแน่ชัดว่าทำไมบิตจึงสูญหาย

ค่าที่ใช้ตรวจจับและวิธีการติดตั้งใช้งาน

  • การตรวจจับความคลาดเคลื่อนนี้ต้องใช้ตัวเลขที่กระตุ้นปัญหา และวิธีค้นหาที่ง่ายที่สุดคือ brute-force
  • ผู้เขียนเคยจัดทำรายการตัวเลข 250 ค่าแรกที่ก่อปัญหาในช่วงห่าง 0.5 และเผยแพร่รายการดังกล่าวไว้ใน gist
  • โค้ดตัวอย่างใช้ 129.5f เป็นตัวเลขเป้าหมายสำหรับการตรวจจับ
    • ตั้งค่า 129.5f ลงใน VF1 ด้วย QMTC2
    • สร้างค่า 1 ใน VF2 ด้วย VADDw
    • คำนวณ VF1 = 1 * 129.5f ด้วย VMUL
    • ดึงผลลัพธ์กลับมายังฝั่ง EE ด้วย QMFC2 แล้วนำไปเทียบกับค่าอินพุต
  • ค่าที่ส่งกลับคือ in[0] != out[0] และหากค่าเดิมต่างจากผลคูณ ก็ถือว่ามีความคลาดเคลื่อนของการคูณ VU อยู่

ผลกระทบต่ออีมูเลเตอร์แต่ละตัว

  • ปัจจุบัน PCSX2, Play!, DobieStation, hps2x64 ยังไม่จำลองพฤติกรรมการคูณ VU ของ PS2 นี้
  • เพราะเพียงแค่คูณตัวเลขหนึ่งค่ากับ 1 แล้วตรวจผลลัพธ์ วิธีตรวจจับนี้จึงมีระดับความยากเพียง 1/5

1 ความคิดเห็น

 
GN⁺ 2024-06-09
ความคิดเห็นจาก Hacker News
  • เคล็ดลับที่ง่ายที่สุดในการตรวจจับการอีมูเลต ARM รุ่นเก่า ซึ่งดูเหมือนจะเคยใช้ในระบบป้องกันการคัดลอกของ Game Boy Advance ด้วย คือการเก็บคำสั่งกับดักไว้ที่ตำแหน่ง PC+4 หรือก็คือคำสั่งถัดไปทันที
    ARM จริง ๆ จะอ่าน PC+8 ระหว่างที่กำลังรันที่ PC และดีโค้ด PC+4 เพราะมีไปป์ไลน์ ดังนั้นคำสั่งที่เพิ่งเก็บเข้าไปใหม่ไม่ควรมีผลอะไร แต่ถ้าเป็นอีมูเลเตอร์ที่ไม่ได้อีมูเลตไปป์ไลน์ของฮาร์ดแวร์ มันก็จะไปรันคำสั่งนั้น
    บทความที่อธิบายละเอียดขึ้น พร้อมเทคนิคขัดขวางการอีมูเลตหลายแบบจากปี 2004: https://mgba.io//2014/12/28/classic-nes/
    • ตัวประมวลผลสัญญาณดิจิทัล TI320C40 ของ Texas Instruments มีปัญหาเรื่องไปป์ไลน์ที่แปลกกว่านั้นอีก: มี branch delay slot (https://en.wikipedia.org/wiki/Delay_slot) ที่คำสั่งอย่างน้อยหนึ่งคำสั่งหลังการ branch จะถูกรันก่อนการ branch จริง และยังมี load delay slot ที่ค่าที่เก็บลงรีจิสเตอร์จะเพิ่งปรากฏหลังผ่านไปอีกไม่กี่คำสั่ง
      ผมเดาว่าค่าของรีจิสเตอร์อาจไม่ได้ถูกกำหนดอยู่หลายไซเคิล การเขียนโค้ดแอสเซมบลีที่ปรับแต่งแบบแน่น ๆ สำหรับชิปแบบนี้ให้ความรู้สึกเหมือนเล่นโคลน Zachtronics ที่รสนิยมแย่เป็นพิเศษ น่ากลัวพอสมควร
    • บน x86 ก็มี prefetch queue ที่ทำให้เกิดพฤติกรรมคล้ายกัน แต่หลังจาก Intel ตัดสินใจให้ CPU หลังยุค Pentium ตรวจจับโค้ดที่แก้ไขตัวเอง การแก้คำสั่งที่กำลังจะถูกรันในไม่ช้าจึงมีผลเสมอ
      หลังจากนั้นอีกนาน มีคนพบกรณีขอบอีกแบบที่ตรวจจับไม่ได้ คือคำสั่งสตริงแบบทำซ้ำที่เขียนทับตัวเอง
      https://silviocesare.wordpress.com/2009/02/02/anti-debugging...
    • CPU แบบไปป์ไลน์บางตัวคงความเข้ากันได้กับ โค้ดที่แก้ไขตัวเอง ไว้ ดังนั้นถ้าเขียนทับคำสั่งที่อยู่ในไปป์ไลน์ มันจะตรวจจับและล้างไปป์ไลน์
      x86 มีกลไกแบบนั้นอยู่ แต่ผมไม่แน่ใจว่าสุดท้ายในรุ่น 64 บิตถูกเอาออกไปหรือไม่
    • นี่คือเหตุผลที่รอม Dragon Ball Z: The Legacy of Goku II บางครั้งไม่ทำงานบน VisualBoyAdvance หรือเปล่า?
  • นี่แหละเหตุผลที่การอีมูเลตที่มุ่งความถูกต้อง 100% เป็นเรื่องของ งานช่างฝีมือ ในวงการ
    ไม่ใช่แค่ต้องรู้พฤติกรรมแปลก ๆ ทั้งหมดของฮาร์ดแวร์และซอฟต์แวร์ต้นฉบับเท่านั้น แต่ยังต้องสร้างมันขึ้นมาใหม่ตามนั้นไม่ว่ามันจะประหลาดแค่ไหน เรื่องนี้ยากในตัวเองอยู่แล้ว และยังต้องคำนึงถึงผลกระทบต่อประสิทธิภาพด้วย
    • อีมูเลเตอร์ต้องมองเรื่องความถูกต้องตามความเป็นจริง เมื่ออีมูเลตระบบที่ทันสมัยกว่า โดยทั่วไปเป็นไปไม่ได้ที่จะตั้งเป้าทั้ง ความถูกต้องระดับฮาร์ดแวร์ 100% และประสิทธิภาพที่ใช้งานได้พร้อมกัน จึงต้องยอมรับการประนีประนอมที่แม้ในเชิงเทคนิคจะต่างจากฮาร์ดแวร์จริง แต่แทบไม่มีความแตกต่างที่สังเกตได้จริง
      การใช้ JIT recompiler ไม่สามารถถูกต้องตรงกับฮาร์ดแวร์ต้นฉบับแบบ cycle-accurate ได้อย่างสมบูรณ์ แต่โดยมากก็ไม่เป็นปัญหา เว้นแต่โค้ดเกมจะตั้งใจออกแบบมาให้ทำให้อีมูเลเตอร์พัง
      Dolphin ก็ต้องจัดสมดุลแบบนี้เมื่อเกม Wii เชิงพาณิชย์บางเกมใส่ โค้ดป้องกันอีมูเลเตอร์ ที่ใช้ประโยชน์จากรายละเอียดพฤติกรรมแคชของ CPU Wii จริง ในทางทฤษฎีสามารถอีมูเลตแคชของ CPU จริงเพื่อให้เกมรันได้ลื่น แต่ overhead ด้านประสิทธิภาพน่าจะทำให้ช้าลงระดับ 10 เท่า จนเล่นไม่ได้ จึงเลือกใช้แพตช์เพื่อเลี่ยงแทน
      https://dolphin-emu.org/blog/2017/02/01/dolphin-progress-rep...
  • จะเริ่มเข้ามาทำอีมูเลชันได้อย่างไร? ดูเหมือนเป็นสายเฉพาะทางที่ยากมากแม้ในโลกซอฟต์แวร์
    เหมือนว่าต้องเข้าใจทั้งอิเล็กทรอนิกส์และมนตร์ดำการเขียนโปรแกรมเชิงลึกเลย
    • เหมือนอย่างอื่น ๆ คือเริ่มแบบ ค่อยเป็นค่อยไป ก็ได้
      ตอนเริ่ม และโดยมากแม้แต่ตอนทำเสร็จ ก็ไม่จำเป็นต้องเข้าใจเวทมนตร์ลึกซึ้งอะไร ส่วนใหญ่คือดูสเปกแล้วนำไปใช้งานตามนั้น ต้องมีความสามารถในการจัดโครงสร้างโค้ดไม่ให้เละ แต่ก็มีแพตเทิร์นทั่วไปอยู่ และพอลองทำอีมูเลเตอร์สักหนึ่งหรือสองตัวแล้วจะง่ายขึ้นมาก
      แทบไม่จำเป็นต้องเข้าใจอิเล็กทรอนิกส์เลย สิ่งที่เราอีมูเลตคือพฤติกรรมเท่านั้น ถ้าพบบั๊กในพฤติกรรมของฮาร์ดแวร์ต้นฉบับ ปกติก็ใส่การจัดการพิเศษในอีมูเลเตอร์ได้ ความรู้อิเล็กทรอนิกส์อาจช่วยให้เข้าใจว่าพฤติกรรมแบบนั้นเกิดขึ้นได้อย่างไร แต่ใกล้เคียงกับความสนใจเชิงประวัติศาสตร์มากกว่าประโยชน์เชิงปฏิบัติ
      มันมีความยากเฉพาะตัวอยู่ เวลาเกิดปัญหา ปกติคุณจะต้องดีบักสามอย่างพร้อมกัน คือความเข้าใจเกี่ยวกับฮาร์ดแวร์ การใช้งานอีมูเลเตอร์ และเกมที่กำลังถูกอีมูเลต อาจยากที่จะจำกัดหาสาเหตุที่แท้จริง แต่ก็ยังแนะนำให้ลองทำแบบคร่าว ๆ ดูก่อน มันอาจไม่สวยงาม แต่อีมูเลเตอร์ทุกตัวเต็มไปด้วยการจัดการพิเศษเพื่อทำให้เกมยอดนิยมรันได้ไม่ทางใดก็ทางหนึ่ง ถ้าแฮ็กสกปรกไม่กี่อย่างทำให้เกมรันได้ ก็ทำแบบนั้นไป ไม่จำเป็นต้องใช้งานพฤติกรรมฮาร์ดแวร์ต้นฉบับให้ถูกต้องเป๊ะ แค่ทำให้เกมรันได้ก็พอ
    • เริ่มจากการอ่านเอกสารฮาร์ดแวร์ก็ได้ และไม่จำเป็นต้องเข้าใจเครื่องในระดับ วงจรอิเล็กทรอนิกส์ เพราะนี่ไม่ใช่การจำลองวงจรดิจิทัล จึงไม่จำเป็นต้องซับซ้อนขนาดนั้น
      CPU 8 บิตเป็น state machine ที่เรียบง่าย มีสถานะเพียงไม่กี่ไบต์ หรือก็คือรีจิสเตอร์ แค่อ่านโปรแกรมทีละไบต์ แล้วเลียนแบบสิ่งที่ CPU จะทำหลังจากอ่านไบต์นั้น เป็นการทำงานที่ง่ายมาก เช่น บวกและลบตัวเลข หรืออ่านและเก็บไบต์
      http://www.6502.org/users/obelisk/6502/registers.html
      http://www.6502.org/users/obelisk/6502/instructions.html
      อีมูเลเตอร์ CPU 6502 จะอ่านไบต์ถัดไปไม่กี่ไบต์ของโปรแกรม ตีความไบต์เหล่านั้นเป็นคำสั่ง แล้วรันคำสั่ง ในกระบวนการนี้จะอัปเดตรีจิสเตอร์หรือเคาน์เตอร์บางตัวของ CPU ทำการคำนวณทางคณิตศาสตร์หรือบิต และถ้าจำเป็นก็อ่านหรือเก็บข้อมูล 1 ไบต์จากตำแหน่งหนึ่งไปยังอีกตำแหน่ง ทำขั้นตอนนี้ซ้ำในลูปไม่รู้จบ

นี่คือการจำลอง วงจรดึงคำสั่ง-ถอดรหัส-ประมวลผล
https://en.wikipedia.org/wiki/Instruction_cycle

  • ขึ้นอยู่กับว่าคุณหมายถึงอะไรเมื่อพูดว่าอีมูเลชัน
    สมัยก่อนผมเคยพอร์ต อินเทอร์พรีเตอร์ 6502 จาก UNIX ไปยัง Classic Macintosh เพื่อเล่นไฟล์เพลง SID ความแม่นยำระดับรอบสัญญาณนาฬิกาไม่สำคัญ เพราะแค่ให้รันได้เร็วพอก็พอ
    มันทำงานโดยเรียกโค้ด C จากในอินเทอร์พรีเตอร์
  • นานมาแล้วตอนที่ผมเคยพยายามเริ่มศึกษา คำแนะนำคือให้เริ่มจากของที่เรียบง่ายมากและมีเอกสารดี แล้วค่อยสะสมทักษะจากตรงนั้น
    ทุกวันนี้ก็ยังอยากลองทำอยู่ แต่ไม่มีเวลา
  • ขอโปรโมตนิดหนึ่ง ผมเคยพูดเรื่องนี้ที่ FOSDEM ครั้งก่อน: https://fosdem.org/2024/schedule/event/fosdem-2024-2146-how-...
    นอกเหนือจากนั้น คอมเมนต์พี่น้องของ @xcv123 ก็พูดได้ถูกต้องมาก
  • เป็นตัวอย่างที่น่าสนใจแบบที่ในซอฟต์แวร์อีมูเลชันคงแทบไม่คุ้มจะใส่ใจ ถ้าอีมูเลตบั๊กนั้นด้วยก็น่าจะช้าลงมาก
    สักวันหนึ่งถ้าการ โคลน PS2 ด้วย FPGA เป็นไปได้ การหาคำตอบว่าพฤติกรรมนี้เกิดขึ้นได้อย่างไรคงเป็นโปรเจกต์ที่สนุกสำหรับใครบางคน
    • การทำ FPGA ก็มักสร้างขึ้นจากโค้ดหรือเอกสารของโปรเจกต์ซอฟต์แวร์อีมูเลชันเช่นกัน
      ไม่มีอะไรรับประกันว่าเวอร์ชัน FPGA ของ PS2 จะไม่ทำบั๊กแบบเดียวกันหรือคล้ายกันไว้
    • ขึ้นอยู่กับว่าบั๊กนั้นทำให้เกมพังหรือไม่
    • การแก้บั๊กนี้จะเป็นส่วนหนึ่งของการแก้ บั๊กทศนิยมลอยตัว อื่น ๆ อีกหลายอย่าง โดยเฉพาะปัญหาเรื่องการปัดเศษและการ clamp
      ซอฟต์แวร์ทศนิยมลอยตัวคงช้า แต่วิธีแก้ทั่วไปน่าจะทำตามอีมูเลเตอร์ PS2 ของ PS4 คือกำหนด whitelist ของช่วงโค้ดในแต่ละเกมที่อนุญาตให้ใช้เส้นทางซอฟต์แวร์ทศนิยมลอยตัว
    • ทำไมถึงอยากใช้งาน FPGA ที่ค่อนข้างแพงเพื่ออีมูเลต CPU MIPS เก่า ๆ แย่ ๆ กันนะ? ประเด็นหลักของการอีมูเลตคอนโซลเก่าคือการทำให้เล่นเกมเก่าได้บนคอมพิวเตอร์หรือมือถือ โดยไม่ขึ้นกับฮาร์ดแวร์
  • เห็นชื่อเรื่องแล้วมีใครอีกไหมที่งงว่าทำไมเมาส์หรือคีย์บอร์ดต้องทำคณิตศาสตร์ด้วย?
    ผมใช้เวลานานเกินไปกว่าจะรู้ว่านี่พูดถึง PlayStation 2 ไม่ใช่ พอร์ต Personal System/2 ที่ใช้ต่อเมาส์กับคีย์บอร์ด
    • อันหนึ่งคือ PS2 ส่วนอีกอันคือ PS/2
    • ถึงคนที่กดโหวตลง: สิ่งที่ชัดเจนสำหรับคุณไม่ได้แปลว่าจะชัดเจนสำหรับทุกคน
      ตัวย่อสามตัวอักษรทำให้หาบริบทได้ยากจริง ๆ เพราะถ้าเอาแค่ตัวย่อไปค้นใน Google บ่อยครั้งผลลัพธ์ส่วนใหญ่แทบไม่เกี่ยวข้องกันเลย