2010-03-02 11 views
6

पृष्ठभूमि जानकारी: आखिरकार, मैं मूल निंटेंडो या गेमबॉय जैसी वास्तविक मशीन का एक एमुलेटर लिखना चाहता हूं। हालांकि, मैंने फैसला किया कि मुझे कहीं और अधिक सरल, शुरू करने की आवश्यकता है। मेरे कंप्यूटर विज्ञान सलाहकार/प्रोफेसर ने मुझे एक बहुत ही सरल काल्पनिक प्रोसेसर के लिए विनिर्देश दिए जो उन्होंने पहले अनुकरण करने के लिए बनाए थे। एक रजिस्टर (संचयक) और 16 ऑपकोड हैं। प्रत्येक निर्देश में 16 बिट होते हैं, जिनमें से पहले 4 में ऑपोड होता है, जिनमें से बाकी ऑपरेंड होता है। निर्देश बाइनरी प्रारूप में स्ट्रिंग के रूप में दिए गए हैं, उदाहरण के लिए, "0101 0101 0000 1111"।एक साधारण सीपीयू एमुलेटर के कार्यान्वयन के संबंध में प्रश्न

मेरा प्रश्न: सी ++ में, प्रसंस्करण के लिए निर्देशों का विश्लेषण करने का सबसे अच्छा तरीका क्या है? कृपया मेरा अंतिम लक्ष्य दिमाग में रखें। यहाँ कुछ अंक मैं माना जाता है कर रहे हैं:

  1. मैं सिर्फ प्रक्रिया नहीं कर सकते और क्योंकि कोड स्वयं संशोधित है के रूप में मैं उन्हें पढ़ने के निर्देशों पर अमल: एक अनुदेश एक बाद अनुदेश बदल सकते हैं। इस बारे में जानने के लिए मैं एकमात्र तरीका देख सकता हूं कि सभी परिवर्तनों को संग्रहित किया जाए और प्रत्येक निर्देश के लिए यह जांचने के लिए कि परिवर्तन को लागू करने की आवश्यकता है या नहीं। यह प्रत्येक निर्देश के निष्पादन के साथ तुलना की भारी मात्रा में हो सकता है, जो अच्छा नहीं है। और इसलिए, मुझे लगता है कि मुझे निर्देशों को एक और प्रारूप में दोबारा जोड़ना है।

  2. हालांकि मैं ऑपोड को एक स्ट्रिंग के रूप में पार्स कर सकता हूं और इसे संसाधित कर सकता हूं, ऐसे कई उदाहरण हैं जहां संपूर्ण रूप से निर्देश को एक संख्या के रूप में लिया जाना चाहिए। वृद्धि के लिए ओपोड, उदाहरण के लिए, निर्देश के ओपोड अनुभाग को भी संशोधित कर सकता है।

  3. यदि मैं निर्देशों को पूर्णांक में परिवर्तित करना था, तो मुझे यकीन नहीं है कि मैं केवल int के opcode या operand अनुभाग को कैसे पार्स कर सकता हूं। यहां तक ​​कि अगर मैं प्रत्येक निर्देश को तीन भागों में पुन: संकलित करना चाहता था, तो एक इंट के रूप में संपूर्ण निर्देश, एक int के रूप में ओपोड, और एक int के रूप में ऑपरेंड, जो अभी भी समस्या का समाधान नहीं करेगा, क्योंकि मुझे एक संपूर्ण निर्देश में वृद्धि करना पड़ सकता है और बाद में प्रभावित ओपोड या ऑपरेंड पार्स। इसके अलावा, क्या मुझे यह रूपांतरण करने के लिए एक फ़ंक्शन लिखना होगा, या सी ++ के लिए कुछ लाइब्रेरी है जिसमें एक फ़ंक्शन "बाइनरी प्रारूप" में एक स्ट्रिंग को एक पूर्णांक में परिवर्तित करता है (जैसे जावा में Integer.parseInt (str1, 2))?

  4. इसके अलावा, मैं शिफ्टिंग बिट्स जैसे संचालन करने में सक्षम होना चाहता हूं। मुझे यकीन नहीं है कि यह कैसे प्राप्त किया जा सकता है, लेकिन इससे प्रभावित हो सकता है कि मैं इस पुनर्मूल्यांकन को कैसे कार्यान्वित करता हूं।

आपकी सहायता या सलाह के लिए धन्यवाद!

उत्तर

5

मूल कोड को पूर्णांक की एक सरणी में पार्स करें। यह सरणी आपके कंप्यूटर की स्मृति है।

