यूनिवर्सल मशीन एमुलेटर को लागू करें


13

लक्ष्य एक पूर्ण कार्यक्रम लिखना है जो आईसीएफपी 2006 से सबसे छोटी कोड के साथ यूनिवर्सल मशीन का अनुकरण करता है। यूनिवर्सल मशीन में यहां बहुत सरल निर्देश सेट किया गया है । एमुलेटर को कमांड-लाइन तर्क से फ़ाइल नाम पढ़ना है, और फ़ाइल को प्रोग्राम के रूप में चलाना है, इसलिए आपकी भाषा को कमांड-लाइन तर्कों और किसी तरह से बाहर / बाहर समर्थन करना है। एमुलेटर को उचित समय (दशकों नहीं) के भीतर सैंडमार्क पूरा करना है । यहाँ निर्देश सेट का संक्षिप्त विवरण दिया गया है:

मशीन में आठ रजिस्टर हैं जिनमें से प्रत्येक में 32-बिट अहस्ताक्षरित पूर्णांक है।
मशीन 32-बिट अहस्ताक्षरित पूर्णांक कोशिकाओं के सरणियों का अनुक्रमित सेट रखती है।
कुछ ही समय में, आवंटन निर्देश एक अपारदर्शी 32-बिट uint लौटाता है जो कि बनाए गए एरे का हैंडल होता है, जिसमें एक स्थिर आकार होता है, और 32-बिट यूंट तत्वों को रखता है।
0'th सरणी प्रोग्राम को दर्शाता है। इसे स्टार्टअप पर एक बड़ी-एंडियन फ़ाइल से लोड किया गया है।
एक इंस्ट्रक्शन पॉइंटर भी है जो 0 एरे में एक सेल की ओर इशारा करता है।
प्रत्येक चरण पर, एक निर्देश को कक्ष से इंगित किया जाता है जिसे इंगित करता है और कुछ भी किए जाने से पहले सूचक को इंगित किया जाता है।
4 सबसे महत्वपूर्ण बिट्स opcode का प्रतिनिधित्व करते हैं।
यदि ओपकोड 13 है, तो अगले 3 बिट्स रजिस्टर का प्रतिनिधित्व करते हैं, और अन्य 25 उस नंबर का प्रतिनिधित्व करते हैं जो उक्त रजिस्टर में लिखा गया है।
अन्यथा 9 कम से कम महत्वपूर्ण बिट्स तीन रजिस्टरों का प्रतिनिधित्व करते हैं, कहते हैं, ए, बी, और सी, जहां सी को कम से कम 3 महत्वपूर्ण बिट्स द्वारा दर्शाया गया है।
फिर ओपोड के आधार पर, निम्न होता है:
0. ए = बी जब तक सी == 0
1. ए = बी [सी]
2. ए [बी] = सी
3. ए = बी + सी
4. ए = बी * सी
5. ए = बी / सी
6. ए = ~ (बी एंड सी)
7. एमुलेटर बाहर निकलता है
8. बी = आवंटित (सी)
9. डीललॉकेट (सी)
10. सी से एक चरित्र को stdout
11. आउटपुट एक चरित्र इनपुट C में स्टड से
12. 0 बी में सरणी बी को कॉपी करें और पॉइंटर को सी पर सेट करें

मैंने x86_64 jitted असेंबली ( एमिट (मज़ा) शुरू होता है ) का उपयोग करके एक अनावश्यक रूप से जटिल लेकिन पूरी तरह से तेजी से कार्यान्वयन (एब) लिखा है , जो मशीन के कुछ पहलुओं को गलत समझने में आपकी मदद करने के लिए सुनिश्चित करेगा।


आपको यह तय करना होगा कि यह कोड-गोल्फ या लोकप्रियता-प्रतियोगिता होनी चाहिए या नहीं। वे अनन्य हैं।
हावर्ड

@ मैं देख रहा हूँ, धन्यवाद
mniip

अगर मैं गलत नहीं हूं, तो मशीन को बिग एंडियन नहीं बल्कि लिटिल एंडियन बताया गया है।
१५:५५ पर हस्त्कर्ण

@ हस्तूरुन डीह मैं हमेशा इन को गड़बड़ करता हूं, मुझे लगता है कि बिग एंडियन "बड़ी बाइट में समाप्त होने" के लिए खड़ा है
मैनीप

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

जवाबों:


6

PHP: 443 416  384 बाइट्स

