- การคูณ 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 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ARM จริง ๆ จะอ่าน PC+8 ระหว่างที่กำลังรันที่ PC และดีโค้ด PC+4 เพราะมีไปป์ไลน์ ดังนั้นคำสั่งที่เพิ่งเก็บเข้าไปใหม่ไม่ควรมีผลอะไร แต่ถ้าเป็นอีมูเลเตอร์ที่ไม่ได้อีมูเลตไปป์ไลน์ของฮาร์ดแวร์ มันก็จะไปรันคำสั่งนั้น
บทความที่อธิบายละเอียดขึ้น พร้อมเทคนิคขัดขวางการอีมูเลตหลายแบบจากปี 2004: https://mgba.io//2014/12/28/classic-nes/
ผมเดาว่าค่าของรีจิสเตอร์อาจไม่ได้ถูกกำหนดอยู่หลายไซเคิล การเขียนโค้ดแอสเซมบลีที่ปรับแต่งแบบแน่น ๆ สำหรับชิปแบบนี้ให้ความรู้สึกเหมือนเล่นโคลน Zachtronics ที่รสนิยมแย่เป็นพิเศษ น่ากลัวพอสมควร
หลังจากนั้นอีกนาน มีคนพบกรณีขอบอีกแบบที่ตรวจจับไม่ได้ คือคำสั่งสตริงแบบทำซ้ำที่เขียนทับตัวเอง
https://silviocesare.wordpress.com/2009/02/02/anti-debugging...
x86 มีกลไกแบบนั้นอยู่ แต่ผมไม่แน่ใจว่าสุดท้ายในรุ่น 64 บิตถูกเอาออกไปหรือไม่
ไม่ใช่แค่ต้องรู้พฤติกรรมแปลก ๆ ทั้งหมดของฮาร์ดแวร์และซอฟต์แวร์ต้นฉบับเท่านั้น แต่ยังต้องสร้างมันขึ้นมาใหม่ตามนั้นไม่ว่ามันจะประหลาดแค่ไหน เรื่องนี้ยากในตัวเองอยู่แล้ว และยังต้องคำนึงถึงผลกระทบต่อประสิทธิภาพด้วย
การใช้ 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 จากในอินเทอร์พรีเตอร์
ทุกวันนี้ก็ยังอยากลองทำอยู่ แต่ไม่มีเวลา
นอกเหนือจากนั้น คอมเมนต์พี่น้องของ @xcv123 ก็พูดได้ถูกต้องมาก
สักวันหนึ่งถ้าการ โคลน PS2 ด้วย FPGA เป็นไปได้ การหาคำตอบว่าพฤติกรรมนี้เกิดขึ้นได้อย่างไรคงเป็นโปรเจกต์ที่สนุกสำหรับใครบางคน
ไม่มีอะไรรับประกันว่าเวอร์ชัน FPGA ของ PS2 จะไม่ทำบั๊กแบบเดียวกันหรือคล้ายกันไว้
ซอฟต์แวร์ทศนิยมลอยตัวคงช้า แต่วิธีแก้ทั่วไปน่าจะทำตามอีมูเลเตอร์ PS2 ของ PS4 คือกำหนด whitelist ของช่วงโค้ดในแต่ละเกมที่อนุญาตให้ใช้เส้นทางซอฟต์แวร์ทศนิยมลอยตัว
ผมใช้เวลานานเกินไปกว่าจะรู้ว่านี่พูดถึง PlayStation 2 ไม่ใช่ พอร์ต Personal System/2 ที่ใช้ต่อเมาส์กับคีย์บอร์ด
ตัวย่อสามตัวอักษรทำให้หาบริบทได้ยากจริง ๆ เพราะถ้าเอาแค่ตัวย่อไปค้นใน Google บ่อยครั้งผลลัพธ์ส่วนใหญ่แทบไม่เกี่ยวข้องกันเลย