8

मैं 8051 असेंबलर बना रहा हूं।एक असेंबलर बनाने के लिए डिजाइन पैटर्न

सब कुछ करने से पहले एक tokenizer जो अगले टोकन पढ़ता है, त्रुटि झंडे सेट, EOF पहचानता है, आदि है
तो फिर वहाँ संकलक के मुख्य पाश, जो अगले टोकन पढ़ता है और वैध स्मृति सहायकों के लिए जांच करें:

mnemonic= NextToken(); 
if (mnemonic.Error) 
{ 
    //throw some error 
} 
else if (mnemonic.Text == "ADD") 
{ 
    ... 
} 
else if (mnemonic.Text == "ADDC") 
{ 
    ... 
} 

और यह कई मामलों में जारी है। इससे भी बदतर प्रत्येक मामले के अंदर कोड है, जो मान्य पैरामीटर के लिए जांच करता है, फिर इसे संकलित कोड में परिवर्तित करता है। अभी यह इस तरह दिखता है:

if (mnemonic.Text == "MOV") 
{ 
    arg1 = NextToken(); 
    if (arg1.Error) { /* throw error */ break; } 
    arg2 = NextToken(); 
    if (arg2.Error) { /* throw error */ break; } 

    if (arg1.Text == "A") 
    { 
     if (arg2.Text == "B") 
      output << 0x1234; //Example compiled code 
     else if (arg2.Text == "@B") 
      output << 0x5678; //Example compiled code 
     else 
      /* throw "Invalid parameters" */ 
    } 
    else if (arg1.Text == "B") 
    { 
     if (arg2.Text == "A") 
      output << 0x9ABC; //Example compiled code 
     else if (arg2.Text == "@A") 
      output << 0x0DEF; //Example compiled code 
     else 
      /* throw "Invalid parameters" */ 
    } 
} 

स्मृति सहायकों मैं वैध पैरामीटर उसके बाद सही संकलित कोड बनाने के लिए जाँच करने के लिए है से प्रत्येक के लिए। प्रत्येक मामले में प्रत्येक निमोनिक दोहराव के लिए वैध मानकों की जांच के लिए बहुत ही समान कोड।

तो क्या इस कोड को बेहतर बनाने के लिए एक डिज़ाइन पैटर्न है?
या इसे लागू करने का एक आसान तरीका है?

संपादित करें: मैंने प्लिंथ का जवाब स्वीकार कर लिया, धन्यवाद। फिर भी यदि आपके पास इस पर विचार है, तो मुझे उन्हें सीखने में खुशी होगी। सबको शुक्रीया।

उत्तर

7

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

यहां इसका कारण दिया - एक ठेठ विधानसभा लाइन शायद कुछ इस तरह दिखेगा:

[label:] [instruction|directive][newline] 

और एक अनुदेश हो जाएगा:

plain-mnemonic|mnemonic-withargs 

और एक निर्देश होगा:

plain-directive|directive-withargs 

आदि

Gold जैसे सभ्य पार्सर जेनरेटर के साथ, आप कुछ घंटों में 8051 के लिए व्याकरण को खटखटा सकते हैं।

.define kMagicNumber 0xdeadbeef 
CMPA #(2 * kMagicNumber + 1) 

जो हाथ से करने के लिए एक असली भालू हो सकता है: लाभ यह हाथ से अधिक पार्स करने के लिए है कि आप की तरह अपने विधानसभा कोड में काफी भाव जटिल करने में सक्षम हो जाएगा।

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

enum { 
    Implied = 1, Direct = 2, Extended = 4, Indexed = 8 // etc 
} AddressingMode; 

/* for a 4 char mnemonic, this struct will be 5 bytes. A typical small processor 
* has on the order of 100 instructions, making this table come in at ~500 bytes when all 
* is said and done. 
* The time to binary search that will be, worst case 8 compares on the mnemonic. 
* I claim that I/O will take way more time than look up. 
* You will also need a table and/or a routine that given a mnemonic and addressing mode 
* will give you the actual opcode. 
*/ 

struct InstructionInfo { 
    char Mnemonic[4]; 
    char AddessingMode; 
} 

/* order them by mnemonic */ 
static InstructionInfo instrs[] = { 
    { {'A', 'D', 'D', '\0'}, Direct|Extended|Indexed }, 
    { {'A', 'D', 'D', 'A'}, Direct|Extended|Indexed }, 
    { {'S', 'U', 'B', '\0'}, Direct|Extended|Indexed }, 
    { {'S', 'U', 'B', 'A'}, Direct|Extended|Indexed } 
}; /* etc */ 

static int nInstrs = sizeof(instrs)/sizeof(InstrcutionInfo); 

InstructionInfo *GetInstruction(char *mnemonic) { 
    /* binary search for mnemonic */ 
} 

int InstructionSize(AddressingMode mode) 
{ 
    switch (mode) { 
    case Inplied: return 1; 
    /* etc */ 
    } 
} 

तब आपके पास प्रत्येक निर्देश की एक सूची होगी जिसमें बदले में सभी एड्रेसिंग मोड की एक सूची होगी।

