Bytecode ล่ามขนาดเล็กที่สุด / VM


17

กระดานผู้นำ - รวบรวม JIT (ด้านล่างดีกว่า)

  1. es1024 - 81.2 คะแนน (รวมถึงคอมไพเลอร์ที่ใช้งานได้!)
  2. Kieth Randall - 116 คะแนน
  3. Ell - 121 คะแนน

กระดานผู้นำ - ตีความ (ด้านล่างดีกว่า)

  1. Martin Büttner - 706654 คะแนน (ประมาณ 2 ชั่วโมง)
  2. criptych - 30379 คะแนน (97 วินาที)

ภารกิจของคุณคุณควรเลือกที่จะยอมรับมันคือการเขียน bytecode interpreter / VM ที่เล็กที่สุดที่เป็นไปได้ VM / interpreter ใช้สถาปัตยกรรม CISC ขนาดเล็ก (การดำเนินการอาจแตกต่างกันในขนาด) ด้วยภาษาที่ระบุด้านล่าง เมื่อเสร็จสิ้นคุณจะต้องพิมพ์ค่าของ CPU 3 ตัวเพื่อลงทะเบียนเพื่อพิสูจน์ว่ามีการพิมพ์เอาต์พุตที่ถูกต้อง (3,126,900,366)

ผู้รวบรวม

หากคุณต้องการทำการทดสอบของคุณเองคอมไพเลอร์จะโพสต์ด้านล่าง อย่าลังเลที่จะโพสต์การทดสอบของคุณด้วยคำตอบของคุณ

ข้อมูลจำเพาะ "VM"

VM มีการลงทะเบียนหนึ่งตัวที่ไม่ได้ลงนาม 3 32 บิต: R0, R1, R2 พวกเขาจะแสดงในรูปแบบเลขฐานสิบหกเป็น 0x00, 0x01 และ 0x02

การดำเนินการต่อไปนี้ต้องได้รับการสนับสนุน:

รูปแบบคือ [ชื่อ] [... ตัวถูกดำเนินการ ... ], [รหัสเลขฐานสิบหก] [... ตัวถูกดำเนินการซ้ำ ... ]

  • โหลด [ลงทะเบียน] [ค่า 4 ไบต์], 0x00 [ลงทะเบียน] [ค่า 4 ไบต์]
  • ผลัก [ลงทะเบียน], 0x02 [ลงทะเบียน]
  • POP [ลงทะเบียน], 0x03 [ลงทะเบียน]
  • เพิ่ม [ลงทะเบียน 1 ไบต์] [ลงทะเบียน 1 ไบต์], 0x04 [ลงทะเบียน] [ลงทะเบียน]
  • SUB [ลงทะเบียน 1 ไบต์] [ลงทะเบียน 1 ไบต์], 0x05 [ลงทะเบียน] [ลงทะเบียน]
  • MUL [ลงทะเบียน 1 ไบต์] [ลงทะเบียน 1 ไบต์], 0x06 [ลงทะเบียน] [ลงทะเบียน]
  • DIV [register, 1 byte] [register, 1 byte], 0x07 [register] [register]
  • JMP [รหัสบรรทัด 4 ไบต์], 0x08 [หมายเลขบรรทัดรหัส 4 ไบต์]
  • CMP [register, 1 byte] [register, 1 byte], 0x09 [register] [register]
  • BRANCHLT [รหัสบรรทัด 4 ไบต์], 0x0a [หมายเลขบรรทัดรหัส 4 ไบต์]

หมายเหตุบางส่วน:

  • การดำเนินการทางคณิตศาสตร์ข้างต้นเพิ่มค่าของการลงทะเบียน 2 ตัวพร้อมกันโดยวางผลลัพธ์ในการลงทะเบียนครั้งแรก
  • CMP ตัวดำเนินการเปรียบเทียบควรเปรียบเทียบค่าของ 2 รีจิสเตอร์และเก็บเอาท์พุทในแฟลกภายในบางตัว
  • หาก BRANCH ถูกเรียกใช้ก่อน CMP เว้นแต่ว่า BRANCHEQ จะถูกเรียกใช้ "VM" ไม่ควรแยกสาขา
  • ผลักดัน / ป๊อปผลักดันหรือป๊อปตัวเลขจากสแต็คอย่างแปลกใจ
  • ตัวดำเนินการกระโดดและสาขาข้ามไปที่การดำเนินการเฉพาะ (บรรทัดของรหัส) ไม่ใช่ที่อยู่ไบนารี
  • การดำเนินการสาขาไม่ทำการเปรียบเทียบ ค่อนข้างพวกเขาจะเอาท์พุทจากการเปรียบเทียบล่าสุดเพื่อดำเนินการ
  • ตัวดำเนินการสาขาและตัวกระโดดใช้ระบบการจัดทำดัชนีหมายเลขบรรทัดตามศูนย์ (เช่น JMP 0 กระโดดไปที่บรรทัดแรก)
  • การดำเนินการทั้งหมดจะต้องดำเนินการกับตัวเลขที่ไม่ได้ลงนามซึ่งล้นเป็นศูนย์และไม่ส่งข้อยกเว้นในการล้นจำนวนเต็ม
  • ไม่อนุญาตให้หารด้วยศูนย์และไม่อนุญาตให้กำหนดพฤติกรรมของโปรแกรม คุณสามารถ (ตัวอย่าง) ...
    • ขัดข้องของโปรแกรม
    • สิ้นสุดการดำเนินการของ VM และกลับสู่สถานะปัจจุบัน
    • แสดงข้อความ "ERR: Division by 0"
  • การสิ้นสุดของโปรแกรมถูกกำหนดเป็นเมื่อตัวชี้คำสั่งถึงจุดสิ้นสุดของโปรแกรม (สามารถสันนิษฐานว่าไม่ใช่โปรแกรมว่าง)

เอาท์พุทเอาท์พุ ทจะต้องตรงนี้ (รวมบรรทัดใหม่)

R0 3126900366
R1 0
R2 10000    

คะแนน สะสมคำนวณจากสูตรต่อไปนี้:Number Of Characters * (Seconds Needed To Run / 2)

เพื่อหลีกเลี่ยงความแตกต่างของฮาร์ดแวร์ที่ทำให้เกิดเวลาที่ต่างกันการทดสอบแต่ละครั้งจะถูกเรียกใช้บนคอมพิวเตอร์ของฉัน (i5-4210u, 8GB ram) ในเซิร์ฟเวอร์ ubuntu หรือ Windows 8 ดังนั้นอย่าพยายามใช้ runtime บ้าๆที่คอมไพล์ใน Dual G5 เท่านั้น Mac Pro ที่มี RAM ฟรี 762.66 mb