<?php @eval(ereg_replace('[U-Z]','$\0',strtr('for(Y=[unpack("N*",join(file($argv[1])))];;A|=0){{W=Y[V=0][++U]
C&&A=B
A=Y[B][C+1]
Y[A][B+1]=C
A=B+C
A=B*C
A=bcdiv(PB),PC))*1
A=~B|~C
die
B=++Z
unset(Y[C])
echo chr(C)
C=fgetc(STDIN);C=ord(C)-(C=="")
Y[0]=Y[B|0];U=C
X[W>>25&7]=W&33554431;}}',['
'=>';}if((W>>28&15)==V++){',A=>'X[W>>6&7]',B=>'X[W>>3&7]',C=>'X[W&7]',P=>'sprintf("%u",'])));

* फिर से सुधार *। यह उतना ही छोटा है जितना मैं अब इसे प्राप्त कर सकता हूं। मैंने वर्णमाला के दूर के अंत तक कुछ चर बनाए रखे ताकि regex जो $ संकेतों को सम्मिलित करता है वह STDIN स्थिर नहीं करता है, इसलिए यहाँ थोड़ा सा शब्दकोष है:

  • यू: निर्देश सूचक
  • V: वर्तमान में परीक्षण किया जा रहा है कि opcode के सूचकांक
  • डब्ल्यू: वर्तमान अनुदेश शब्द
  • X: 8 सामान्य प्रयोजन रजिस्टर
  • Y: मुख्य मेमोरी (प्रत्येक ब्लॉक 1-आधारित है, क्योंकि यह unpack()रिटर्न एरे है)
  • Z: अगले मुफ्त मेमोरी ब्लॉक की आईडी (अंत में अतिप्रवाह होगा, लेकिन सैंडमार्क केवल ~ 92 मिलियन का उपयोग करता है)
  • ए, बी, सी वर्तमान निर्देश के रजिस्टर हैं जैसा कि कल्पना में है

अनसाइनड डिवीजन एक सूक्ष्म उपद्रव है (यह *1सुनिश्चित करने के लिए आवश्यक है कि बड़ी संख्या सही इंट में वापस डाली जाए) लेकिन अंकगणित के बाकी अंक A|=0को प्रत्येक निर्देश पर 0 ( ) के साथ अंकगणित रजिस्टर द्वारा 32-बिट रखना आसान है ।


मुझे यह परियोजना वास्तव में दिलचस्प लगी लेकिन चरित्र की गिनती को कम करने के प्रयास ने इसे धीमा और असहाय बना दिया, इसलिए मैंने एक सरल (गोल्फ वाला नहीं) जावा संस्करण भी बनाया, जो पूरे दिन के बजाय कुछ ही मिनटों में सैंडमार्क पूरा कर सकता है:

import java.io.*;
import java.util.HashMap;

public class UniversalMachine {
    public static void main(String[] args) throws IOException {
        if (args.length == 0) {
            System.err.println("Program not specified.");
            System.exit(1);
        }

        int[] program;
        try (RandomAccessFile raf = new RandomAccessFile(args[0], "r")) {
            program = new int[(int)(raf.length() / 4)];
            for (int i = 0; i < program.length; i++) {
                program[i] = raf.readInt();
            }
        }

        HashMap<Integer,int[]> memory = new HashMap<>();
        memory.put(0, program);
        int nextMemKey = 1;

        int[] R = new int[8]; // Registers
        int IP = 0; // Execution Finger (Instruction Pointer)

        loop: for (;;) {
            int ins = program[IP++];
            int op = ins >>> 28;
            if (op == 13) { // Orthography
                int A = (ins >> 25) & 7;
                int num = ins & 0x01FF_FFFF;
                R[A] = num;
            } else {
                final int A = (ins >> 6) & 7;
                final int B = (ins >> 3) & 7;
                final int C = (ins >> 0) & 7;
                switch (op) {
                case 0: // Conditional Move
                    if (R[C] != 0) R[A] = R[B];
                    break;
                case 1: // Array Index
                    R[A] = memory.get(R[B])[R[C]];
                    break;
                case 2: // Array Amendment
                    memory.get(R[A])[R[B]] = R[C];
                    break;
                case 3: // Addition
                    R[A] = R[B] + R[C];
                    break;
                case 4: // Multiplication
                    R[A] = R[B] * R[C];
                    break;
                case 5: // Division
                    R[A] = (int)((R[B] & 0xFFFF_FFFFL) / (R[C] & 0xFFFF_FFFFL));
                    break;
                case 6: // Not-And
                    R[A] = ~(R[B] & R[C]);
                    break;
                case 7: // Halt
                    break loop;
                case 8: // Allocation
                    // note: must use C before setting B, as they may be the same reg
                    memory.put(nextMemKey, new int[R[C]]);
                    R[B] = nextMemKey++;
                    break;
                case 9: // Abandonment
                    memory.remove(R[C]);
                    break;
                case 10: // Output
                    System.out.print((char)R[C]);
                    break;
                case 11: // Input
                    R[C] = System.in.read();
                    break;
                case 12: // Load Program
                    IP = R[C];
                    if (R[B] != 0) {
                        memory.put(0, program = memory.get(R[B]).clone());
                    }
                    break;
                }
            }
        }
    }
}

मुझे नहीं लगता कि आपको विभाजन के परिणाम को 32 बिट्स पर समायोजित करने की आवश्यकता है क्योंकि यह हमेशा लाभांश की तुलना में छोटा-से-या-बराबर-बराबर होता है, जो पहले से ही समायोजित है
mniip

बस जिज्ञासा से बाहर, यह कैसा दिखता है अपराजित?
टिम सेगिन

@mniip यह अब थोड़ा अलग है, लेकिन मुझे विभाजन से सावधान रहने की आवश्यकता है क्योंकि विभाजन के दौरान नंबर अहस्ताक्षरित होते हैं, और हर दूसरे क्षण वे हस्ताक्षरित होते हैं।
Boann

3

पर्ल, 407

ऐसा लगता है कि प्रश्न बहुत जटिल लग सकता है, वास्तव में यह बहुत सरल है।
मैं अभी भी perl के लिए बहुत नया हूँ, वैसे भी यहाँ है

open$f,shift;binmode$f;push@{$m[0]},unpack'N',$b while read$f,$b,4;$z=2**32;while(){$o=$m[0][$p++];$a=\$r[$o>>6&7];$b=\$r[$o>>3&7];$c=\$r[$o&7];eval qw,$$a=($$b)if$$c $$a=$m[$$b][$$c] $m[$$a][$$b]=$$c $$a=($$b+$$c)%$z $$a=$$b*$$c%$z $$a=$==$$b/$$c $$a=$$b&$$c^($z-1) exit $$b=scalar@m;$m[$$b]=[] undef$m[$$c] print(chr$$c) $$c=ord(getc) $m[0]=[@{$m[$$b]}]if$$b;$p=$$c $r[$o>>25&7]=$o&33554431,[$o>>28].";";}

यह वास्तव में धीमी गति से चलता है, शायद JITed x86_64 एक की तुलना में 800x धीमा।
इसके अलावा, मेरे एक दोस्त ने एक संदर्भ सी कार्यान्वयन किया


क्या यह संदर्भ C कोड में एक समस्या है ?: if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);निर्देश को कैश नहीं किया गया है, इसलिए कोई भी opcodes नहीं 13 अगले निर्देश को पूर्व-निष्पादित करेगा, नहीं?
लूजर

2

सी, 924 838 825 696 646 623

मैं bनिर्देश में निर्दिष्ट रजिस्टर में एक "पॉइंटर" (बाइट-ऑफ़्स) संग्रहीत करता हूं , और जो भी रजिस्टर pseudocode में एक सरणी को उसी तरीके से उपयोग करता है (या रिवर्स, बल्कि, एक पॉइंटर को फिर से व्यवस्थित करने के लिए) इस सरणी को बाद में एक्सेस करने के लिए। अभी भी परीक्षण कार्यक्रम की कोशिश करने की जरूरत है ...

संपादित करें: गयी टिप्पणियाँ।

संपादित करें: निश्चित निर्देश 12. सूचक को बदलें, न कि निर्देश को स्मृति में। गणना सभी टिप्पणियों, इंडेंट और हटाए गए नए समाचारों के साथ है।

संपादित करें: ऐसा प्रतीत होता है कि अब चल रहा है, मुझे लगता है कि मैं परिणामों की सही व्याख्या कर रहा हूं। :) अंतिम बोध यह था कि 0 सरणी वास्तव में हैंडल 0 द्वारा संदर्भित है , जो कि एक गैर-पंजीकृत रजिस्टर में पाया जा सकता है। एक बहुत छोटी मशीन! :)

