हम एक एमुलेटर लिखते हैं जिसकी किसी को जरूरत नहीं है

शुभ दिन।


काफी समय पहले कुछ प्रोसेसर के एक एमुलेटर लिखने की इच्छा थी।
और साइकिल का आविष्कार करने से बेहतर क्या हो सकता है?


बाइक का नाम V16 है, शब्द आभासी शब्द से और वास्तव में, थोड़ी गहराई से।



कहाँ से शुरू करें?


और आपको प्रोसेसर के विवरण के साथ, निश्चित रूप से शुरू करने की आवश्यकता है।


बहुत शुरुआत में, मैंने DCPU-16 एमुलेटर लिखने की योजना बनाई, लेकिन इंटरनेट पर इस तरह के चमत्कारों की संख्या बहुत अधिक है, इसलिए मैंने केवल DCPU-16 1.1 के साथ सबसे बुनियादी "चाट" पर ध्यान केंद्रित करने का फैसला किया।


आर्किटेक्चर


मेमोरी और पोर्ट


  • V16 रैम के 128Kb (65536 शब्द) को संबोधित करता है, जिसे डिवाइस बफ़र्स और स्टैक के रूप में भी इस्तेमाल किया जा सकता है।
  • स्टैक एफएफएफएफ पते से शुरू होता है, इसलिए, आरएसपी का मानक मान 0xFFFF है
  • V16 I / O पोर्ट में 256 हैं, इन सभी की लंबाई 16 बिट्स है। उनमें से पढ़ना और लिखना IN b, a और OUT b, a के निर्देशों के माध्यम से किया जाता OUT b, a

रजिस्टरों


V16 में सामान्य प्रयोजन रजिस्टर के दो सेट हैं: प्राथमिक और वैकल्पिक।
एक प्रोसेसर केवल एक सेट के साथ काम कर सकता है, इसलिए आप XCR निर्देश का उपयोग करके सेट के बीच स्विच कर सकते हैं।


अनुदेश


सभी निर्देशों में अधिकतम तीन शब्द होते हैं और पहले पूरी तरह से परिभाषित होते हैं
पहले शब्द को तीन मूल्यों में विभाजित किया गया है: कम बाइट ओपकोड है, दो 4-बिट मान के रूप में उच्च बाइट ऑपरेंड का वर्णन है।


बीच में आता है


यहां व्यवधान उन पतों से अधिक कुछ नहीं है जिनके पते प्रोसेसर प्रोसेसर CALL निर्देश को CALL । यदि पता मान शून्य है, तो व्यवधान कुछ भी नहीं करता है, यह केवल एचएफ ध्वज को रीसेट करता है।


मूल्य सीमाविवरण
0x0 ... 0x3मूल्य के रूप में मामला
0x4 ... 0x7पर मान के रूप में पंजीकृत करें
0x8 ... 0xBपते पर मूल्य के रूप में रजिस्टर + निरंतर
0xCएक मूल्य के रूप में लगातार
0xDमान के रूप में लगातार
0xERIP रजिस्टर केवल-पढ़ने के लिए मान के रूप में
0xFमूल्य के रूप में आरएसपी रजिस्टर

छद्मकोश और शब्दों का एक उदाहरण जिसमें यह सब अनुवाद किया जाना चाहिए:


 MOV RAX, 0xABCD ; 350D ABCD MOV [RAX], 0x1234 ; 354D 1234 

साइकिल (चक्र)


V16 1, 2 या 3 उपायों में एक निर्देश निष्पादित कर सकता है। प्रत्येक मेमोरी एक्सेस एक अलग घड़ी चक्र है। निर्देश नहीं है !


लिखना शुरू करते हैं!