หากคุณใช้ runtime / ภาษาเฉพาะกรุณาโพสต์ลิงค์

  • สำหรับผู้ที่สนใจฉันได้โพสต์รหัสทดสอบ (เขียนเป็นภาษา C #) ที่นี่: http://pastebin.com/WYCG5Uqu

โปรแกรมทดสอบ

แนวคิดมาจากที่นี่ดังนั้นเราจะใช้โปรแกรมที่มีการแก้ไขบ้าง

ผลลัพธ์ที่ถูกต้องสำหรับโปรแกรมคือ: 3,126,900,366

ใน C:

int s, i, j;
for (s = 0, i = 0; i < 10000; i++) {
    for (j = 0; j < 10000; j++)
        s += (i * j) / 3;
}

ในรหัส: [R0 เป็นตัวแทนของ s, R1 ของ j, R2 ของ i]

LOAD R0 0
LOAD R2 0 <--outer loop value
LOAD R1 0 <--inner loop value
     --Begin inner loop--
PUSH R1 <--push inner loop value to the stack
MUL R1 R2 <--(i*j)
PUSH R2
LOAD R2 3
DIV R1 R2 <-- / 3
POP R2
ADD R0 R1 <-- s+=
POP R1
PUSH R2 
LOAD R2 1
ADD R1 R2 <--j++
POP R2
PUSH R2
LOAD R2 10000
CMP R1 R2 <-- j < 10000
POP R2
BRANCHLT 3 <--Go back to beginning inner loop
--Drop To outer loop--
LOAD R1 1
ADD R2 R1 <--i++
LOAD R1 10000
CMP R2 R1 <-- i < 10000
LOAD R1 0 <--Reset inner loop
BRANCHLT 2

ในไบนารี / ฐานสิบหก:

0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x02 0x00 0x00 0x00 0x00
0x00 0x01 0x00 0x00 0x00 0x00
0x02 0x01
0x06 0x01 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x03
0x07 0x01 0x02
0x03 0x02
0x04 0x00 0x01
0x03 0x01
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x01
0x04 0x01 0x02
0x03 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x27 0x10
0x09 0x01 0x02
0x03 0x02
0x0a 0x00 0x00 0x00 0x03
0x00 0x01 0x00 0x00 0x00 0x01
0x04 0x02 0x01
0x00 0x01 0x00 0x00 0x27 0x10
0x09 0x02 0x01
0x00 0x01 0x00 0x00 0x00 0x00
0x0a 0x00 0x00 0x00 0x02

คะแนนโบนัส (เอฟเฟ็กต์ถูกนำไปใช้คูณ) เช่นถ้าคุณมีคุณสมบัติครบทั้งสามจะเป็น ((ตัวละคร * 0.50) * 0.75) * 0.90

  • ลดลง 50% หากล่ามนั้นเป็นคอมไพเลอร์ JIT
  • ลดลง 25% หากใช้การเพิ่มประสิทธิภาพแบบวนซ้ำใด ๆ / การเพิ่มประสิทธิภาพที่มีความหมาย
  • ลดลง 10% หากคุณขยาย VM ด้วย
    • BRANCHEQ [รหัสบรรทัด 4 ไบต์] (สาขาถ้าเท่ากัน - opcode 0x0b)
    • BRANCHGT [รหัสบรรทัด 4 ไบต์] (สาขาถ้ามากกว่า - opcode 0x0c)
    • BRANCHNE [รหัสบรรทัด 4 ไบต์] (สาขาถ้าไม่เท่ากัน - opcode 0x0d)
    • ดาวน์โหลด [ลงทะเบียน 1] [ลงทะเบียน 2] (ย้ายค่าลงทะเบียน 2 เพื่อลงทะเบียน 1 - opcode 0x01)

ไม่ได้รับอนุญาต

  • การคอมไพล์เคสการทดสอบล่วงหน้าในโปรแกรมเป็นสิ่งต้องห้าม คุณต้องยอมรับ bytecode จาก STDIN หรือจากไฟล์ (ไม่สำคัญว่าจะต้องทำอะไร)
  • การส่งคืนเอาต์พุตโดยไม่ต้องรันโปรแกรม
  • วิธีอื่นที่คุณสามารถคิดได้ว่าจะโกงข้อกำหนดของ VM

ทำไมไม่รวมโปรแกรมทดสอบอีกสองสามรายการเพื่อกีดกันสิ่งที่คุณพูดว่าไม่ได้รับอนุญาต ถ้าเป็น VM มันควรจะสามารถรันอะไรก็ได้ที่เขียนในข้อกำหนดของ bytecode ใช่ไหม?
Kasran

ฉันจะพยายามทำในคืนนี้ ฉันกำลังเขียนคอมไพเลอร์ตอนนี้
มีสีสันขาวดำ

คอมไพเลอร์อยู่ที่นี่jsfiddle.net/aL9y19bd/23
Monochrome ที่มีสีสัน

1
ไม่CMPตรวจสอบน้อยกว่าหรือเท่าเทียมกัน? และเกิดอะไรขึ้นกับผลลัพธ์ของมัน
es1024

1
MULและDIVยังไม่ระบุโดยละเอียด พวกเขาควรจะลงนามหรือไม่ได้ลงนาม? เกิดอะไรขึ้นกับการคูณล้น?
feersum

คำตอบ:


8

C, 752 (589 + 163 สำหรับกำหนดธง) * 0.5 (JIT) * 0.9 (ส่วนขยาย) * (การเพิ่มประสิทธิภาพ 0.75) * (0.64 วินาที / 2) = 81.216

C[S],J[S],i,j,k,y,c,X,Y,Z;char*R,a,b[9];main(x){R=mmap(0,S,6,34,-1,0);N=85;while(scanf("%c%s%*[ R]%d%*[ R]%d",&a,b,&x,&y)&&~getchar())a-=65,a-1?a-15?a-9?a?a-2?a-3?a-11?a-12?a-17?(N=41,v):(N=137,v):(N=137,u,N=247,g(H,4),N=139,u):(y?N=189+x,s(y):(N=51,g(G,G))):(N=137,u,N=247,g(H,6),N=139,u):(N=57,v,s(0xFC8A9F),--j):(N=1,v):(N=233,J[k++]=i,s(x)):b[1]-80?N=85+x:(N=93+x):(c=b[5],s(0x0F9EE78A),N=(c-69?c-71?c-76?1:8:11:0)+132,J[k++]=i,s(x)),C[++i]=j;U(E8,X)U(F0,Y)U(F8,Z)s(50013);i=j;while(k--)j=C[J[k]]+1,R[j-1]-233&&(j+=4),s(C[*(int*)(R+j)]-j-4);((int(*)())R)();printf("%u %u %u\n",X,Y,Z);}

รับโค้ด ( LOAD R0, ฯลฯ ), ไม่มีตัวอักษรต่อท้าย, เว้นวรรคเดียว, ไม่มีบรรทัดว่างตรงกลาง, ไม่แสดงความคิดเห็น, ฯลฯ ต้องขึ้นบรรทัดใหม่

นี่จะถูกแปลงเป็น 80386 bytecode และดำเนินการ

โหลด0ลงในทะเบียนจะถูกแทนที่โดยxorไอเอ็นจีลงทะเบียนด้วยตัวเองแทนmovไอเอ็นจี0เข้ามาลงทะเบียนซึ่งเป็นสามไบต์สั้นใน bytecode สร้างขึ้นและอาจจะมากได้เร็วขึ้นเล็กน้อย

รวบรวมกับ:

gcc -m32 -D"g(a,b)=(N=192|b<<3|a)"-D"s(b)=(*(int*)(R+j)=b,j+=4)"-DN=R[j++]-D"G=((x+1)|4)"
-D"H=((y+1)|4)"-DS=9999-D"u=g(0,G)"-D"v=g(G,H)"-D"U(b,c)=s(0xA3##b##89),--j,s(&c);"
bytecode.c -o bytecode

ต้องใช้ระบบปฏิบัติการที่สอดคล้องกับ POSIX

อินพุตถูกอ่านจาก STDIN (ใช้./bytecode < fileเพื่อไพพ์จากไฟล์)

bytecode ที่เป็นผลลัพธ์สำหรับโปรแกรมทดสอบ:

; start
 0:   55                      push   %ebp
; LOAD R0 0
 1:   33 ed                   xor    %ebp,%ebp
; LOAD R2 0
 3:   33 ff                   xor    %edi,%edi
; LOAD R1 0
 5:   33 f6                   xor    %esi,%esi
; PUSH $1
 7:   56                      push   %esi
; MUL R1 R2
 8:   89 f0                   mov    %esi,%eax
 a:   f7 e7                   mul    %edi
 c:   8b f0                   mov    %eax,%esi
; PUSH R2
 e:   57                      push   %edi
; LOAD R2 3
 f:   bf 03 00 00 00          mov    $0x3,%edi
; DIV R1 R2
14:   89 f0                   mov    %esi,%eax
16:   f7 f7                   div    %edi
18:   8b f0                   mov    %eax,%esi
; POP R2
1a:   5f                      pop    %edi
; ADD R0 R1
1b:   01 f5                   add    %esi,%ebp
; POP R1
1d:   5e                      pop    %esi
; PUSH R2
1e:   57                      push   %edi
; LOAD R2 1
1f:   bf 01 00 00 00          mov    $0x1,%edi
; ADD R1 R2
24:   01 fe                   add    %edi,%esi
; POP R2
26:   5f                      pop    %edi
; PUSH R2
27:   57                      push   %edi
; LOAD R2 10000
28:   bf 10 27 00 00          mov    $0x2710,%ed
; CMP R1 R2
2d:   39 fe                   cmp    %edi,%esi
2f:   9f                      lahf
30:   8a fc                   mov    %ah,%bh
; POP R2
32:   5f                      pop    %edi
; BRANCHLT 3
33:   8a e7                   mov    %bh,%ah
35:   9e                      sahf
36:   0f 8c cb ff ff ff       jl     0x7
; LOAD R1 1
3c:   be 01 00 00 00          mov    $0x1,%esi
; ADD R2 R1
41:   01 f7                   add    %esi,%edi
; LOAD R1 10000
43:   be 10 27 00 00          mov    $0x2710,%es
; CMP R2 R1
48:   39 f7                   cmp    %esi,%edi
4a:   9f                      lahf
4b:   8a fc                   mov    %ah,%bh
; LOAD R1 0
4d:   33 f6                   xor    %esi,%esi
; BRANCHLT 2
4f:   8a e7                   mov    %bh,%ah
51:   9e                      sahf
52:   0f 8c ad ff ff ff       jl     0x5
; copy R0 to X
58:   89 e8                   mov    %ebp,%eax
5a:   a3 28 5b 42 00          mov    %eax,0x425b
; copy R1 to Y
5f:   89 f0                   mov    %esi,%eax
61:   a3 38 55 44 00          mov    %eax,0x4455
; copy R2 to Z
66:   89 f8                   mov    %edi,%eax
68:   a3 40 55 44 00          mov    %eax,0x4455
; exit
6d:   5d                      pop    %ebp
6e:   c3                      ret

Ungolfed:

C[9999],J[9999],i,j,k,y,c,X,Y,Z;
char *R,a,b[9];
main(x){
    // 6 is PROC_WRITE|PROC_EXEC
    // 34 is MAP_ANON|MAP_PRIVATE
    R=mmap(0,'~~',6,34,-1,0);

    N=0x55;
    while(scanf("%c%s%*[ R]%d%*[ R]%d",&a,b,&x,&y)&&~getchar())
        a-=65,
        a-1? // B[RANCH**]
            a-15? // P[USH/OP]
                a-9? // J[MP]
                    a? // A[DD]
                        a-2? // C[MP]
                            a-3? // D[IV]
                                a-11? // L[OAD]
                                    a-12? // M[UL]
                                        a-17? // R[LOAD]
                                            // SUB
                                            (N=0x29,g(G,H))
                                        :(N=0x89,g(G,H))
                                    :(N=0x89,g(0,G),N=0xF7,g(H,4),N=0x8B,g(0,G))
                                :(y?N=0xBD+x,s(y):(N=0x33,g(G,G)))
                            :(N=0x89,g(0,G),N=0xF7,g(H,6),N=0x8B,g(0,G))
                        :(N=0x39,g(G,H),s(0xfc8a9f),--j)
                    :(N=0x1,g(G,H))
                :(N=0xE9,J[k++]=i,s(x))
            :b[1]-80? 
                N=0x55+x // PUSH
            :(N=0x5D+x) // POP
        :(c=b[5],s(0x0f9ee78a),N=(
        c-69? // EQ
            c-71? // GT
                c-76? // LT
                    1 // NE
                :8
            :11
        :0
        )+0x84,J[k++]=i,s(x)),
        C[++i]=j
        ;
    // transfer registers to X,Y,Z
    s(0xA3E889),--j,s(&X);
    s(0xA3F089),--j,s(&Y);
    s(0xA3F889),--j,s(&Z);

    // pop and ret
    s(0xC35D);

    i=j;
    // fix distances for jmp/branch**
    while(k--)
        j=C[J[k]]+1,R[j-1]-0xE9&&(j+=4),
        s(C[*(int*)(R+j)]-j-4);

    // call
    ((int(*)())R)();

    // output
    printf("%u %u %u\n",X,Y,Z);
}

ว้าว. ฉันหวังว่าฉันจะได้เพิ่มโบนัสสำหรับการรวมคอมไพเลอร์ใน VM
มีสีสันขาวดำ

เฉลี่ย 0.67 วินาทีต่อการวิ่งมากกว่า 15 ครั้ง
มีสีสันขาวดำ

ฉันไม่เห็นด้วยที่ xoring เป็นการปรับให้เหมาะสม ในขณะที่ขนาดรหัสฉลาดฉลาด, xoring ไม่เปลี่ยนลักษณะการทำงานของ VM (แก้ไขฉันถ้าฉันผิด) สิ่งที่ฉันหมายถึงการปรับให้เหมาะสมคือการเปลี่ยนหรือลบคำแนะนำจากรหัสอินพุต (เช่นการลบ POP ที่ซ้ำซ้อน ... PUSH) หรือการรับรู้ 2 คำสั่งในแถวโหลดรีจิสเตอร์เพื่อให้สามารถลบออกได้
สีสันขาวดำ

แก้ไข: ที่จริงแล้วนั่นคือการเพิ่มประสิทธิภาพ: มันลดลงถึง 0.64 วินาทีต่อการวิ่งมากกว่า 15 ครั้ง ฉันเดาว่าจะป้องกันการหวดแคชหรือบางสิ่งบางอย่างโดยย่อรหัส (หรือลบการเข้าถึงหน่วยความจำซ้ำซ้อน) หรือไม่
ขาวดำที่มีสีสัน

@ColorfullyMonochrome สถาปัตยกรรมบางอย่างเมื่อแสดงด้วย xoring ของการลงทะเบียนกับตัวเองจะไม่ได้ดำเนินการตามคำสั่งจริง แต่เพียงลงทะเบียนตัวเองเป็นศูนย์
es1024

7

C, คะแนน = 854 ไบต์ × (~ 0.8 วินาที / 2) × 0.5 [JIT] × 0.9 [ส่วนขยาย] = ~ 154 ไบต์

#define G getchar()
#define L for(i=0;i<3;++i)
#define N*(int*)
#define M(x)"P\x8a\xe7\x9e\xf"#x"    KL"
*T[1<<20],**t=T,*F[1<<20],**f=F,R[3],r[]={1,6,7};char*I[]={"L\xb8    GGJH","I\x8b\xc0HHGH","H\x50GG","H\x58GG","I\3\xc0HHGH","I\53\xc0HHGH","M\x8b\xc0\xf7\xe0\x8b\xc0IHLGJ","O\63\xd2\x8b\xc0\xf7\xf0\x8b\xc0IJNGL","L\xe9    KH","L\73\xc0\x9f\x8a\xfcHHGH",M(\x82),M(\x84),M(\x87),M(\x85)},C[1<<24],*c=C;main(i,o,l,g){N c=0xb7ec8b60;c[4]=70;c+=5;while((o=G)>=0){char*s=I[o];l=*s-'G';memcpy(c,s+1,l);for(s+=l+1;o=*s++;){o-='G';if(o<3){g=r[G];c[*s++-'G']|=g<<3*(o&1);if(o>1)c[*s++-'G']|=g<<3;}else{if(o>3)*f++=c+*s-'G';for(i=4;i;--i)c[*s-'G'+i-1]=G;++s;}}*t++=c;c+=l;}*t=c;while(f>F)--f,**f=(int)T[**f]-(int)*f-4;L N&c[7*i]=0x5893e|r[i]<<19,N&c[3+7*i]=R+i;N&c[21]=0xc361e58b;mprotect((int)C>>12<<12,1<<24,7);((void(*)())C)();L printf("R%d %u\n",i,R[i]);}

คอมไพล์ด้วยgcc vm.c -ovm -m32 -wบน x86 POSIX ระบบปฏิบัติการ
เรียกใช้ด้วย./vm < programซึ่งprogramเป็นไฟล์โปรแกรมไบนารี


ไปเพื่อความเร็ว โปรแกรมดำเนินการแปลตรงไปตรงมาสวยของโปรแกรมอินพุตเป็นรหัสเครื่อง x86 และช่วยให้ CPU ทำส่วนที่เหลือ

ตัวอย่างเช่นนี่คือคำแปลของโปรแกรมทดสอบ ecx, esiและediสอดคล้องกับR0, R1และR2ตามลำดับ; bhถือธงสถานะ; eaxและedxลงทะเบียนเป็นรอยขีดข่วน; call-stack สอดคล้องกับ stack ของ VM:

# Prologue
     0:   60                      pusha
     1:   8b ec                   mov    ebp,esp
     3:   b7 46                   mov    bh,0x46
# LOAD R0 0
     5:   b9 00 00 00 00          mov    ecx,0x0
# LOAD R2 0 <--outer loop value
     a:   bf 00 00 00 00          mov    edi,0x0
# LOAD R1 0 <--inner loop value
     f:   be 00 00 00 00          mov    esi,0x0
#      --Begin inner loop--
# PUSH R1 <--push inner loop value to the stack
    14:   56                      push   esi
# MUL R1 R2 <--(i*j)
    15:   8b c6                   mov    eax,esi
    15:   f7 e7                   mul    edi
    19:   8b f0                   mov    esi,eax
# PUSH R2
    1b:   57                      push   edi
# LOAD R2 3
    1c:   bf 03 00 00 00          mov    edi,0x3
# DIV R1 R2 <-- / 3
    21:   33 d2                   xor    edx,edx
    23:   8b c6                   mov    eax,esi
    25:   f7 f7                   div    edi
    27:   8b f0                   mov    esi,eax
# POP R2
    29:   5f                      pop    edi
# ADD R0 R1 <-- s+=
    2a:   03 ce                   add    ecx,esi
# POP R1
    2c:   5e                      pop    esi
# PUSH R2
    2d:   57                      push   edi
# LOAD R2 1
    2e:   bf 01 00 00 00          mov    edi,0x1
# ADD R1 R2 <--j++
    33:   03 f7                   add    esi,edi
# POP R2
    35:   5f                      pop    edi
# PUSH R2
    36:   57                      push   edi
# LOAD R2 10000
    37:   bf 10 27 00 00          mov    edi,0x2710
# CMP R1 R2 <-- j < 10000
    3c:   3b f7                   cmp    esi,edi
    3e:   9f                      lahf
    3f:   8a fc                   mov    bh,ah
# POP R2
    41:   5f                      pop    edi
# BRANCHLT 4 <--Go back to beginning inner loop
    42:   8a e7                   mov    ah,bh
    44:   9e                      sahf
    45:   0f 82 c9 ff ff ff       jb     0x14
# --Drop To outer loop--
# LOAD R1 1
    4b:   be 01 00 00 00          mov    esi,0x1
# ADD R2 R1 <--i++
    50:   03 fe                   add    edi,esi
# LOAD R1 10000
    52:   be 10 27 00 00          mov    esi,0x2710
# CMP R2 R1 <-- i < 10000
    57:   3b fe                   cmp    edi,esi
    59:   9f                      lahf
    5a:   8a fc                   mov    bh,ah
# LOAD R1 0 <--Reset inner loop
    5c:   be 00 00 00 00          mov    esi,0x0
# BRANCHLT 3
    61:   8a e7                   mov    ah,bh
    63:   9e                      sahf
    64:   0f 82 a5 ff ff ff       jb     0xf
# Epilogue
    6a:   3e 89 0d 60 ac 04 09    mov    DWORD PTR ds:0x904ac60,ecx
    71:   3e 89 35 64 ac 04 09    mov    DWORD PTR ds:0x904ac64,esi
    78:   3e 89 3d 68 ac 04 09    mov    DWORD PTR ds:0x904ac68,edi
    7f:   8b e5                   mov    esp,ebp
    81:   61                      popa
    82:   c3                      ret

Ungolfed


ว้าว ... JIT ของฉันมีรหัส ~ 900 บรรทัด (เขียนเป็น c ++) ...
Monochrome ที่มีสีสัน

เฉลี่ย 0.63 วินาทีต่อการรัน 15 ครั้ง
มีสีสันขาวดำ

2

CJam, 222 187 185 ไบต์ * (ช้าเกินไป / 2)

ฉันแค่อยากจะเห็นว่าฉันจะได้รับ bytecode VM สั้น ๆ แค่ไหนโดยเขียนลงใน CJam ดูเหมือนว่าจะน้อยกว่า 200 ไบต์ มันช้ามากเลยเพราะ CJam ตีความตัวเอง ใช้เวลานานในการรันโปรแกรมทดสอบ

304402480 6b:P;q:iD-);{(_P=@/(\L*@@+\}h;]:P;TTT]:R;{_Rf=~}:Q;{4G#%R@0=@t:R;}:O;{TP=("R\(\GG*bt:R;  ~R= R\~@t:R; Q+O Q4G#+-O Q*O Q/O ~(:T; Rf=~-:U; GG*bU0<{(:T}*;"S/=~T):TP,<}g3,{'R\_S\R=N}/

หากต้องการเรียกใช้ให้ดาวน์โหลดตัวแปล Java ที่ลิงก์ sourceforge นี้บันทึกรหัสในvm.cjamและเรียกใช้ด้วย

java -jar cjam-0.6.2.jar vm.cjam

โปรแกรมคาดว่า bytecode บน STDIN ฉันยังไม่พบวิธีที่จะส่งข้อมูลแบบไบนารี่ไปยังโปรแกรมโดยที่ไม่มี PowerShell เพิ่มตัวแบ่งบรรทัดต่อท้ายและการแปลง0x0aไป0x0d 0x0aซึ่งเป็นที่น่ารำคาญจริงๆ รหัสประกอบด้วย 4 ไบต์เพื่อแก้ไขปัญหานั้น ( D-);) ซึ่งฉันไม่ได้รวมอยู่ในการนับทั้งหมดเนื่องจากไม่ใช่สิ่งที่โปรแกรมควรทำถ้าได้รับ bytecode เองบน STDIN แทนที่จะเป็นรุ่นที่เข้ารหัสอย่างแปลก ๆ . หากมีคนรู้วิธีการแก้ไขโปรดแจ้งให้เราทราบ