संपादित करें:write इसके बजाय उपयोग करने के लिए डिबगिंग तंत्र को फिर से लिखना printf.... बग को हटाने के लिए यहां विचार है । :) संपादित करें: putchar() और getchar()इसके साथ भी नहीं हैं sbrk। यह अब काम करता है, और काफी तेज दिखाई देता है।

#define O(_)*a=*b _*c;B
#define B break;case
#define U unsigned
U*m,r[8],*p,*z,f,x,*a,*b,*c;main(int n,char**v){U char
u[4];z=m=p=sbrk(4);f=n>1?open(v[1],0):0;\
while(read(f,u,4)){*m++=(((((*u<<8)|u[1])<<8)|u[2])<<8)|u[3];sbrk(4);}sbrk(4);\
for(;x=*p++,1;){c=r+(x&7);b=r+((x>>3)&7);a=r+((x>>6)&7);switch(x>>28){case
0:*c?*a=*b:0;B
1:*a=(*b?m+*b:z)[*c];B
2:(*a?m+*a:z)[*b]=*c;B
3:O(+)4:O(*)5:O(/)6:*a=~(*b&*c);B
7:return 0;case
8:*b=1+(U*)sbrk(4*(1+*c))-m;(m+*b)[-1]=*c;B
9:B
10:*u=*c;write(1,u,1);B 
11:read(0,u,1);*c=*u;B
12:*b?memcpy(z=sbrk(4*(m+*b)[-1]),m+*b,4*(m+*b)[-1]):0;p=&z[*c];B
13:a=r+((x>>25)&7);*a=x&0x1ffffff;}}}

