Universal Machine emülatörünü uygulayın


13

Amaç, ICFP 2006'dan Universal Machine'i en kısa kodla taklit eden tam bir program yazmaktır. Universal Machine burada açıklanan çok basit bir talimat setine sahiptir . Öykünücü, komut satırı bağımsız değişkeninden bir dosya adı okumalı ve dosyayı program olarak çalıştırmalıdır, böylece dilinizin komut satırı bağımsız değişkenlerini ve stdin / out'ı bir şekilde desteklemesi gerekir. Emülatör kum işaretini makul bir süre içinde tamamlamak zorundadır (onlarca yıl değil). İşte talimat setinin kısa bir açıklaması:

Makinede, her biri 32 bit işaretsiz tamsayı bulunan sekiz kayıt vardır.
Makine, 32 bit işaretsiz tamsayı hücrelerinin dizine dizilmiş dizisini tutar.
Kısaca, ayırma komutu, statik bir boyuta sahip olan ve 32 bit uint öğelerini tutan, oluşturulan dizinin tutamacı olan opak bir 32 bit uint döndürür.
0'ıncı dizi programı gösterir. Başlangıçta big-endian bir dosyadan yüklenir.
Ayrıca, 0 dizisindeki bir hücreye işaret eden bir Talimat İşaretçisi de vardır.
Her adımda, İşaretçinin işaret ettiği hücreden bir talimat okunur ve İşaretçi herhangi bir şey yapılmadan önce teşvik edilir.
En önemli 4 bit opcode'u temsil eder.
Eğer opcode 13 ise, sonraki 3 bit yazmacı ve diğer 25 ise adı geçen yazmacıya yazılan sayıyı temsil eder.
Aksi takdirde, en az önemli 9 bit, A, B ve C olmak üzere üç kaydı temsil eder; burada C, en az önemli 3 bitle temsil edilir.
Sonra opcode'a bağlı olarak aşağıdakiler olur:
0. A = B C == 0
1. A = B [C]
2. A [B] = C
3. A = B + C
4. A = B * C
5. A = B / C
6. A = ~ (B & C)
7. Emülatör
8'den çıkar . B = tahsis (C)
9. deallocate (C)
10. C'den stdout'a bir karakter çıkışı
11. bir karakter girin stdin'den C'ye
12. B dizisini 0 dizisine kopyalayın ve İşaretçiyi C olarak ayarlayın

Makinenin bazı yönlerini yanlış anlarsanız size kesinlikle yardımcı olacak x86_64 jitted derleme (eğlence emit () başlar ) kullanarak gereksiz derecede karmaşık ama tamamen hızlı bir uygulama (ab) yazdım .


Bunun kod golf mü yoksa popülerlik yarışması mı olduğuna karar vermelisiniz. Özeldirler.
Howard

@ Anladım, teşekkürler
mniip

Yanılmıyorsam, makine Little Endian değil, Big Endian olarak tanımlanır.
Hasturkun

@Hasturkun d'oh Bunları her zaman berbat ediyorum, Big
Endian'ın

1
@mniip Big Endian ve Little Endian, Gulliver's Travels'tan ödünç alınan terimlerdir. Lilliput'un küçük halkı Blefuscu'nun küçük halkı ile savaş halindeydi, çünkü Lilliputyalılar önce haşlanmış yumurtanın büyük ucunu yemeniz gerektiğine inanan "Big Endians" ve Blefuscanlar bunun tersine inandılar. Orijinal Gulliver'in Seyahatleri Jonathan swift'in ciddi bir romanıydı. Yazar, siyasi ve dini farklılıklar konusunda savaşa girmenin aptallığı hakkında yorum yapıyordu. Gulliver savaşta yardım etmeyi reddettiği için ihanetle suçlandıktan sonra ayrılmak zorunda kaldı.
Level River St

Yanıtlar:


6

PHP: 443 416384  bayt

<?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",'])));

* Yeniden yenilendi *. Şimdi alabildiğim kadar küçük. Alfabenin uzak ucunda bazı değişkenler tuttum, böylece $ işaretlerini ekleyen normal ifade STDIN sabitini değiştirmez, bu yüzden küçük bir sözlük:

  • U: talimat işaretçisi
  • V: şu anda test edilen opcode dizini
  • W: geçerli talimat kelimesi
  • X: 8 genel amaçlı kayıt
  • Y: ana bellek (her blok 1 tabanlıdır, çünkü unpack()diziler bu şekilde döner)
  • Z: bir sonraki boş bellek bloğunun kimliği (sonunda taşacak, ancak kum işareti sadece ~ 92 milyon kullanıyor)
  • A, B, C, spesifikasyondaki gibi mevcut talimatın kayıtlarıdır.