ungolfed เล็กน้อย:

304402480 6b:P; "Create lookup table for instruction sizes. Store in P.";
q:i             "Read program and convert bytes to integers.";
D-);            "Remove spurious carriage returns. This shouldn't be necessary.";
{(_P=@/(\L*@@+\}h;]:P; "Split into instructions. Store in P.";
"We'll use T for the instruction pointer as it's initialised to 0.";
"Likewise, we'll use U for the CMP flag.";
TTT]:R; "Store [0 0 0] in R for the registers.";
{_Rf=~}:Q; "Register lookup block.";
{4G#%R@0=@t:R;}:O; "Save in register block.";
{TP=("R\(\GG*bt:R;

~R=
R\~@t:R;
Q+O
Q4G#+-O
Q*O
Q/O
~(:T;
Rf=~-:U;
GG*bU0<{(:T}*;"N/=~T):TP,<}g "Run program.";
3,{'R\_S\R=N}/

ฉันจะเพิ่มคำอธิบายที่เหมาะสมในวันพรุ่งนี้

กล่าวโดยย่อคือฉันเก็บรีจิสเตอร์ทั้งหมดตัวชี้คำสั่งและแฟล็กการเปรียบเทียบในตัวแปรเพื่อให้ฉันสามารถใช้สแต็กของ CJam ให้เป็นสแต็คของ VM ได้ฟรี


ขอให้เรายังคงอภิปรายนี้ในการแชท
Martin Ender

1
เฉลี่ย 15.279 วินาทีสำหรับการวนซ้ำ 20 ครั้ง - การทดสอบ 15 ครั้ง นั่นหมายถึง 2.12208333 ชั่วโมงต่อการทดสอบ
Monochrome ที่มีสีสัน

1

python / c ++, คะแนน = 56.66

1435 ตัวอักษร * .234 / 2 วินาที * .5 [JIT] * .75 [การเพิ่มประสิทธิภาพ] * .90 [คำแนะนำเพิ่มเติม]

คอมไพล์โปรแกรมอินพุตเป็น c ++, รัน gcc จากนั้นรันผลลัพธ์ ส่วนใหญ่ใช้เวลาภายใน gcc

การปรับให้เหมาะสมอย่างหนึ่งที่ฉันทำคือลดการดำเนินการสแต็กให้เป็นตัวแปรที่ชัดเจนหากได้รับอนุญาตให้มีความหมาย มันช่วยได้มากขึ้นประมาณ 10 เท่ารันไทม์ของโค้ดที่คอมไพล์ได้ดีขึ้น (ประมาณ. 056 วินาทีเพื่อรันไบนารี่ที่ได้จริง) ฉันไม่แน่ใจว่าสิ่งที่ gcc กำลังทำอยู่นั้นทำให้คุณได้รับการปรับปรุง แต่ก็ดี

import sys,os
x=map(ord,sys.stdin.read())
w=lambda x:(x[0]<<24)+(x[1]<<16)+(x[2]<<8)+x[3]
I=[]
while x:
 if x[0]==0:f='r%d=%d'%(x[1],w(x[2:]));n=6
 if x[0]==1:f='r%d=r%d'%(x[1],x[2]);n=3
 if x[0]==2:f='P%d'%x[1];n=2
 if x[0]==3:f='O%d'%x[1];n=2
 if x[0]==4:f='r%d=r%d+r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==5:f='r%d=r%d-r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==6:f='r%d=r%d*r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==7:f='r%d=r%d/r%d'%(x[1],x[1],x[2]);n=3
 if x[0]==8:f='goto L%d'%w(x[1:]);n=5
 if x[0]==9:f='a=r%d;b=r%d'%(x[1],x[2]);n=3
 if x[0]==10:f='if(a<b)goto L%d'%w(x[1:]);n=5
 if x[0]==11:f='if(a==b)goto L%d'%w(x[1:]);n=5
 if x[0]==12:f='if(a>b)goto L%d'%w(x[1:]);n=5
 if x[0]==13:f='if(a!=b)goto L%d'%w(x[1:]);n=5
 I+=[f];x=x[n:]
D=[]
d=0
for f in I:D+=[d];d+='P'==f[0];d-='O'==f[0]
J=[]
if all(d==D[int(f[f.find('L')+1:])]for f,d in zip(I,D)if f[0]in'gi'):
 H='uint32_t '+','.join('s%d'%i for i in range(max(D)))+';'
 for f,d in zip(I,D):
  if f[0]=='P':f='s%d=r'%d+f[1:]
  if f[0]=='O':f='r'+f[1:]+'=s%d'%(d-1)
  J+=[f]
else:
 H='std::vector<uint32_t>s;'
 for f,d in zip(I,D):
  if f[0]=='P':f='s.push_back(r'+f[1:]+')'
  if f[0]=='O':f='r'+f[1:]+'=s.back();s.pop_back()'
  J+=[f]
P='#include<vector>\n#include<cstdint>\nuint32_t r0,r1,r2,a,b;'+H+'int main(){'
for i,f in enumerate(J):P+='L%d:'%i+f+';'
P+=r'printf("R0 %u\nR1 %u\nR2 %u\n",r0,r1,r2);}'
c=open("t.cc", "w")
c.write(P)
c.close()
os.system("g++ -O1 t.cc")
os.system("./a.out")

สามารถตีกอล์ฟได้มากกว่านี้อย่างแน่นอน


เฉลี่ย 0.477 วินาทีต่อการรันมากกว่า 15 ครั้ง
มีสีสันขาวดำ

1

Lua 5.2 (หรือ LuaJIT), 740 ไบต์

ลองครั้งแรกเล่นกอล์ฟเพียงเล็กน้อยเท่านั้น รุ่นนี้ใช้งานได้ (อย่างน้อยในโปรแกรมทดสอบ) และใช้ opcodes พิเศษ แต่ไม่สนับสนุนข้อกำหนดทางคณิตศาสตร์ที่ไม่ได้ลงนามและไม่รวดเร็วเป็นพิเศษ แม้ว่าโบนัสจะเป็น VM ที่ทำงานอยู่ใน VM และถูกเขียนขึ้นเพื่อให้สามารถตีความ (รันด้วย PUC-Lua) หรือ sort-of-JIT (ทำงานกับ LuaJIT ยังคงตีความ แต่ล่ามอยู่ในขณะนี้ JITted)

แก้ไข: Golfed ดีขึ้นยังใหญ่

แก้ไข:แก้ไขข้อผิดพลาดที่สำคัญและตอนนี้ จำกัด เลขคณิตเป็นunsigned longช่วง อย่างใดจัดการเพื่อให้ขนาดจากการออกไปจากมือ แต่ก็ยังคงให้คำตอบที่ผิด

แก้ไข:เปิดออกผลลัพธ์ถูกต้อง แต่ผลลัพธ์ไม่ถูกต้อง เปลี่ยนเป็นการพิมพ์ด้วย%uแทนที่จะเป็น%dและทั้งหมดเป็นไปด้วยดี เปลี่ยนจากการลงทะเบียนตารางตามตัวแปรเพื่อปรับปรุงขนาดและความเร็วบ้าง

แก้ไข:การใช้gotoคำสั่งของ Lua 5.2 (มีให้ใน LuaJIT) ฉันได้แทนที่ล่ามด้วย "JIT-to-Lua" ซึ่งเป็นรหัสสร้างซึ่งรันโดยตรงโดย Lua VM เอง ไม่แน่ใจว่านี่จะนับเป็น JIT จริง ๆ หรือไม่ แต่จะปรับปรุงความเร็ว

U,S,P,F=table.unpack,table.insert,table.remove,math.floor X,r0,r1,r2,p,m,s=2^32,0,0,0,1,0,{}C={{'r%u=%u',1,4},{'r%u=r%u',1,1},{'S(s,r%u)',1},{'r%u=P(s)',1},{'r%u=(r%u+r%u)%%X',1,0,1},{'r%u=(r%u-r%u)%%X',1,0,1},{'r%u=(r%u*r%u)%%X',1,0,1},{'r%u=F(r%u/r%u)%%X',1,0,1},{'goto L%u',4},{'m=r%u-r%u',1,1},{'if m<0 then goto L%u end',4},{'if m==0 then goto L%u end',4},{'if m>0 then goto L%u end',4},{'if m~=0 then goto L%u end',4}}t={io.open(arg[1],'rb'):read('*a'):byte(1,-1)}i,n,r=1,0,{}while i<=#t do c,i,x,a=C[t[i]+1],i+1,0,{}for j=2,#c do y=c[j]if y>0 then x=0 for k=1,y do i,x=i+1,x*256+t[i]end end S(a,x)end S(r,('::L%d::'):format(n))n=n+1 S(r,c[1]:format(U(a)))end load(table.concat(r,' '))()print(('R0 %u\nR1 %u\nR2 %u'):format(r0,r1,r2))

นี่คือเวอร์ชันดั้งเดิมที่อ่านได้

U,S,P,F=table.unpack,table.insert,table.remove,math.floor

X,r0,r1,r2,p,m,s=2^32,0,0,0,1,0,{}

C={
    {'r%u=%u',1,4},
    {'r%u=r%u',1,1},
    {'S(s,r%u)',1},
    {'r%u=P(s)',1},
    {'r%u=(r%u+r%u)%%X',1,0,1},
    {'r%u=(r%u-r%u)%%X',1,0,1},
    {'r%u=(r%u*r%u)%%X',1,0,1},
    {'r%u=F(r%u/r%u)%%X',1,0,1},
    {'goto L%u',4},
    {'m=r%u-r%u',1,1},
    {'if m<0 then goto L%u end',4},
    {'if m==0 then goto L%u end',4},
    {'if m>0 then goto L%u end',4},
    {'if m~=0 then goto L%u end',4},
}

t={io.open(arg[1],'rb'):read('*a'):byte(1,-1)}
i,n,r=1,0,{}
while i<=#t do
    c,i,x,a=C[t[i]+1],i+1,0,{}
    for j=2,#c do
        y=c[j]
        if y>0 then
            x=0 
            for k=1,y do 
                i,x=i+1,x*256+t[i]
            end 
        end
        S(a,x)
    end
    S(r,('::L%d::'):format(n)) 
    n=n+1
    S(r,c[1]:format(U(a)))
end
load(table.concat(r,' '))()
print(('R0 %u\nR1 %u\nR2 %u'):format(r0,r1,r2))

เมื่อฉันวิ่งโปรแกรมของคุณผมมีข้อผิดพลาดต่อไปนี้: pastebin.com/qQBD7Rs8 คุณคาดหวัง bytecode กว่า stdin หรือเป็นไฟล์หรือไม่?
มีสีสันขาวดำ

ขอโทษ ไบนารีของฉันสำหรับ windows เสียหาย ดังนั้นทุกรุ่น gcc / linux ทำงานได้ แต่ windows ทดสอบทั้งหมดล้มเหลว อย่างไรก็ตามมันยังคงรายงานว่า R0 และ R1 เป็น 0 ในขณะที่ R2 คือ 1
Colorfully Monochrome

ฉันสงสัยว่ามันไม่ได้ดำเนินการจริง: ใช้เวลา 33.8 มิลลิวินาทีในการทำงานโดยเฉลี่ย (GCC ใช้เวลา ~ .25 วินาที)
มีสีสันขาวดำ

สคริปต์คาดว่า bytecode เป็นไฟล์โดยมีชื่อไฟล์ที่ส่งผ่านบรรทัดคำสั่ง แม้ว่าคุณจะถูกฉันติดตามมันและดูเหมือนว่ามันเป็นเพียงการทำวงรอบนอกครั้งแรก กลับไปที่กระดานวาดรูป ...
criptych ย่อมาจาก Monica

นั่นคือสิ่งที่ฉันได้รับในการคิดใน C และการเขียนใน Lua: ฉันใช้<ในลูปของฉันแทน<=ดังนั้นคำสั่งสาขาสุดท้ายถูกทิ้ง มันยังคงได้รับคำตอบที่ผิด แต่ตอนนี้ใช้เวลาซักครู่ :)
criptych ย่อมาจาก Monica

1

ค#

1505 1475 ไบต์

นี่คือล่ามรุ่นของฉันที่เขียนด้วยภาษา C # สามารถปรับให้เหมาะสม / เล่นกอล์ฟมากกว่านี้ฉันคิดว่า แต่ฉันไม่รู้ว่าที่ไหน;)

รุ่น golfed:

using System;using System.Collections.Generic;using System.IO;using System.Linq;class M{static void Main(string[]a){if(a.Length==1&&File.Exists(a[0])){B.E(B.P(File.ReadAllLines(a[0])));Console.WriteLine(B.O);}}}class B{public enum I{L=0x00,P=0x02,Q=0x03,A=0x04,S=0x05,M=0x06,D=0x07,J=0x08,C=0x09,BL=0x0a,BE=0x0b,BG=0x0c,BN=0x0d}public enum R{A,B,C}enum C{N,L,E,G}public static Dictionary<R,uint>r=new Dictionary<R,uint>{{R.A,0},{R.B,0},{R.C,0}};public static Stack<uint>s=new Stack<uint>();static C c=C.N;public static string O{get{return string.Format("R0 {0}\nR1 {1}\nR2 {2}",r[R.A],r[R.B],r[R.C]);}}public static void E(byte[][]l){for(uint i=0;i<l.Length;i++){var q=l[i];switch((I)q[0]){case I.L:r[(R)q[1]]=U(q,2);break;case I.P:r[(R)q[1]]=s.Pop();break;case I.Q:s.Push(r[(R)q[1]]);r[(R)q[1]]=0;break;case I.A:s.Push(r[(R)q[1]]+r[(R)q[2]]);break;case I.S:s.Push(r[(R)q[1]]-r[(R)q[2]]);break;case I.M:s.Push(r[(R)q[1]]*r[(R)q[2]]);break;case I.D:s.Push(r[(R)q[1]]/r[(R)q[2]]);break;case I.J:i=U(q,1)-1;break;case I.C:{uint x=r[(R)q[1]],y=r[(R)q[2]];c=x<y?C.L:x>y?C.G:C.E;}break;case I.BL:if(c==C.L)i=U(q,1)-1;break;case I.BG:if(c==C.G)i=U(q,1)-1;break;case I.BE:if(c==C.E)i=U(q,1)-1;break;case I.BN:if(c!=C.E)i=U(q,1)-1;break;}}}public static byte[][]P(string[]c){return c.Where(l=>!l.StartsWith("#")).Select(r=>r.Split(' ').Where(b=>b.Length>0).Select(b=>Convert.ToByte(b,16)).ToArray()).Where(l=>l.Length>0).ToArray();}static uint U(byte[]b,int i){return(uint)(b[i]<<24|b[i+1]<<16|b[i+2]<<8|b[i+3]);}}

แก้ไข

ลบบางส่วนที่ไม่จำเป็นpublicและprivateตัวดัดแปลง:

using System;using System.Collections.Generic;using System.IO;using System.Linq;class M{static void Main(string[]a){if(a.Length==1&&File.Exists(a[0])){B.E(B.P(File.ReadAllLines(a[0])));Console.Write(B.O);}}}class B{enum I{L=0x00,P=0x02,Q=0x03,A=0x04,S=0x05,M=0x06,D=0x07,J=0x08,C=0x09,BL=0x0a,BE=0x0b,BG=0x0c,BN=0x0d}enum R{A,B,C}enum C{N,L,E,G}static Dictionary<R,uint>r=new Dictionary<R,uint>{{R.A,0},{R.B,0},{R.C,0}};static Stack<uint>s=new Stack<uint>();static C c=C.N;public static string O{get{return string.Format("R0 {0}\nR1 {1}\nR2 {2}\n",r[R.A],r[R.B],r[R.C]);}}public static void E(byte[][]l){for(uint i=0;i<l.Length;i++){var q=l[i];switch((I)q[0]){case I.L:r[(R)q[1]]=U(q,2);break;case I.P:r[(R)q[1]]=s.Pop();break;case I.Q:s.Push(r[(R)q[1]]);r[(R)q[1]]=0;break;case I.A:s.Push(r[(R)q[1]]+r[(R)q[2]]);break;case I.S:s.Push(r[(R)q[1]]-r[(R)q[2]]);break;case I.M:s.Push(r[(R)q[1]]*r[(R)q[2]]);break;case I.D:s.Push(r[(R)q[1]]/r[(R)q[2]]);break;case I.J:i=U(q,1)-1;break;case I.C:{uint x=r[(R)q[1]],y=r[(R)q[2]];c=x<y?C.L:x>y?C.G:C.E;}break;case I.BL:if(c==C.L)i=U(q,1)-1;break;case I.BG:if(c==C.G)i=U(q,1)-1;break;case I.BE:if(c==C.E)i=U(q,1)-1;break;case I.BN:if(c!=C.E)i=U(q,1)-1;break;}}}public static byte[][]P(string[]c){return c.Where(l=>!l.StartsWith("#")).Select(r=>r.Split(' ').Where(b=>b.Length>0).Select(b=>Convert.ToByte(b,16)).ToArray()).Where(l=>l.Length>0).ToArray();}static uint U(byte[]b,int i){return(uint)(b[i]<<24|b[i+1]<<16|b[i+2]<<8|b[i+3]);}}

เรียกมันว่าด้วยexecutable.exe filenameที่filenameเป็นไฟล์ที่มีรหัสที่จะตีความ

"โปรแกรมทดสอบ" ของฉัน:

# LOAD R0 5
# CMP R0 R1
# BRANCHEQ 13
# LOAD R1 1
# LOAD R2 1
# CMP R0 R2
# MUL R1 R2
# LOAD R1 1
# ADD R2 R1
# PUSH R2
# PUSH R1 
# BRANCHEQ 13
# JMP 5
# POP R2
# POP R0
# POP R1
# PUSH R0

0x0 0x0 0x0 0x0 0x0 0x5
0x9 0x0 0x1 
0xb 0x0 0x0 0x0 0xd 
0x0 0x1 0x0 0x0 0x0 0x1 
0x0 0x2 0x0 0x0 0x0 0x1 
0x9 0x0 0x2 
0x6 0x1 0x2 
0x0 0x1 0x0 0x0 0x0 0x1 
0x4 0x2 0x1 
0x2 0x2 
0x2 0x1 
0xb 0x0 0x0 0x0 0xd 
0x8 0x0 0x0 0x0 0x5 
0x3 0x2 
0x3 0x0 
0x3 0x1 
0x2 0x0 

ล่าม ungolfed ที่มีตัวแปรชื่อ, คลาส, ...

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length == 1 && File.Exists(args[0]))
        {
            var code = ByteCodeInterpreter.ParseCode(File.ReadAllLines(args[0]));
            ByteCodeInterpreter.Execute(code);
            Console.WriteLine(ByteCodeInterpreter.Output);
        }
    }
}

public static class ByteCodeInterpreter
{
    public enum Instruction : byte
    {
        LOAD = 0x00,
        PUSH = 0x02,
        POP = 0x03,
        ADD = 0x04,
        SUB = 0x05,
        MUL = 0x06,
        DIV = 0x07,
        JMP = 0x08,
        CMP = 0x09,
        BRANCHLT = 0x0a,
        BRANCHEQ = 0x0b,
        BRANCHGT = 0x0c,
        BRANCHNE = 0x0d
    }

    public enum Register : byte
    {
        R0 = 0x00,
        R1 = 0x01,
        R2 = 0x02
    }

    private enum CompareFlag : byte
    {
        NONE = 0x00,
        LT = 0x01,
        EQ = 0x02,
        GT = 0x03,
    }

    public static readonly Dictionary<Register, uint> register = new Dictionary<Register, uint>
    {
        {Register.R0, 0},
        {Register.R1, 0},
        {Register.R2, 0}
    };

    public static readonly Stack<uint> stack = new Stack<uint>();
    private static CompareFlag compareFlag = CompareFlag.NONE;

    public static string Output
    {
        get
        {
            return string.Format("R0 {0}\nR1 {1}\nR2 {2}", register[Register.R0], register[Register.R1],
                register[Register.R2]);
        }
    }

    public static void Execute(byte[][] lines)
    {
        for (uint i = 0; i < lines.Length; i++)
        {
            var line = lines[i];
            switch ((Instruction)line[0])
            {
                case Instruction.LOAD:
                    register[(Register)line[1]] = GetUint(line, 2);
                    break;
                case Instruction.PUSH:
                    register[(Register)line[1]] = stack.Pop();
                    break;
                case Instruction.POP:
                    stack.Push(register[(Register)line[1]]);
                    register[(Register)line[1]] = 0;
                    break;
                case Instruction.ADD:
                    stack.Push(register[(Register)line[1]] + register[(Register)line[2]]);
                    break;
                case Instruction.SUB:
                    stack.Push(register[(Register)line[1]] - register[(Register)line[2]]);
                    break;
                case Instruction.MUL:
                    stack.Push(register[(Register)line[1]] * register[(Register)line[2]]);
                    break;
                case Instruction.DIV:
                    stack.Push(register[(Register)line[1]] / register[(Register)line[2]]);
                    break;
                case Instruction.JMP:
                    i = GetUint(line, 1) - 1;
                    break;
                case Instruction.CMP:
                    {
                        uint v0 = register[(Register)line[1]], v1 = register[(Register)line[2]];
                        if (v0 < v1)
                            compareFlag = CompareFlag.LT;
                        else if (v0 > v1)
                            compareFlag = CompareFlag.GT;
                        else
                            compareFlag = CompareFlag.EQ;
                    }
                    break;
                case Instruction.BRANCHLT:
                    if (compareFlag == CompareFlag.LT)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHGT:
                    if (compareFlag == CompareFlag.GT)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHEQ:
                    if (compareFlag == CompareFlag.EQ)
                        i = GetUint(line, 1) - 1;
                    break;
                case Instruction.BRANCHNE:
                    if (compareFlag != CompareFlag.EQ)
                        i = GetUint(line, 1) - 1;
                    break;
            }
        }
    }

    public static byte[][] ParseCode(string[] code)
    {
        return
            code.Where(line => !line.StartsWith("#"))
                .Select(line => line.Split(' ').Where(b => b.Length > 0).Select(b => Convert.ToByte(b, 16)).ToArray())
                .Where(line => line.Length > 0)
                .ToArray();
    }

    private static uint GetUint(byte[] bytes, int index)
    {
        return (uint)(bytes[index] << 24 | bytes[index + 1] << 16 | bytes[index + 2] << 8 | bytes[index + 3]);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.