केवल छोटे-एंडियन के लिए, एक 611 वर्ण संस्करण है।

#define O(_)*a=*b _*c;B
#define B break;case
#define U unsigned
U*m,r[8],*p,*z,f,x,*a,*b,*c;main(int n,char**v){U char
u[4];z=m=p=sbrk(4);f=n>1?open(v[1],0):0;while(read(f,u,4)){*m++=(((((*u<<8)|u[1])<<8)|u[2])<<8)|u[3];sbrk(4);}sbrk(4);for(;x=*p++,1;){c=r+(x&7);b=r+((x>>3)&7);a=r+((x>>6)&7);switch(x>>28){case
0:*c?*a=*b:0;B
1:*a=(*b?m+*b:z)[*c];B
2:(*a?m+*a:z)[*b]=*c;B
3:O(+)4:O(*)5:O(/)6:*a=~(*b&*c);B
7:return 0;case
8:*b=1+(U*)sbrk(4*(1+*c))-m;(m+*b)[-1]=*c;B
9:B
//10:*u=*c;write(1,u,1);B //generic
10:write(1,c,1);B //little-endian
//11:read(0,u,1);*c=*u;B //generic
11:read(0,c,1);B //little-endian
12:*b?memcpy(z=sbrk(4*(m+*b)[-1]),m+*b,4*(m+*b)[-1]):0;p=&z[*c];B
13:a=r+((x>>25)&7);*a=x&0x1ffffff;}}}

टिप्पणी की (विस्तारित) डिबगिंग तंत्र के साथ टिप्पणी की (विस्तारित)।