बुनियादी प्रोसेसर संरचनाओं का कार्यान्वयन


  1. रजिस्टर का एक सेट। केवल चार रजिस्टर हैं, लेकिन स्थिति में सुधार है कि प्रोसेसर में दो ऐसे सेट हैं। XCR अनुदेश का उपयोग करके स्विचिंग होती है।


     typedef struct Regs { uint16_t rax, rbx; //Primary Accumulator, Base Register uint16_t rcx, rdx; //Counter Register, Data Register } regs_t; 

  2. झंडे। DCPU-16 के विपरीत, V16 में सशर्त जंप, सबरूटीन कॉल और उसी से रिटर्न होता है। फिलहाल, प्रोसेसर में 8 झंडे हैं, जिनमें से 5 हालत झंडे हैं।


     //  ,    stdbool.h typedef struct Flags { bool IF, IR, HF; bool CF, ZF; bool EF, GF, LF; } flags_t; 

  3. दरअसल, प्रोसेसर ही। यह रुकावट के पतों की तालिका का भी वर्णन करता है, जिसे डिस्क्रिप्टर कहा जा सकता है और x86 का एक और संदर्भ मिल सकता है।


     typedef struct CPU { //CPU Values uint16_t ram[V16_RAMSIZE]; //Random Access Memory uint16_t iop[V16_IOPSIZE]; //Input-Output Ports uint16_t idt[V16_IDTSIZE]; //Interrupt vectors table (Interrupt Description Table) flags_t flags; //Flags regs_t reg_m, reg_a; //Main and Alt register files regs_t * reg_current; //Current register file uint16_t rip, rsp, rex; //Internal Registers: Instruction Pointer, Stack Pointer, EXtended Accumulator //Emulator values bool reg_swapped; //Is current register file alt bool running; //Is cpu running uint32_t cycles; //RAM access counter } cpu_t; 

  4. संकार्य। मूल्यों को प्राप्त करते समय, हमें पहले पढ़ने की ज़रूरत है, फिर बदलें, और उसके बाद मान वापस लिखें जहां हमें यह मिला है।


     typedef struct Opd { uint8_t code : 4; uint16_t value; uint16_t nextw; } opd_t; 


संरचनाओं के साथ काम करने के लिए कार्य


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


 cpu_t * cpu_create(void); //   void cpu_delete(cpu_t *); //   void cpu_load(cpu_t *, const char *); // ROM   void cpu_rswap(cpu_t *); //   uint16_t cpu_nextw(cpu_t *); //RAM[RIP++]. Nuff said void cpu_getop(cpu_t *, opd_t *, uint8_t); //  void cpu_setop(cpu_t *, opd_t *, uint16_t); //  void cpu_tick(cpu_t *); //   void cpu_loop(cpu_t *); // ,    

इसके अलावा, मैंने ऑपरेशन कोड के साथ एक बड़ी गणना का उल्लेख नहीं किया है, लेकिन यह आवश्यक नहीं है और केवल यह समझने के लिए आवश्यक है कि सभी गड़बड़ी में क्या हो रहा है।


टिक () फ़ंक्शन


इसके अलावा, केवल tick() से कॉल करने के लिए इच्छित स्थिर कार्यों के लिए कॉल हैं।


 void cpu_tick(cpu_t *cpu) { //    HLT,      if(cpu->flags.HF) { //      ,      if(!cpu->flags.IF) { cpu->running = false; } return; } //       uint16_t nw = cpu_nextw(cpu); uint8_t op = ((nw >> 8) & 0xFF); uint8_t ob = ((nw >> 4) & 0x0F); uint8_t oa = ((nw >> 0) & 0x0F); //     //   opd_t opdB = { 0 }; opd_t opdA = { 0 }; //    cpu_getop(cpu, &opdB, ob); cpu_getop(cpu, &opdA, oa); //        -  uint16_t B = opdB.value; uint16_t A = opdA.value; uint32_t R = 0xFFFFFFFF; //    bool clearf = true; //       ? //   ! switch(op) { //     . ,   ,    R } //   if(clearf) { cpu->flags.EF = false; cpu->flags.GF = false; cpu->flags.LF = false; } //  ,  32-   16-  //  0xFFFF0000,   0xFFFF << 16 //        32-  if(R != 0xFFFFFFFF) { cpu_setop(cpu, &opdB, (R & 0xFFFF)); cpu->rex = ((R >> 16) & 0xFFFF); cpu->flags.CF = (cpu->rex != 0); cpu->flags.ZF = (R == 0); } return; } 

आगे क्या करना है?


इस प्रश्न का उत्तर खोजने की कोशिश में, मैंने एम से सी से सी ++ तक पांच बार रिवाइटर किया, और इसके विपरीत।


हालाँकि, मुख्य लक्ष्यों को अब पहचाना जा सकता है:


  • सामान्य इंटरप्ट को फास्ट करें (केवल एक फ़ंक्शन को कॉल करने और अन्य इंटरप्ट को प्राप्त करने पर प्रतिबंध लगाने के बाद, फ़ंक्शन कॉल करें और कतार में नए इंटरप्ट को जोड़ें)।
  • स्क्रू डिवाइस, साथ ही उनके साथ संवाद करने के तरीके, opcodes का लाभ 256 हो सकता है।
  • पढ़ाने के लिए अपने आप को हैबर पर कोई विधर्म नहीं लिखें प्रोसेसर 200 MHz की विशिष्ट घड़ी की गति से संचालित होता है।

निष्कर्ष


मुझे उम्मीद है कि यह "लेख" किसी के लिए उपयोगी होगा, कोई उन्हें कुछ समान लिखने के लिए संकेत देगा।


मेरे प्यादों को गितुब पर देखा जा सकता है।


इसके अलावा, डरावनी के बारे में, मेरे पास इस इम्यूलेटर के पुराने संस्करण के लिए कोडांतरक है (नहीं, यहां तक ​​कि प्रयास न करें, एमुलेटर कम से कम गलत ROM प्रारूप के बारे में शिकायत करेगा)

Source: https://habr.com/ru/post/hi457144/


All Articles