विभिन्न क्षेत्रों को निकालने के लिए bitwise संचालन का उपयोग करें। उदाहरण के लिए, इस:

unsigned int x = 0xfeed; 
unsigned int opcode = (x >> 12) & 0xf; 

एक unsigned int में संग्रहीत एक 16-बिट मूल्य से सर्वोच्च चार बिट (0xf, यहाँ) निकाल देंगे। फिर आप इसका उपयोग कर सकते हैं उदा। switch() opcode का निरीक्षण किया है और उचित कार्रवाई करने के लिए:

enum { ADD = 0 }; 

unsigned int execute(int *memory, unsigned int pc) 
{ 
    const unsigned int opcode = (memory[pc++] >> 12) & 0xf; 

    switch(opcode) 
    { 
    case OP_ADD: 
    /* Do whatever the ADD instruction's definition mandates. */ 
    return pc; 
    default: 
    fprintf(stderr, "** Non-implemented opcode %x found in location %x\n", opcode, pc - 1); 
    } 
    return pc; 
} 

संशोधित करना स्मृति सिर्फ पूर्णांक अपने सरणी में लेखन, शायद भी कुछ बिटवाइज़ गणित का उपयोग कर अगर जरूरत का एक मामला है।

+0

मैं उम्मीद कर रहा था कि कोई इस तरह की अवधारणा का उल्लेख करेगा। मैंने पहले कभी इसका इस्तेमाल नहीं किया है, इसलिए मुझे और अधिक शोध करना होगा। धन्यवाद! –

+0

आह, विश्वविद्यालय परियोजनाओं की यादें! – sdg

+0

+1। यह मूल दृष्टिकोण है जिसे आपको लेना चाहिए। यहां मुख्य बिंदु, ब्रैंडन, आपके प्रश्न के सापेक्ष, यह है कि इसे सामान्य रूप से संपर्क करने के लिए, आपको "मशीन कोड" प्राप्त करने की आवश्यकता है जो आपके वर्चुअल कंप्यूटर के पता स्थान का प्रतिनिधित्व करने वाले सरणी में बाइट्स की सरणी होगी। फिर यदि निर्देश स्मृति (कोड) संपादित करते हैं, तो आप कुछ भी विशेष नहीं करते हैं, तो आप केवल निर्देशों का पालन करें और उन्हें आपकी बड़ी वर्चुअल मेमोरी सरणी के अंदर सही चीज़ करना चाहिए। आइओ, आपको असेंबलर (वह टूल जो टेक्स्ट स्ट्रिंग्स को निर्देश बाइट्स में अनुवाद करता है) और एमुलेटर की आवश्यकता है, जो चीज –

1

मुझे लगता है कि निर्देशों को पढ़ने के लिए सबसे अच्छा तरीका है, उन्हें हस्ताक्षरित पूर्णांक में परिवर्तित करें, और उन्हें स्मृति में संग्रहीत करें, फिर उन्हें स्मृति से निष्पादित करें।

  1. एक बार जब आप निर्देश पार्स और उन्हें स्मृति में संग्रहीत किया है, स्वयं संशोधन प्रत्येक निर्देश के लिए परिवर्तन की सूची के भंडारण की तुलना में बहुत आसान है। आप उस स्थान पर केवल स्मृति को बदल सकते हैं (माना जाता है कि आपको कभी भी यह जानने की आवश्यकता नहीं है कि पुराना निर्देश क्या था)।

  2. चूंकि आप निर्देशों को पूर्णांक में परिवर्तित कर रहे हैं, इसलिए यह समस्या म्यूट है।

  3. opcode और संकार्य वर्गों को पार्स करने के लिए आपको थोड़ा स्थानांतरण और मास्किंग का उपयोग करना होगा। उदाहरण के लिए, ओप कोड प्राप्त करने के लिए, आप ऊपरी 4 बिट्स को मुखौटा करते हैं और 12 बिट्स (instruction >> 12) द्वारा स्थानांतरित करते हैं। आप ऑपरेंड भी प्राप्त करने के लिए एक मुखौटा का उपयोग कर सकते हैं।

  4. आपका मतलब है कि आपकी मशीन में निर्देश हैं जो बिट्स को स्थानांतरित करते हैं? इससे प्रभावित नहीं होना चाहिए कि आप ऑपरेटरों को कैसे स्टोर करते हैं। जब आप उन निर्देशों में से किसी एक को निष्पादित करते हैं, तो आप केवल C++ बिट-स्थानांतरण ऑपरेटरों << और >> का उपयोग कर सकते हैं।