İmzasız bölüm ince bir sıkıntıdır ( *1büyük sayıların doğru int'e geri dönmesini sağlamak için gereklidir), ancak aritmetik işlemin geri kalanının her komuttan A|=0sonra 0 ( ) ile aritmetik yazıyı OR ( ) kullanarak 32 bit tutması kolaydır .


Bu projeyi gerçekten ilginç buldum, ancak karakter sayısını en aza indirgemek yavaş ve elverişsiz hale geldi, bu yüzden tüm gün yerine kum işaretini birkaç dakika içinde tamamlayabilen basit bir (golf değil) Java sürümü yaptım:

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;
                }
            }
        }
    }
}

Bölme sonucunu 32 bit olarak ayarlamanız gerektiğini düşünmüyorum çünkü her zaman önceden ayarlanmış olan temettüden daha küçük veya eşittir
mniip

Sadece meraktan kurtulmuş gibi görünüyor?
Tim Seguine

@mniip Şimdi biraz farklı bir şekilde düzenlenmiş, ancak bölünme konusunda dikkatli olmam gerekiyor çünkü bölünme sırasında sayılar imzasız ve diğer her an imzalanıyorlar.
Boann

3

Perl, 407

Soru çok karmaşık görünebilir, aslında çok basit.
Yine de perl için hala çok yeniyim, yine de burada

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].";";}

Gerçekten yavaş çalışıyor, muhtemelen JITed x86_64 olandan 800 kat daha yavaş.
Ayrıca, bir arkadaşım referans C uygulaması yaptı


Bu referans C kodunda bir sorun mu var ?: if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);talimat önbelleğe alınmaz, bu nedenle 13 olmayan herhangi bir opcodes sonraki talimatı önceden yürütür, değil mi?
luser droog

2

C 924 838 825 696 646 623

bTalimatta belirtilen kayıt defterinde bir "işaretçi" (bayt-offset) depolar ve bu diziye daha sonra erişmek için sözde kodda bir diziyi aynı şekilde (ya da bir işaretçiyi yeniden oluşturmak için tersine) atadığı herhangi bir kaydı kullanır. Yine de test programını denemeniz gerekiyor ...

Düzenle: yorum eklendi.

Düzenleme: sabit talimat 12. bellekteki talimatı değil, işaretçiyi değiştirin. Sayım, tüm yorumlar, girintiler ve yeni satırlar kaldırılmış durumda.

Düzenleme: Sonuçları doğru yorumladığımı varsayarak şimdi çalışıyor gibi görünüyor. :) Son gerçekleştirme, 0 dizisine gerçekten de başlatılmamış bir kayıtta bulunabilen tutamaç 0 tarafından atıfta bulunulmasıydı . Çok bükülmüş küçük bir makine! :)

Düzenleme: hata ayıklama aygıtı writeyerine kullanmak için yeniden yazdı printf.... Burada fikir hataları kaldırmaktır . :) Edit: putchar() ve getchar()ayrıca no-nos vardır sbrk. Şimdi çalışıyor ve oldukça hızlı görünüyor.

#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;}}}

Sadece küçük endianlar için 611 karakterlik bir versiyon var.

#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;}}}

Girintili ve yorumlu (uzatılmış) yorumlu hata ayıklama cihazı ile.

//#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
        }
    }
}

Dizi kolları% 100 opaktır. Ne iletirseniz iletin, programın dizilere erişirken aynı değeri kullanması gerekir. PS ben sadece derleme çalıştı, eksik bir çift içerir. PPS hiç derledin mi? ne lbreakve nasıl unary- *anint
mniip

Evet. Biraz fazla hevesli. :) Cygwin'de güncellenmiş kod gcc ile derleniyor.
luser droog

@mniip Yani "number" ile gösterilen sadece 0 dizisi mi?
luser droog

sadece derledi, sadece kum işareti dışında 2 talimatlar yürütür: d000108f c0000030ve sonra çıkar
mniip

Bir hatayı düzelttim. Durmadan önce 7 talimatı uygular.
luser droog
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.