//#define DEBUG 1
#include <fcntl.h> // open
#include <signal.h> // signal
#include <stdio.h> // putchar getchar
#include <string.h> // memcpy
#include <sys/types.h> // open
#include <sys/stat.h> // open
#include <unistd.h> // sbrk read
unsigned long r[8],*m,*p,*z,f,x,o,*a,*b,*c; // registers memory pointer zero file working opcode A B C
char alpha[] = "0123456789ABCDEF";
//void S(int x){signal(SIGSEGV,S);sbrk(9);} // autogrow memory while reading program
void writeword(int fd, unsigned long word){
    char buf[8];
    unsigned long m=0xF0000000;
    int off;
    for (off = 28; off >= 0; m>>=4, off-=4) {
        buf[7-(off/4)]=alpha[(word&m)>>off];
    }
    write(fd, buf, 8);
    write(fd, " ", 1);
}
int main(int n,char**v){
#ifdef DEBUG
    int fdlog;
#endif
    unsigned char u[4]; // 4-byte buffer for reading big-endian 32bit words portably
    int cnt;

#ifdef DEBUG
    fdlog = open("sandlog",O_WRONLY|O_CREAT|O_TRUNC, 0777);
#endif
    z=m=p=sbrk(4); // initialize memory and pointer
    //signal(SIGSEGV,S); // invoke autogrowing memory -- no longer needed
    f=n>1?open(v[1],O_RDONLY):0; // open program
    while(read(f,u,4)){ // read 4 bytes
        *m++=(((((*u<<8)|u[1])<<8)|u[2])<<8)|u[3]; // pack 4 bytes into 32bit unsigned in mem
        sbrk(4); // don't snip the end of the program
    }
    sbrk(4);
    for(cnt=0;x=*p++,1;cnt++){ // working = *ptr; ptr+=1
        c=r+(x&7); // interpret C register field
        b=r+((x>>3)&7); // interpret B register field
        a=r+((x>>6)&7); // interpret A register field
#ifdef DEBUG
        {int i;write(fdlog,"{",1);for(i=0;i<8;i++)writeword(fdlog, r[i]);
            write(fdlog,"} ",2);
        }
        write(fdlog, alpha+(x), 1);
        write(fdlog, alpha+(x>>28), 1);
#endif
        switch(o=x>>28){ // interpret opcode
            case 0:
#ifdef DEBUG
                write(fdlog, "if(rX)rX=rX\n", 12);
#endif
                *c?*a=*b:0;
                break; // Conditional Move A=B unless C==0
            case 1:
#ifdef DEBUG
                write(fdlog, "rX=rX[rX]\n", 10);
#endif
                *a=(*b?m+*b:z)[*c];
                break; // Array Index A=B[C]
            case 2:
#ifdef DEBUG
                write(fdlog, "rX[rX]=rX\n", 10);
#endif
                (*a?m+*a:z)[*b]=*c;
                break; // Array Amendment A[B] = C
            case 3:
#ifdef DEBUG
                write(fdlog, "rX=rX+rX\n", 9);
#endif
                *a=*b+*c;
                break; // Addition A = B + C
            case 4:
#ifdef DEBUG
                write(fdlog, "rX=rX*rX\n", 9);
#endif
                *a=*b**c;
                break; // Multiplication A = B * C
            case 5:
#ifdef DEBUG
                write(fdlog, "rX=rX/rX\n", 9);
#endif
                *a=*b/ *c;
                break; // Division A = B / C
            case 6:
#ifdef DEBUG
                write(fdlog, "rX=~(rX&rX)\n", 12);
#endif
                *a=~(*b&*c);
                break; // Not-And A = ~(B & C)
            case 7:
#ifdef DEBUG
                write(fdlog, "halt\n", 5);
#endif
                return 0; // Halt 
            case 8:
#ifdef DEBUG
                write(fdlog, "rX=alloc(rX)\n", 13);
#endif
                *b=1+(unsigned long*)sbrk(4*(1+*c))-m;
                   (m+*b)[-1]=*c;

                   break; // Allocation B = allocate(C)
            case 9:
#ifdef DEBUG
                   write(fdlog, "free(rX)\n", 9);
#endif
                   break; // Abandonment deallocate(C)
            case 10:
#ifdef DEBUG
                   write(fdlog, "output(rX)\n", 11);
#endif
                   //putchar(*c);
                   //*u=u[1]=u[2]=' ';
                   u[3]=(char)*c;
                   write(fileno(stdout), u+3, 1);
                   break; // Output char from C to stdout
            case 11:
#ifdef DEBUG
                   write(fdlog, "rX=input()\n", 11);
#endif
                   //x=getchar();*c=x;
                   read(fileno(stdin), u+3, 1);
                   *c=u[3];
                   break; // Input char from stdin into C
            case 12:
#ifdef DEBUG
                   write(fdlog, "load(rX)[rX]\n", 13);
#endif
                    *b?memcpy(z=sbrk(4*(m+*b)[-1]),m+*b,4*(m+*b)[-1]):0;
                    p=&z[*c];
                    break; // Load Program copy the array B into the 0 array, Ptr=C
            case 13:
#ifdef DEBUG
                    write(fdlog, "rX=X\n", 5);
#endif
                    a=r+((x>>25)&7);*a=x&0x1ffffff; // Orthography REG=immediate-25bit
        }
    }
}

ऐरे हैंडल 100% अपारदर्शी हैं। इससे कोई फर्क नहीं पड़ता कि आप इसे पास करते हैं, एरे को एक्सेस करते समय प्रोग्राम को उसी मान का उपयोग करना चाहिए। पुनश्च मैं सिर्फ यह संकलन की कोशिश की, आप एक जोड़ी शामिल हैं याद आ रही है। पीपीएस क्या आपने कभी इसे संकलित किया? क्या lbreakऔर कैसे आप unary- कर सकते हैं *एकint
mniip

हाँ। थोड़ी बहुत उत्सुकता। :) अद्यतित कोड Cygwin पर gcc के साथ संकलित करता है।
लूजर

@mniip तो यह केवल सरणी 0 है जो "संख्या" द्वारा निर्दिष्ट है?
लूजर

बस इसे संकलित किया, यह केवल सैंडमार्क से 2 निर्देशों को निष्पादित करता है: d000108f c0000030और फिर बाहर निकलता है
mniip

मैंने एक बग तय किया। यह रुकने से पहले अब 7 निर्देशों को निष्पादित करता है।
लूसर डोग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.