+0

1 के संबंध में: उस स्थिति में, मुझे अभी भी उन्हें निष्पादित करने के विरोध में निर्देशों के सेट को फिर से संकलित करना होगा क्योंकि वे पढ़ रहे हैं, सही? यह कोई समस्या नहीं है, मैं बस सोच रहा था कि गैर-पुनर्मूल्यांकन विधि को पूरा करने का कोई अच्छा तरीका हो सकता है या नहीं। // 4 के बारे में: ठीक है, यही मेरा मतलब था। मैंने सोचा कि यह प्रभावित करेगा कि मैं निर्देश टुकड़ों को कैसे स्टोर करूंगा, क्योंकि मुझे प्रत्येक टुकड़े को अलग से विचार करना होगा। लेकिन यह थोड़ा गणित ऐसा लगता है जैसे यह इसे प्राप्त कर सकता है। // आपने जो उल्लेख किया है वह अवधारणात्मक रूप से समझ में आता है। जब मैं आज घर आऊंगा तो मैं कोशिश करूंगा। धन्यवाद! –

+0

मुझे नहीं पता कि आप "recompile" से क्या मतलब है। ए (सरल) सीपीयू कुछ भी "recompile" नहीं करता है: यह बिट्स मिल गया है, और यह वही करता है जो वे कहते हैं। क्या आप इसका स्पष्टीकरण दे सकते हैं कि इसका मतलब क्या है? – Ken

+0

मेरी समझ यह है कि अनुकरणकर्ता अक्सर दूसरे प्रारूप में निर्देशों को दोबारा जोड़कर काम करते हैं जिन्हें एमुलेटर की मशीन द्वारा अधिक आसानी से संसाधित किया जा सकता है। मैं यहां शब्द का उपयोग कर रहा हूं, लेकिन मुझे लगता है कि यह वही विचार है। मैं बस सोच रहा था कि निर्देशों को किसी अन्य रूप में स्मृति में कहीं और निर्देशों को संग्रहीत किए बिना निर्देशों को निष्पादित करने का कोई प्रभावी तरीका है (उदाहरण के लिए, बिना हस्ताक्षरित इन्ट्स के रूप में)। –

0

बस अगर यह मदद करता है, तो सी ++ में मैंने लिखा अंतिम सीपीयू एमुलेटर है। असल में, यह एकमात्र एमुलेटर है जिसे मैंने सी ++ में लिखा है।

कल्पना की भाषा से थोड़ा विशेष स्वभाव का है, लेकिन यह एक पूरी तरह से सम्मानित, सरल वीएम विवरण, संभवतः काफी अपने प्रोफेसर के वी एम के समान है:

http://www.boundvariable.org/um-spec.txt

यहाँ मेरी (कुछ अति-इंजीनियर) कोड है, जो देना चाहिए आप कुछ विचार ,

http://www.eschatonic.org/misc/um.zip

आप शायद एक वेब खोज के साथ तुलना के लिए अन्य कार्यान्वयन पा सकते हैं के बाद से बहुत से लोगों को प्रतियोगिता में प्रवेश किया (उदाहरण के लिए: यह कैसे um.cpp में गणितीय ऑपरेटर, विशालकाय स्विच वक्तव्य में लागू करने के लिए पता चलता मैं उनमें से एक नहीं था: मैंने इसे बहुत बाद में किया)। हालांकि मुझे लगता है कि सी ++ में बहुत से लोग नहीं हैं।

अगर मैं तुम्हें थे, मैं सिर्फ निर्देश, स्टोर चाहते हैं के रूप में तार के साथ शुरू करने के लिए है कि अगर जिस तरह से है कि अपने आभासी मशीन विनिर्देश उन पर आपरेशन को परिभाषित करता है है। फिर उन्हें आवश्यकतानुसार पूर्णांक में रूपांतरित करें, हर बार जब आप उन्हें निष्पादित करना चाहते हैं। यह धीमा हो जाएगा, लेकिन क्या? आपका वास्तविक वीएम नहीं है जिसे आप समय-महत्वपूर्ण कार्यक्रम चलाने के लिए उपयोग करने जा रहे हैं, और कुत्ते-धीमी दुभाषिया अभी भी उन महत्वपूर्ण बिंदुओं को दर्शाता है जिन्हें आपको इस चरण में जानने की आवश्यकता है।

