การจำลองเป็นพื้นที่ที่มีหลายแง่มุม นี่คือแนวคิดพื้นฐานและส่วนประกอบการทำงาน ฉันจะแบ่งมันออกเป็นชิ้น ๆ แล้วกรอกรายละเอียดผ่านการแก้ไข หลายสิ่งที่ฉันจะอธิบายจะต้องใช้ความรู้เกี่ยวกับการทำงานภายในของโปรเซสเซอร์ - ความรู้การชุมนุมเป็นสิ่งที่จำเป็น หากฉันเข้าใจบางสิ่งเล็กน้อยเกินไปโปรดถามคำถามเพื่อให้ฉันสามารถปรับปรุงคำตอบนี้ได้
แนวคิดพื้นฐาน:
การจำลองทำงานโดยการจัดการพฤติกรรมของโปรเซสเซอร์และส่วนประกอบแต่ละตัว คุณสร้างแต่ละชิ้นส่วนของระบบจากนั้นเชื่อมต่อชิ้นส่วนที่คล้ายกับสายไฟในฮาร์ดแวร์
การจำลองการประมวลผล:
มีสามวิธีในการจัดการการจำลองโปรเซสเซอร์:
- การตีความ
- การรวบรวมซ้ำแบบไดนามิก
- รวบรวมใหม่คงที่
ด้วยพา ธ ทั้งหมดเหล่านี้คุณมีเป้าหมายโดยรวมเหมือนกัน: รันโค้ดบางส่วนเพื่อแก้ไขสถานะโปรเซสเซอร์และโต้ตอบกับ 'ฮาร์ดแวร์' สถานะตัวประมวลผลเป็นการรวมกลุ่มของตัวประมวลผลการลงทะเบียนตัวจัดการขัดจังหวะ ฯลฯ สำหรับเป้าหมายตัวประมวลผลที่กำหนด สำหรับ 6502 คุณจะมีจำนวนของจำนวนเต็ม 8 บิตเป็นตัวแทนของการลงทะเบียน: A
, X
, Y
, P
และS
; คุณต้องมีการPC
ลงทะเบียน16 บิต
ด้วยการตีความคุณจะเริ่มที่IP
(ตัวชี้คำสั่ง - เรียกอีกอย่างว่าตัวPC
นับโปรแกรม) และอ่านคำแนะนำจากหน่วยความจำ รหัสของคุณวิเคราะห์คำสั่งนี้และใช้ข้อมูลนี้เพื่อเปลี่ยนสถานะตัวประมวลผลตามที่ระบุโดยตัวประมวลผลของคุณ ปัญหาหลักของการตีความคือช้ามาก ทุกครั้งที่คุณจัดการกับคำสั่งที่กำหนดคุณจะต้องถอดรหัสและดำเนินการสิ่งที่จำเป็น
ด้วยการคอมไพล์ซ้ำแบบไดนามิกคุณวนซ้ำรหัสเหมือนกับการตีความ แต่แทนที่จะเรียกใช้ opcodes คุณจะสร้างรายการการดำเนินการ เมื่อคุณไปถึงคำสั่งสาขาแล้วคุณจะต้องรวบรวมรายการการดำเนินการนี้ไปยังรหัสเครื่องสำหรับแพลตฟอร์มโฮสต์ของคุณจากนั้นคุณแคชรหัสที่คอมไพล์แล้วและเรียกใช้งาน จากนั้นเมื่อคุณกดกลุ่มคำสั่งที่กำหนดอีกครั้งคุณจะต้องรันโค้ดจากแคช (BTW คนส่วนใหญ่ไม่ได้ทำรายการคำแนะนำจริง ๆ แต่รวบรวมพวกเขาเป็นรหัสเครื่องจักรได้ทันที - นี่ทำให้ยากต่อการปรับให้เหมาะสม แต่มันอยู่นอกขอบเขตของคำตอบนี้เว้นแต่มีคนสนใจพอ)
ด้วยการรวบรวมซ้ำแบบคงที่คุณทำเช่นเดียวกับในการรวบรวมซ้ำแบบไดนามิก แต่คุณตามสาขา คุณต้องสร้างกลุ่มของรหัสที่แสดงถึงรหัสทั้งหมดในโปรแกรมซึ่งสามารถดำเนินการได้โดยไม่มีการรบกวนเพิ่มเติม นี่จะเป็นกลไกที่ยอดเยี่ยมหากไม่ได้มีปัญหาดังต่อไปนี้:
- รหัสที่ไม่ได้อยู่ในโปรแกรมที่จะเริ่มต้นด้วย (เช่นบีบอัดเข้ารหัสสร้าง / แก้ไขเมื่อรันไทม์ ฯลฯ ) จะไม่ถูกคอมไพล์ใหม่ดังนั้นจึงจะไม่ทำงาน
- ได้รับการพิสูจน์แล้วว่าการค้นหารหัสทั้งหมดในไบนารีที่กำหนดนั้นเทียบเท่ากับปัญหาการหยุดทำงาน
การรวมกันเหล่านี้เพื่อให้การคอมไพล์ซ้ำแบบคงที่เป็นไปไม่ได้อย่างสมบูรณ์ใน 99% ของกรณี สำหรับข้อมูลเพิ่มเติม Michael Steil ได้ทำการวิจัยที่ยอดเยี่ยมเกี่ยวกับการรวบรวมซ้ำแบบคงที่ซึ่งดีที่สุดที่ฉันเคยเห็น
อีกด้านหนึ่งของการจำลองโปรเซสเซอร์คือวิธีการที่คุณโต้ตอบกับฮาร์ดแวร์ นี่มีสองด้านจริง ๆ :
- เวลาประมวลผล
- การจัดการขัดจังหวะ
เวลาประมวลผล:
บางแพลตฟอร์ม - โดยเฉพาะคอนโซลที่อายุมากกว่าเช่น NES, SNES และอื่น ๆ - ต้องการให้อีมูเลเตอร์ของคุณมีเวลาที่เข้มงวดเพื่อให้เข้ากันได้อย่างสมบูรณ์ ด้วย NES คุณจะมี PPU (หน่วยประมวลผลพิกเซล) ซึ่งกำหนดให้ CPU ใส่พิกเซลลงในหน่วยความจำในเวลาที่แม่นยำ หากคุณใช้การตีความคุณสามารถนับรอบและเลียนแบบเวลาที่เหมาะสมได้อย่างง่ายดาย ด้วยการคอมไพล์แบบไดนามิก / คงที่สิ่งต่าง ๆ มีความซับซ้อนมากขึ้น
การจัดการขัดจังหวะ:
การขัดจังหวะเป็นกลไกหลักที่ CPU สื่อสารกับฮาร์ดแวร์ โดยทั่วไปส่วนประกอบฮาร์ดแวร์ของคุณจะบอก CPU เกี่ยวกับสิ่งที่ขัดจังหวะด้วย นี่เป็นวิธีที่ตรงไปตรงมา - เมื่อโค้ดของคุณขว้างอินเทอร์รัปต์ที่กำหนดมาให้คุณดูที่ตารางตัวจัดการอินเทอร์รัปต์และโทรติดต่อกลับที่เหมาะสม
การจำลองฮาร์ดแวร์:
มีสองด้านในการจำลองอุปกรณ์ฮาร์ดแวร์ที่ให้มา:
- จำลองการทำงานของอุปกรณ์
- จำลองอุปกรณ์อินเตอร์เฟสจริง
นำกรณีของฮาร์ดไดรฟ์ ฟังก์ชั่นถูกจำลองด้วยการสร้างที่เก็บข้อมูลสำรองรูทีนการอ่าน / เขียน / รูปแบบ ฯลฯ ส่วนนี้โดยทั่วไปจะตรงไปตรงมามาก
อินเทอร์เฟซที่แท้จริงของอุปกรณ์นั้นซับซ้อนกว่าเล็กน้อย นี่คือการรวมกันของการลงทะเบียนหน่วยความจำที่แมปบางส่วน (เช่นส่วนของหน่วยความจำที่อุปกรณ์มองหาการเปลี่ยนแปลงเพื่อส่งสัญญาณ) และการขัดจังหวะ สำหรับฮาร์ดไดรฟ์คุณอาจมีพื้นที่หน่วยความจำที่แมปซึ่งคุณวางคำสั่งอ่านเขียน ฯลฯ จากนั้นอ่านข้อมูลนี้กลับ
ฉันจะลงรายละเอียดเพิ่มเติม แต่มีหลายวิธีที่คุณสามารถไปกับมันได้ หากคุณมีคำถามเฉพาะที่นี่โปรดถามและฉันจะเพิ่มข้อมูล
แหล่งข้อมูล:
ฉันคิดว่าฉันได้รับที่ดีงามบทนำที่นี่ แต่มีตันในพื้นที่เพิ่มเติม ฉันมีความสุขมากกว่าที่จะช่วยตอบคำถามใด ๆ ฉันคลุมเครือมากในเรื่องนี้เพราะความซับซ้อนอันยิ่งใหญ่
ลิงค์บังคับวิกิพีเดีย:
ทรัพยากรการจำลองทั่วไป:
- Zophar - นี่คือที่ที่ฉันเริ่มต้นด้วยการจำลองการดาวน์โหลดอีมูเลเตอร์แรกและในที่สุดก็ปล้นเอกสารที่มีขนาดใหญ่มาก นี่เป็นแหล่งข้อมูลที่ดีที่สุดที่คุณสามารถมีได้
- NGEmu - มีทรัพยากรไม่มากนักโดยตรง แต่ฟอรัมของพวกเขานั้นไม่สามารถเอาชนะได้
- RomHacking.net - ส่วนเอกสารมีทรัพยากรเกี่ยวกับสถาปัตยกรรมเครื่องสำหรับคอนโซลที่ได้รับความนิยม
โครงการจำลองเพื่อการอ้างอิง:
- IronBabel - นี่คือแพลตฟอร์มการจำลองสำหรับ. NET, เขียนด้วย Nemerle และคอมไพล์โค้ดใหม่เป็น C # ได้ทันที คำเตือน: นี่คือโครงการของฉันดังนั้นโปรดอภัยเสียบไร้ยางอาย
- BSnes - โปรแกรมจำลอง SNES ที่ยอดเยี่ยมพร้อมเป้าหมายความแม่นยำที่สมบูรณ์แบบ
- MAME - ตัวจำลองอาร์เคด การอ้างอิงที่ดี
- 6502asm.com - นี่คืออีมูเลเตอร์ JavaScript 6502 พร้อมฟอรัมเล็ก ๆ
- dynarec'd 6502asm - นี่คือการแฮ็กเล็กน้อยที่ฉันทำในหนึ่งหรือสองวัน ฉันเอาอีมูเลเตอร์ที่มีอยู่จาก 6502asm.com และเปลี่ยนเป็นโค้ดซ้ำอีกครั้งแบบไดนามิกเป็น JavaScript เพื่อเพิ่มความเร็วสูง
การอ้างอิงการคอมไพล์ซ้ำตัวประมวลผล:
- การวิจัยลงใน recompilation คงทำโดยไมเคิล Steil (อ้างถึงข้างต้น) culminated ในบทความนี้และคุณสามารถหาแหล่งที่มาและเช่นที่นี่
ภาคผนวก:
เป็นเวลานานกว่าหนึ่งปีแล้วตั้งแต่คำตอบนี้ถูกส่งมาและด้วยความสนใจทั้งหมดที่ได้รับฉันคิดว่าถึงเวลาที่จะต้องปรับปรุงบางสิ่ง
บางทีสิ่งที่น่าตื่นเต้นที่สุดในการลอกเลียนแบบในตอนนี้ก็คือlibcpuซึ่งเริ่มโดย Michael Steil มันเป็นไลบรารีที่มีวัตถุประสงค์เพื่อสนับสนุนคอร์ CPU จำนวนมากซึ่งใช้ LLVM สำหรับการคอมไพล์ใหม่ (สแตติกและไดนามิก!) มันมีศักยภาพมากและฉันคิดว่ามันจะทำสิ่งที่ดีสำหรับการลอกเลียนแบบ
emu-docsก็ถูกนำมาให้ฉันด้วยเช่นกันซึ่งเป็นที่เก็บเอกสารระบบที่ยอดเยี่ยมซึ่งมีประโยชน์มากสำหรับการจำลอง ฉันไม่ได้ใช้เวลามากที่นั่น แต่ดูเหมือนว่าพวกเขามีทรัพยากรที่ดีมากมาย
ฉันดีใจที่โพสต์นี้มีประโยชน์และฉันหวังว่าฉันจะลาออกและจบหนังสือของฉันในหัวข้อภายในสิ้นปี / ต้นปีหน้า