char *line = ReadLine(); 
int nextStart = 0; 
int labelLen; 
char *label = GetLabel(line, &labelLen, nextStart, &nextStart); // may be empty 
int mnemonicLen; 
char *mnemonic = GetMnemonic(line, &mnemonicLen, nextStart, &nextStart); // may be empty 
if (IsOpcode(mnemonic, mnemonicLen)) { 
    AddressingModeInfo info = GetAddressingModeInfo(line, nextStart, &nextStart); 
    if (IsValidInstruction(mnemonic, info)) { 
     GenerateCode(mnemonic, info); 
    } 
    else throw new BadInstructionException(mnemonic, info); 
} 
else if (IsDirective()) { /* etc. */ } 
+0

मेरी मेमोरी लगभग 200 कीबी है और मैं बहुत खुश हूं कि आपने पिछले ऑब्जेक्ट-ओरिएंटेड कोड को इस के साथ बदल दिया है! अब एक फॉलो-अप (मुझे पता है ...) प्रश्न: 'Instruction8051 {सार्वजनिक स्ट्रिंग निमोनिक {get; का उपयोग कर आपका पिछला तरीका था; सेट; } सार्वजनिक सूची <निर्देशक जानकारी> जानकारी {प्राप्त करें; सेट; }} 'कमांड पैटर्न? और क्या कोई कारण/परिस्थितियां हैं जिन्हें मैं लुक-अप टेबल्स के लिए कमांड पैटर्न पसंद करता हूं? – Hossein

+0

कमांड पैटर्न? नहीं - उनमें से दोनों को टेबल अप देखने के लिए तैयार किया गया होगा। पहले सी # कोड केवल एक है जो जेनेरिक संग्रह और ऑटो गुणों के लिए भाषा के समर्थन का लाभ उठाता है। – plinth

3

हां। अधिकांश असेंबलर डेटा की एक तालिका का उपयोग करते हैं जो निर्देशों का वर्णन करता है: निमोनिक, ओप कोड, ऑपरेंड फॉर्म आदि

मैं as के लिए स्रोत कोड को देखने का सुझाव देता हूं। मुझे हालांकि इसे खोजने में कुछ परेशानी हो रही है। देखें here। (होसेन के लिए धन्यवाद।)

+0

@wallyk:

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

+0

@ होसेन: एक लुकअप टेबल शायद आपके पास मौजूद केस कोड की कई प्रतियों की तुलना में अधिक स्मृति नहीं लेगा। प्रदर्शन के लिए, आप शायद कोई अंतर नहीं देखेंगे। आपका कोड स्ट्रिंग तुलना और फ़ंक्शन कॉल का उपयोग कर रहा है, इसलिए ऐसा नहीं है कि आप एक कड़े आंतरिक लूप को अनुकूलित कर रहे हैं। 80/20 नियम याद रखें। – Anton

+1

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

0

मुझे लगता है कि आपको विज़िटर पैटर्न में देखना चाहिए। यह आपके कोड को इतना आसान नहीं बना सकता है, लेकिन युग्मन को कम करेगा और पुन: प्रयोज्यता बढ़ाएगा। SableCC संकलक बनाने के लिए एक जावा ढांचा है जो इसे व्यापक रूप से उपयोग करता है।

+0

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

+0

नहीं, लेकिन आपके पास एक स्नोमोनिक क्लास हो सकती है जो विज़िटर पैटर्न के माध्यम से अपने आउटपुट कोड को परिभाषित कर सकती है। आप निमोनिक्स को लूप करते हैं, उन्हें आगंतुक पास करते हैं, वे अपना आउटपुट वापस करते हैं। मैंने कभी एक असेंबलर को डिजाइन नहीं किया है, इसलिए यह सिर्फ एक अनुमान है हे। –

0

क्या आपने "कमांड डिस्पैचर" पैटर्न देखा है?

http://en.wikipedia.org/wiki/Command_pattern

सामान्य विचार एक वस्तु है कि प्रत्येक अनुदेश (आदेश) संभालती बनाते हैं, और एक लुक-अप तालिका हैंडलर वर्ग के लिए प्रत्येक अनुदेश नक्शे बनाने के लिए किया जाएगा। प्रत्येक कमांड क्लास में एक सामान्य इंटरफ़ेस होगा (उदाहरण के लिए कमांड.एक्सक्यूट (* तर्क)) जो निश्चित रूप से आपको अपने वर्तमान विशाल स्विच स्टेटमेंट से क्लीनर/अधिक लचीला डिज़ाइन देगा।

+0

मैं प्रदर्शन समस्याओं के कारण "बहुत अधिक" वस्तु-उदारता से दूर रहना चाहता हूं। – Hossein

0

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

आपकी सर्वश्रेष्ठ शर्त असेंबली भाषा वाक्यविन्यास विनिर्देश को पकड़ने के लिए हो सकती है। टोकन में कनवर्ट करने के लिए लेक्सर लिखें (** कृपया, अगर-elseif-else cadders का उपयोग न करें)। फिर अर्थशास्त्र के आधार पर, कोड जारी करें।

लंबे समय पहले, असेंबलर कम से कम दो पास थे: स्थिरांक को हल करने और कंकाल कोड (प्रतीक तालिकाओं सहित) बनाने के लिए पहला। दूसरा पास अधिक ठोस या पूर्ण मूल्य उत्पन्न करना था।

क्या आपने हाल ही में ड्रैगन बुक पढ़ा है?

+0

क्या ड्रैगन बुक भी असेंबलरों पर चर्चा करता है? मैं अभी भी इसका डाउनलोड करने योग्य संस्करण ढूंढने की कोशिश कर रहा हूं या अनुवादित संस्करण प्राप्त कर रहा हूं लेकिन सफल रहा हूं। – Hossein

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