कि हालांकि वीएम वास्तव में पूर्णांक के मामले में सब कुछ को परिभाषित करता है, और तार कार्यक्रम का विवरण देने में जब यह मशीन में लोड है बस देखते हैं यह संभव है। उस स्थिति में, प्रोग्राम को शुरुआत में पूर्णांक में कनवर्ट करें। यदि वीएम कार्यक्रमों और डेटा को एकसाथ स्टोर करता है, तो दोनों ही परिचालनों के साथ काम करते हैं, तो यह जाने का तरीका है।

उनके बीच चयन करने का तरीका ओपोड को देखना है जिसका उपयोग प्रोग्राम को संशोधित करने के लिए किया जाता है। क्या नया निर्देश इसे पूर्णांक के रूप में या स्ट्रिंग के रूप में प्रदान किया गया है? जो भी हो, शुरू करने के लिए सबसे आसान बात शायद उस प्रारूप में प्रोग्राम को स्टोर करना है। एक बार काम करने के बाद आप हमेशा बाद में बदल सकते हैं।

उपरोक्त वर्णित यूएम के मामले में, मशीन को 32 बिट्स के लिए "प्लैटर्स" के संदर्भ में परिभाषित किया गया है।स्पष्ट रूप से इन्हें सी ++ में 32-बिट पूर्णांक के रूप में प्रदर्शित किया जा सकता है, इसलिए मेरा कार्यान्वयन यही है।

+0

इससे पहले कि मैंने अपने प्रोफेसर से बात की, मैंने उस प्रतियोगिता को अनुकरण करने और पाया जाने के लिए कुछ आसान खोजा। मेरे साथ शुरू करने के लिए यह अभी भी थोड़ा जटिल था, लेकिन यह मेरा अगला कदम हो सकता है। मैं विचारों के लिए अपने कोड को देख लूंगा। धन्यवाद! –

0

मैंने कस्टम क्रिप्टोग्राफिक प्रोसेसर के लिए एक एमुलेटर बनाया। मैंने आधार कक्षाओं का पेड़ बनाकर सी ++ के बहुरूपता का शोषण किया:

struct Instruction // Contains common methods & data to all instructions. 
{ 
    virtual void execute(void) = 0; 
    virtual size_t get_instruction_size(void) const = 0; 
    virtual unsigned int get_opcode(void) const = 0; 
    virtual const std::string& get_instruction_name(void) = 0; 
}; 

class Math_Instruction 
: public Instruction 
{ 
    // Operations common to all math instructions; 
}; 

class Branch_Instruction 
: public Instruction 
{ 
    // Operations common to all branch instructions; 
}; 

class Add_Instruction 
: public Math_Instruction 
{ 
}; 

मेरे पास कुछ कारखानियां भी थीं। कम से कम दो उपयोगी होंगे:

  1. टेक्स्ट से निर्देश बनाने के लिए फैक्टरी।
  2. फैक्टरी opcode

से निर्देश बनाने के लिए अनुदेश कक्षाएं एक इनपुट स्रोत से अपने डेटा लोड करने के तरीकों होना चाहिए (उदाहरण के लिए std::istream) या पाठ (std::string)। कोरोलरी आउटपुट के तरीकों को भी समर्थित किया जाना चाहिए (जैसे निर्देश का नाम और ऑपोड)।

मेरे पास एप्लिकेशन इनपुट फ़ाइल से ऑब्जेक्ट्स बनाते हैं, और उन्हें Instruction के वेक्टर में रखें। निष्पादक विधि सरणी में प्रत्येक निर्देश की 'execute() `विधि चलाएगी। यह क्रिया निर्देशित पत्ती ऑब्जेक्ट पर घूमती है जिसने विस्तृत निष्पादन किया।

अन्य वैश्विक वस्तुएं हैं जिन्हें अनुकरण की भी आवश्यकता हो सकती है। मेरे मामले में कुछ में डेटा बस, रजिस्ट्रार, एएलयू और मेमोरी लोकेशन शामिल थे।

कृपया कोड करने से पहले परियोजना के बारे में सोचने और सोचने में अधिक समय व्यतीत करें। मुझे यह एक चुनौती मिली, विशेष रूप से single-step सक्षम डीबगर और जीयूआई लागू करना।

शुभकामनाएं!

+0

बीटीडब्ल्यू, मेरे अल्मा माटर में, प्रोफेसरों में से एक ने अपने छात्रों को * माइक्रोप्रोसेसर डिजाइन * कक्षा के लिए हार्डवेयर घटकों का अनुकरण करने के लिए कार्यों को लिखा था। प्रोसेसर इम्यूलेशन के बारे में हमने कुछ अच्छी चर्चा की थी। :-) –

संबंधित मुद्दे