8 बिट वर्चुअल मशीन


31

पृष्ठभूमि

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

कार्य

निम्नलिखित विनिर्देश के अनुरूप 8-बिट वर्चुअल मशीन को लागू करें। यह , इसलिए सबसे कम बाइट्स जीत के साथ कार्यान्वयन।

इनपुट

आपके कार्यान्वयन को निम्नलिखित इनपुट लेने चाहिए:

  • एक एकल अहस्ताक्षरित बाइट pc, यह प्रारंभिक प्रोग्राम काउंटर है (मेमोरी में पता जहां वीएम निष्पादन शुरू करता है, 0-based)

  • 256प्रविष्टियों की अधिकतम लंबाई के साथ बाइट्स की एक सूची , यह वर्चुअल मशीन के लिए रैम है (इसकी प्रारंभिक सामग्री के साथ)

आप इस इनपुट को किसी भी समझदार प्रारूप में ले सकते हैं।

उत्पादन

वीएम समाप्त होने के बाद बाइट्स की एक सूची जो रैम की अंतिम सामग्री है (नीचे देखें)। आप मान सकते हैं कि आपको केवल इनपुट मिल सकता है जो अंततः समाप्त होता है। किसी भी समझदार प्रारूप की अनुमति है।

वर्चुअल सीपीयू

वर्चुअल CPU है

  • 8-बिट प्रोग्राम काउंटर,
  • 8-बिट संचायक रजिस्टर कहा जाता है Aऔर
  • 8-बिट इंडेक्स रजिस्टर कहा जाता है X

तीन स्थिति ध्वज हैं:

  • Z - शून्य ध्वज को कुछ ऑपरेशन के परिणाम के बाद सेट किया गया है 0
  • N - एक नकारात्मक संख्या में कुछ ऑपरेशन के परिणाम के बाद नकारात्मक ध्वज सेट किया गया है (परिणाम का iow बिट 7 सेट किया गया है)
  • C - कैरी फ़्लैग को परिणाम के "लापता" बिट के लिए परिवर्धन और बदलाव द्वारा निर्धारित किया जाता है

शुरू करने पर, झंडे सब साफ़ हो जाते हैं, प्रोग्राम काउंटर किसी दिए गए मूल्य और की सामग्री के लिए सेट है Aऔर Xअनिश्चित है।

8-बिट मान या तो प्रतिनिधित्व करते हैं

  • रेंज में एक अहस्ताक्षरित पूर्णांक[0..255]
  • एक हस्ताक्षरित पूर्णांक, 2 का पूरक, सीमा में[-128..127]

संदर्भ के आधार पर। यदि कोई ऑपरेशन ओवरफ्लो करता है या कम होता है, तो मान चारों ओर घूमता है (और इसके अलावा, कैरी फ्लैग प्रभावित होता है)।

समाप्ति

वर्चुअल मशीन कब समाप्त होती है

  • एक HLTनिर्देश पहुँच जाता है
  • एक गैर-मौजूदा मेमोरी एड्रेस एक्सेस किया जाता है
  • प्रोग्राम काउंटर मेमोरी के बाहर चलता है (ध्यान दें कि यह चारों ओर लपेटता नहीं है, भले ही वीएम को मेमोरी के पूर्ण 256 बाइट्स दिए गए हों)

अनुकूल करने की विधा

  • निहित - निर्देश का कोई तर्क नहीं है, ऑपरेंड निहित है
  • तत्काल - ऑपरेंड निर्देश के बाद सीधे बाइट है
  • सापेक्ष - (केवल शाखा के लिए) निर्देश पर हस्ताक्षर होने के बाद बाइट (2 का पूरक) और शाखा निर्धारित होने पर प्रोग्राम काउंटर में जोड़ने के लिए ऑफसेट निर्धारित करता है। 0निम्नलिखित निर्देश का स्थान है
  • निरपेक्ष - निर्देश के बाद बाइट ऑपरेंड का पता है
  • अनुक्रमित - निर्देश प्लस X(रजिस्टर) के बाद बाइट ऑपरेंड का पता है

अनुदेश

प्रत्येक निर्देश में एक ओपोड (एक बाइट) होता है और, संबोधन मोड में तत्काल , सापेक्ष , निरपेक्ष और एक दूसरे तर्क बाइट को अनुक्रमित किया जाता है। जब वर्चुअल सीपीयू एक निर्देश निष्पादित करता है, तो यह प्रोग्राम काउंटर को तदनुसार ( 1या 2) बढ़ाता है ।

यहाँ दिखाए गए सभी ऑपकोड हेक्स में हैं।

  • LDA - लोड ऑपरेंड में A

    • Opcodes: तत्काल:, 00पूर्ण:, 02अनुक्रमित:04
    • झंडे: Z,N
  • STA- Aओपेरा में स्टोर करें

    • Opcodes: तत्काल:, 08पूर्ण:, 0aअनुक्रमित:0c
  • LDX - लोड ऑपरेंड में X

    • Opcodes: तत्काल:, 10पूर्ण:, 12अनुक्रमित:14
    • झंडे: Z,N
  • STX- Xओपेरा में स्टोर करें

    • Opcodes: तत्काल:, 18पूर्ण:, 1aअनुक्रमित:1c
  • AND- बिटवाइज़ और की Aऔर संकार्य मेंA

    • Opcodes: तत्काल:, 30पूर्ण:, 32अनुक्रमित:34
    • झंडे: Z,N
  • ORA- बिटवाइज़ या की Aऔर संकार्य मेंA

    • Opcodes: तत्काल:, 38पूर्ण:, 3aअनुक्रमित:3c
    • झंडे: Z,N
  • EOR- का बिटवाइस xor (एक्सक्लूसिव या) Aऔर ऑपरेंडA

    • Opcodes: तत्काल:, 40पूर्ण:, 42अनुक्रमित:44
    • झंडे: Z,N
  • LSR - लॉजिकल शिफ्ट राइट, ऑपरेंड के सभी बिट्स को एक स्थान पर दाईं ओर शिफ्ट करें, बिट 0 ले जाने के लिए जाता है

    • Opcodes: तत्काल:, 48पूर्ण:, 4aअनुक्रमित:4c
    • झंडे: Z, N,C
  • ASL - अंकगणित पारी छोड़ दिया, ऑपरेंड के सभी बिट्स को बाईं ओर एक स्थान पर स्थानांतरित करें, बिट 7 ले जाने के लिए जाता है

    • Opcodes: तत्काल:, 50पूर्ण:, 52अनुक्रमित:54
    • झंडे: Z, N,C
  • ROR - दाईं ओर घुमाएं, ऑपरेंड के सभी बिट्स को एक स्थान पर दाईं ओर शिफ्ट करें, कैरी 7 पर जाता है, बिट 0 ले जाने के लिए जाता है

    • Opcodes: तत्काल:, 58पूर्ण:, 5aअनुक्रमित:5c
    • झंडे: Z, N,C
  • ROL - बाईं ओर घुमाएं, ऑपरेंड के सभी बिट्स को बाईं ओर एक स्थान पर शिफ्ट करें, कैरी 0 पर जाता है, बिट 7 ले जाने के लिए जाता है

    • Opcodes: तत्काल:, 60पूर्ण:, 62अनुक्रमित:64
    • झंडे: Z, N,C
  • ADC- कैरी के साथ जोड़ें, ऑपरेंड प्लस कैरी को जोड़ा जाता है A, कैरी को ओवरफ्लो पर सेट किया जाता है

    • Opcodes: तत्काल:, 68पूर्ण:, 6aअनुक्रमित:6c
    • झंडे: Z, N,C
  • INC - वेतन वृद्धि एक-एक करके

    • Opcodes: तत्काल:, 78पूर्ण:, 7aअनुक्रमित:7c
    • झंडे: Z,N
  • DEC - एक के बाद वेतन वृद्धि

    • Opcodes: तत्काल:, 80पूर्ण:, 82अनुक्रमित:84
    • झंडे: Z,N
  • CMP- Aसे ऑपरेंड घटाकर ऑपरेंड के साथ तुलना करें A, परिणाम भूल जाएं। अंडरफ्लो पर कैरी साफ किया जाता है, अन्यथा सेट करें

    • Opcodes: तत्काल:, 88पूर्ण:, 8aअनुक्रमित:8c
    • झंडे: Z, N,C
  • CPX- तुलना X- रूप में एक ही CMPके लिएX

    • Opcodes: तत्काल:, 90पूर्ण:, 92अनुक्रमित:94
    • झंडे: Z, N,C
  • HLT - समाप्त करें

    • Opcodes: निहित: c0
  • INX- Xएक से वृद्धि

    • Opcodes: निहित: c8
    • झंडे: Z,N
  • DEX- Xएक से घटाव

    • Opcodes: निहित: c9
    • झंडे: Z,N
  • SEC - सेट कैरी फ्लैग

    • Opcodes: निहित: d0
    • झंडे: C
  • CLC - स्पष्ट कैरी फ्लैग

    • Opcodes: निहित: d1
    • झंडे: C
  • BRA - शाखा हमेशा

    • Opcodes: रिश्तेदार: f2
  • BNE- शाखा अगर Zझंडा साफ हो गया

    • Opcodes: रिश्तेदार: f4
  • BEQ- शाखा अगर Zझंडा सेट

    • Opcodes: रिश्तेदार: f6
  • BPL- शाखा अगर Nझंडा साफ हो गया

    • Opcodes: रिश्तेदार: f8
  • BMI- शाखा अगर Nझंडा सेट

    • Opcodes: रिश्तेदार: fa
  • BCC- शाखा अगर Cझंडा साफ हो गया

    • Opcodes: रिश्तेदार: fc
  • BCS- शाखा अगर Cझंडा सेट

    • Opcodes: रिश्तेदार: fe

opcodes

VM का व्यवहार अपरिभाषित है, यदि कोई भी ओपकोड पाया जाता है जो उपरोक्त सूची से मान्य निर्देश के लिए मैप नहीं करता है।

के अनुसार जोनाथन एलन के अनुरोध , आप कर सकते हैं में दिखाया गया opcodes के बजाय opcodes के अपने स्वयं के सेट का चयन निर्देश अनुभाग। यदि आप ऐसा करते हैं, तो आपको अपने उत्तर में उपयोग किए गए ऑपकोड में एक पूर्ण मानचित्रण जोड़ना होगा

मैपिंग जोड़े के साथ एक हेक्स फ़ाइल होनी चाहिए <official opcode> <your opcode>, जैसे कि यदि आपने दो ओपकोड्स को बदल दिया है:

f4 f5
10 11

Newlines यहाँ कोई फर्क नहीं पड़ता।

परीक्षण के मामले (आधिकारिक ऑपकोड)

// some increments and decrements
pc:     0
ram:    10 10 7a 01 c9 f4 fb
output: 10 20 7a 01 c9 f4 fb

// a 16bit addition
pc:     4
ram:    e0 08 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
output: 0a 0b 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01

// a 16bit multiplication
pc:     4
ram:    5e 01 28 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
        02 62 03 c9 f8 e6 c0 00 00
output: 00 00 00 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
        02 62 03 c9 f8 e6 c0 b0 36

मैं बाद में और अधिक टेस्टेसिस जोड़ सकता हूं।

संदर्भ और परीक्षण

स्वयं के प्रयोगों के साथ मदद करने के लिए, यहाँ कुछ (पूरी तरह से गोल्फ नहीं) संदर्भ कार्यान्वयन है - यह ट्रेसिंग जानकारी ( डिसएम्ड किए गए निर्देशों सहित) को आउटपुट कर सकता है stderrऔर रनिंग के दौरान ऑपकोड को परिवर्तित कर सकता है।

स्रोत प्राप्त करने के लिए अनुशंसित तरीका:

git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules

या चेकआउट शाखा challengeऔर git submodule update --init --recursiveमेरे कस्टम बिल्ड सिस्टम को प्राप्त करने के लिए, क्लोनिंग के बाद करें।

GNU मेक के साथ टूल का निर्माण करें (बस टाइप करें make, या gmakeयदि आपके सिस्टम पर, डिफ़ॉल्ट मेक अप GNU मेक नहीं है)।

उपयोग :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram

  • -s startpc - प्रारंभिक कार्यक्रम काउंटर, के लिए चूक 0
  • -h - इनपुट हेक्स में है (अन्यथा बाइनरी)
  • -t - का पता लगाने के लिए निष्पादन stderr
  • -c convfile - इसमें दिए गए मैपिंग के अनुसार ऑपकोड को कन्वर्ट करें convfile
  • -d - डंप परिणामी मेमोरी को बाइनरी डेटा के रूप में
  • -x - डंप परिणामस्वरूप स्मृति हेक्स के रूप में
  • initial_ram - प्रारंभिक रैम सामग्री, या तो हेक्स या बाइनरी

ध्यान दें कि रूपांतरण सुविधा उन कार्यक्रमों पर विफल हो जाएगी जो रन करते समय opcodes को संशोधित करते हैं।

अस्वीकरण: ऊपर दिए गए नियम और चश्मा चुनौती के लिए आधिकारिक हैं, इस उपकरण के लिए नहीं। यह विशेष रूप से opcode रूपांतरण सुविधा पर लागू होता है। यदि आपको लगता है कि यहां प्रस्तुत टूल में बग बग ऐनक है, तो कृपया टिप्पणी में रिपोर्ट करें :)


1
मुझे लगता है कि निर्देशों के लिए अलग-अलग opcodes को चुनने के लिए संभवतः कई गोल्फिंग अवसर होंगे, लेकिन ऐसा लगता है कि opcodes तय हो गए हैं (भले ही निर्देश सेट क्या मशीन को परिभाषित करता है)। शायद यह कार्यान्वयन पर विचार करने के लिए अपने स्वयं के कोड-पृष्ठ की अनुमति देने के लायक है?
जोनाथन एलन

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

1
@Arnauld ने मेरे तर्क को अनुमति देते हुए कहा कि यह विशेष मामलों की मात्रा को कम करने के लिए है, इसलिए यह बेहतर "गोल्फ" होना चाहिए - प्रत्येक ओपकोड या तो निहित है, एक रिश्तेदार शाखा है या सभी तीन अन्य संबोधित करने के तरीके की अनुमति देता है :)
फेलो मैनमेन

1
अगर BRA("शाखा हमेशा") नियंत्रण प्रवाह में एक शाखा का परिचय नहीं देता है, तो क्या इसे नहीं बुलाया जाना चाहिए JMP?
ngn

1
@ अच्छी तरह से, BRAबाद में चिप डिजाइन (6502 में ऐसा कोई निर्देश नहीं है) 65C02 और MC 68000 की JMPतरह मौजूद है। साथ ही मौजूद है। अंतर यह है कि BRAसापेक्ष पते का JMPउपयोग करता है और निरपेक्ष पते का उपयोग करता है। इसलिए, मैंने सिर्फ इन डिजाइनों का पालन किया - वास्तव में, यह सब तर्कसंगत नहीं लगता है;)
फेलिक्स पाम्स

जवाबों:


16

सी (जीसीसी) , 1381 1338 1255 1073 बाइट्स

छत और रोजम के लिए विशाल सुधार धन्यवाद।

#include<stdio.h>
C*F="%02hhx ";m[256],p,a,x,z,n,c,e;g;*I(){R++p+m;}*A(){R*I()+m;}*X(){R*I()+m+x;}C*Q(){W(printf,m[g],1)exit(a);}C*(*L[])()={I,Q,A,Q,X,Q,Q,Q};l(C*i){R 254/p?*i=*L[m[p]&7]():*Q();}s(i){R 254/p?*L[m[p]&7]()=i:*Q();}q(){p>254?Q():++p;}C*y(){p+=e;}B(U,z)B(V,!z)B(_,n)B(BM,!n)B(BC,c)B(BS,!c)C*(*b[])()={Q,Q,y,Q,U,Q,V,Q,_,Q,BM,Q,BC,Q,BS,Q};j(){z=!l(&a);v}o(){s(a);}t(){z=!l(&x);n=x&H;}D(){s(x);}f(K(&=)h(K(|=)i(K(^=)J(E;c=e&1;z=!(e/=2);s(e);w}k(E;c=w;z=!e;s(e*=2);}T(E;g=e&1;z=!(e=e/2|H*!!c);c=g;s(e);w}M(E;g=w z=!(e=e*2|H*!!c);c=g;s(e);}N(E;z=!(a=g=a+e+!!c);c=g>>8%2;G}P(E;z=!~e;--p;s(g=e+1);G}u(E;g=e-1;z=!g;--p;s(g);G}r(E;g=a-e;z=!g;c=G}S(E;g=x-e;z=!g;c=G}Y(){z=!(x=g=1-m[p]%2*2);n=x&H;}Z(){c=~m[p]&1;}d(){p<255||Q();e=m[++p];b[m[p-1]&15]();}(*O[])()={j,o,t,D,Q,Q,f,h,i,J,k,T,M,N,Q,P,u,r,S,Q,Q,Q,Q,Q,Q,Y,Z,Q,Q,Q,d,d};main(){scanf(F,&p);W(scanf,&m[g],0)for(;;q())O[m[p]/8]();}

इसे ऑनलाइन आज़माएं!

ढेर सारे डिफाइनर कंपाइलर फ्लैग में चले गए।

स्पष्टीकरण (बहुत असम्बद्ध):

#include<stdio.h>

// useful defines
#define C unsigned char
#define H 128 // highest bit
#define R return

// code generator for I/O
#define W(o,p,q)for(g=-1;++g<256&&((q)||!feof(stdin));)(o)(F,(p));

// code generator for branching instruction handlers
#define BB(q)(){(q)||Y();}

// frequent pieces of code
#define NA n=a&H;
#define NE n=e&H;
#define NG n=g&H;
#define E l(&e)

// printf/scanf template
C * F = "%02hhx ";

// global state: m=memory, pax=registers, znc=flags
// e and g are for temporaries and type conversions
C m[256],p,a,x,z,n,c,e;g;

// get the pointer to a memory location:
C * I() {R &m[++p];} // immediate
C * A() {R &m[m[++p]];} // absolute
C * X() {R &m[m[++p]+x];} // indexed

// terminate the VM (and dump memory contents)
C * Q() { W(printf,m[g],1) exit(a);}

// an array of functions for accessing the memory
// They either return the pointer to memory location
// or terminate the program.
C * (*L[])()={I,Q,A,Q,X,Q,Q,Q};

// load a byte from the memory into the variable pointed by i
// terminate the program if we cannot access the address byte
l (C * i) {return 254 / p ? *i = *L[m[p]&7] () : *Q ();}

// save a byte i to the memory
// terminate the program if we cannot access the address byte
s (C i) {return 254 / p ? *L[m[p]&7]() = i : *Q ();}

// advance the instruction pointer (or fail if we fall outside the memory)
q () {p > 254 ? Q () : ++p;}

// branch
C * Y() {p += e;}

// generated functions for conditional branches
C * BN BB(z)
C * BZ BB(!z)
C * BP BB(n)
C * BM BB(!n)
C * BC BB(c)
C * BS BB(!c)

// a list of branch functions
C * (*B[])() = {Q,Q,Y,Q,BN,Q,BZ,Q,BP,Q,BM,Q,BC,Q,BS,Q};

// Instruction handling functions

OA () {z = !l (&a); NA} // lda
OB () {s (a);} // sta
OC () {z = !l (&x); n = x & H;} // ldx
OD () {s (x);} // stx
OG () {E; z = !(a &= e); NA} // and
OH () {E; z = !(a |= e); NA} // ora
OI () {E; z = !(a ^= e); NA} // eor
OJ () {E; c = e & 1; z = !(e /= 2); s (e); NE} // lsr
OK () {E; c = NE; z = !e; s (e *= 2);} // asl
OL () {E; g = e & 1; z = !(e = e / 2 | H * !!c); c = g; s (e); NE} // ror
OM () {E; g = e & H; z = !(e = e * 2 | H * !!c); c = g; s (e); NE} // rol
ON () {E; z = !(a = g = a + e + !!c); c = !!(g & 256); NG} // adc
OP () {E; z = !~e; --p; s (g = e + 1); NG} // inc
OQ () {E; g = e - 1; z = !g; --p; s (g); NG} // dec
OR () {E; g = a - e; z = !g; c = NG} // cmp
OS () {E; g = x - e; z = !g; c = NG} // cpx
OY () {z = !(x = g = ~m[p] & 1 * 2 - 1); n = x & H;} // inx/dex
OZ () {c = ~m[p] & 1;} // sec/clc
Od () {p < 255 || Q (); e = m[++p]; B[m[p-1]&15] ();} // branching

// list of opcode handlers
(*O[]) () = {OA,OB,OC,OD,Q,Q,OG,OH,OI,OJ,OK,OL,OM,ON,Q,OP,OQ,OR,OS,Q,Q,Q,Q,Q,Q,OY,OZ,Q,Q,Q,Od,Od};

// main function
main ()
{
    // read the instruction pointer
    scanf (F, &p);

    // read memory contents
    W(scanf, &m[g], 0)

    // repeatedly invoke instruction handlers until we eventually terminate
    for (;; q())
        O[m[p]/8] ();
}

महान काम, तत्काल +1, वास्तव में पहले सी समाधान की उम्मीद नहीं थी :) आपकी 00बाइट्स का अपीयरेंस नियमों को थोड़ा झुका सकता है ... मैं मानता हूं कि मैंने इस कोड का विश्लेषण करने की कोशिश नहीं की ... क्या आप कर सकते हैं हेक्स के बजाय बाइनरी में I / O कर रहे बाइट्स बचाएं? नियमों द्वारा अनुमति दी जाएगी :)
फेलिक्स पाम्स

मैं इस नियम को बदलना चाहता हूं कि अवैध ऑपकोड्स को यह कहते हुए समाप्त कर दिया जाता है कि अवैध ऑपकोड का व्यवहार अपरिभाषित है ... क्या इससे आपके उत्तर को नुकसान पहुंचेगा या आप इसके साथ ठीक हैं?
फेलिक्स पालमेन

@FelixPalmen अच्छी तरह से, जब तक समाप्ति काफी वैध "अपरिभाषित" व्यवहार है, यह चोट नहीं
पहुँचाएगा

@MaxYekhlakov "चोट" से मेरा मतलब है कि आप अपने समाधान के प्रति अनुचित हैं क्योंकि आप शायद "बाइट्स" खर्च करते हैं, यह सुनिश्चित करने के लिए कि अवैध ओपोड वीएम को समाप्त करता है। मुझे खुशी है कि आप एक नियम के रूप में नियम परिवर्तन का स्वागत करते हैं :) और फिर से, बधाई देता हूं, मैं सिर्फ सी में एक समाधान देखना पसंद करता हूं, जो मेरी सर्वकालिक पसंदीदा प्रोग्रामिंग भाषा है। यह अफ़सोस की बात है कि आप शायद ही कभी सी में एक कोड-गोल्फ चुनौती जीतेंगे - फिर भी, "गोल्फ" सी सिर्फ शांत है :)
फेलिक्स पाम्स

आप पोस्ट करने के लिए झंडे का हिस्सा जोड़ सकते हैं?
l4m2

8

APL (Dyalog Classic) , 397 332 330 बाइट्स

धन्यवाद @ -8 बाइट्स के लिए

f←{q2a x c←≡B256⋄{0::m
⍺←(∇q∘←)0∘=,B≤+⍨
30u←⌊8÷⍨bpm:∇p+←129-B|127-1pm×⊃b2/(,~,⍪)1,q,c
p+←1
u=25:⍺x⊢←B|x1*b
u=26:∇c⊢←~2|b
p+←≢om⊃⍨i←⍎'p⊃m+x'↑⍨1+8|b
u⊃(,⍉'⍺ax⊢←o' '∇m[i]←ax'∘.~'xa'),5 4 2 3 2/'⍺⌽⊃'∘,¨'a⊢←2⊥(⍕⊃u⌽''∧∨≠'')/o a⊤⍨8⍴2' 'c(i⊃m)←u⌽d⊤(⌽d←u⌽2B)⊥u⌽o,c×u>10' 'c a⊢←2B⊤a+o+c' 'm[i]←B|o-¯1*u' 'c⊢←⊃2B⊤o-⍨⊃u⌽x a'}p m←⍵}

इसे ऑनलाइन आज़माएं!

p  program counter
m  memory
a  accumulator register
x  index register
q  flags z (zero) and n (negative) as a length-2 vector
c  flag for carry
  function to update z and n
b  current instruction
u  highest 5 bits of b
o  operand
i  target address in memory


क्या इस समाधान में अनपेक्षित ओपकोड हैं और यदि नहीं, तो क्या आपने उनसे बचने के लिए बाइट्स खर्च किए हैं? कारण पूछने पर यह टिप्पणी देखिए ...
फेलिक्स पालमेन

@FelixPalmen अब आप इसका उल्लेख करते हैं, हां :( शुरू में मैंने उस नियम का पालन किया था, लेकिन जैसा कि मैं गोल्फ कर रहा था मैंने गलती से 4, 5, और संभवतः अन्य, वैध opcodes बना दिया। इसलिए उनके व्यवहार को अपरिभाषित बनाने का निर्णय सबसे स्वागत योग्य होगा :)।
ngn

2
अब, मुझे एहसास हुआ कि यह पहली जगह में सबसे अच्छा निर्णय नहीं था और @MaxYekhlakov दुर्भाग्य से नियम परिवर्तन के बारे में कुछ भी कहने के लिए नहीं था।
फेलिक्स पाम्स

क्या आपको जरूरत है f←?
आउटगॉल्फ

8

C (gcc) , 487 , 480 , 463 , 452 , 447 , 438 बाइट्स

इस निर्देश मैपिंग का उपयोग करता है । निर्देशों का अद्यतन 9 बाइट्स से मुंडा, और भविष्य में संभवतः अधिक। पहले तर्क द्वारा इंगित स्मृति को संशोधित करके लौटाता है ( M)। कुछ बाइट्स बंद करने के लिए @ceilingcat को धन्यवाद।

झंडे के साथ संकलित किया जाना चाहिए -DO=*o -DD=*d -DI(e,a,b)=if(e){a;}else{b;} -Du="unsigned char"(पहले से ही बाइट्स में शामिल)।

e(M,Z,C)u*M,C;{for(u r[2],S=0,D,O,c,t;o=C<Z?M+C++:0;){I(c=O,d=r+!(c&4),break)I(o=c&3?C<Z&&C?M+C++:0:d,o=c&2?O+c%2**r+M:o,break)t=(c/=8)&7;I(c<24&c>4&&t,t&=3;I(c&8,I(c&4,c&=S&1;S=O>>7*!(t/=2);O=t=O<<!t>>t|c<<7*t,t=O+=t%2*2-1),I(c&4,D=t=t?t&2?t&1?O^D:O|D:O&D:O,I(c&1,S=D>(t=D+=O+S%2),t=D-O;S=t>D)))S=S&1|t>>6&2|4*!t,I(c&8,C+=!(t&~-t?~t&S:t&~S)*O,I(t,S=S&6|c%2,O=D)))I(C,,Z=0)}}

इसे ऑनलाइन आज़माएं!

प्रीप्रोसेसर

-DO=*o -DD=*d

ये दोनों बिंदुओं को कम करने के लिए एक छोटा रास्ता प्रदान करते हैं।

-DI(e,a,b)=if(e){a;}else{b;} -Du="unsigned char"

If-elses और प्रकार की घोषणाओं के लिए आवश्यक बाइट्स की संख्या कम करें।

कोड

नीचे कोड का एक मानव-पठनीय संस्करण है, जिसमें प्रीप्रोसेसर निर्देश विस्तारित हैं, और चर पठनीयता के लिए नामांकित हैं।

exec_8bit(unsigned char *ram, int ramSize, unsigned char PC)
{  
  for(unsigned char reg[2], SR=0, // The registers. 
                                  // reg[0] is X, reg[1] is A. 
                                  // SR contains the flags.
      *dst, *op, opCode, tmp;
      // Load the next instruction as long as we haven't gone out of ram.
      op = PC < ramSize ? ram + PC++ : 0;)
  { // Check for HLT.
    if(opCode=*op)
    { // Take a pointer to the register selected by complement of bit 3.
      dst = reg+!(opCode&4);
    } else break;
    // Load operand as indicated by bits 0 and 1. Also check that we don't
    // go out of bounds and that the PC doesn't overflow.
    if(op = opCode&3 ? PC<ramSize && PC ? ram + PC++ : 0 : dst)
    {
      op = opCode&2 ? *op + opCode%2 * *reg + ram: op
    } else break;

    // Store the bits 3-5 in tmp.
    tmp = (opCode/=8) & 7;
    if(opCode<24 & opCode>4 && tmp)
    { // If not HLT, CLC, SEC or ST, enter this block.
      tmp &= 3; // We only care about bits 3&4 here.
      if(opCode&8) // Determine whether the operation is binary or unary.
      { // Unary
        if(opCode&4)
        { // Bitshift
          opCode &= SR&1; // Extract carry flag and AND it with bit 3 in opCode.
          SR=*op >> 7*!(tmp/=2);// Update carry flag.
          // Shift to left if bit 4 unset, to right if set. Inclusive-OR 
          // the carry/bit 3 onto the correct end.
          *op = tmp = *op << !tmp >> tmp | opCode << 7*tmp;
        } else tmp=*o+=tmp%2*2-1;
      } else if(opCode&4) {
        // Bitwise operations and LD.
        // Ternary conditional to determine which operation.
        *dst = tmp = tmp? tmp&2? tmp&1? *op^*dst: *op|*dst: *op&*dst: *op
      } else if(opCode&1) {
        // ADC. Abuse precedence to do this in fewer bytes.
        // Updates carry flag.
        SR = *dst > (tmp = *dst += *op + SR%2);
      } else tmp=*dst-*op; SR = tmp > *dst; // Comparison.
      SR = SR&1 | tmp >> 6&2 | 4*!tmp; // Update Z and N flags, leaving C as it is.
    } else if(opCode&8) {
      // Branch.
      // tmp&~-tmp returns a truthy value when tmp has more than one bit set
      // We use this to detect the "unset" and "always" conditions.
      // Then, we bitwise-AND either ~tmp&SR or tmp&~SR to get a falsy value
      // when the condition is fulfilled. Finally, we take logical complement,
      // and multiply the resulting value (`1` or `0`) with the operand,
      // and add the result to program counter to perform the jump.
      PC += !(tmp & ~-tmp? ~tmp&SR : tmp&~SR) * *op;
    } else if (tmp) { // SEC, CLC
      SR = SR&6 | opCode % 2;
    } else {
      *op = *dst; // ST
    }
    if(!PC){ // If program counter looped around, null out ramSize to stop.
           // There's likely a bug here that will kill the program when it
           // branches back to address 0x00
      ramSize=0;
    }
  }
}

अनुदेश

निर्देश निम्नानुसार संरचित हैं:

  • 6-7 बिट्स निर्देश का संकेत देते हैं ( 00नलरी, यूनरी 01, 10बाइनरी, 11बाइनरी)

  • बिट्स 0-2 ऑपरेंड (ओं) को निर्धारित R=0करता है : चयन करता है Aऔर R=1चयन करता है XOP=00एक ऑपरेटर के रूप में रजिस्टर का उपयोग करता है, OP=01तत्काल ऑपरेंड OP=10का चयन करता है , निरपेक्ष ऑपरेंड का OP=11चयन करता है और अनुक्रमित ऑपरेंड का चयन करता है।

    • जैसा कि आपने देखा होगा, यह किसी भी ऑपरेशन के लिए या तो रजिस्टर पर प्रदर्शन करने की अनुमति देता है (हालांकि आप अभी भी केवल सूचकांक कर सकते हैं X) तब भी जब वे सामान्य रूप से प्रति विनिर्देशन का उपयोग नहीं कर सकते हैं। जैसे INC A, ADC X, 10और ASL Xसब काम।
  • बिट्स 3-5 ब्रांचिंग के लिए स्थिति निर्धारित करते हैं: बिट्स में से एक इंगित करता है कि कौन सा झंडा परीक्षण करने के लिए है (बिट 3-> सी, बिट 4-> एन, बिट 5-> जेड)। यदि केवल एक बिट सेट किया गया है, तो सेट फ्लैग के लिए निर्देश परीक्षण। एक परेशान ध्वज के लिए परीक्षण करने के लिए, बिट्स के पूरक को लें। उदाहरण के 110लिए अनसेट कैरी के लिए और 001सेट कैरी के लिए। 111और 000शाखा हमेशा।

  • आप एक रजिस्टर में संग्रहीत पते की शाखा में भी शाखा कर सकते हैं, जिससे आप फ़ंक्शन लिख सकते हैं, या आप मानक अनुक्रमण मोड का उपयोग कर सकते हैं। OP=01विनिर्देश शाखा की तरह व्यवहार करता है।

+-----+----------+-------+-----------------------------------------------+
| OP  | BINARY   | FLAGS | INFO                                          |
+-----+----------+-------+-----------------------------------------------+
| ST  | 10000ROP |       | Register -> Operand                           |
| LD  | 10100ROP | Z N   | Operand -> Register                           |
| AND | 10101ROP | Z N   | Register &= Operand                           |
| XOR | 10111ROP | Z N   | Register ^= Operand                           |
| IOR | 10110ROP | Z N   | Register |= Operand                           |
| ADC | 10011ROP | Z N C | Register += Operand + Carry                   |
| INC | 01011ROP | Z N   | Operand += 1                                  |
| DEC | 01010ROP | Z N   | Operand -= 1                                  |
| ASL | 01100ROP | Z N C | Operand <<= 1                                 |
| LSR | 01110ROP | Z N C | Operand >>= 1                                 |
| ROL | 01101ROP | Z N C | Operand = Operand << 1 | Carry                |
| ROR | 01111ROP | Z N C | Operand = Operand >> 1 | Carry << 7           |
| CMP | 10010ROP | Z N C | Update ZNC based on Register - Operand        |
| BR  | 11CNDROP |       | PC += Condition ? Operand : 0      |
| SEC | 00011000 |     C | Set carry                                     |
| CLC | 00010000 |     C | Clear carry                                   |
| HLT | 00000000 |       | Halt execution.                               |
+-----+----------+-------+-----------------------------------------------+

7

जावास्क्रिप्ट (ईएस 6), 361 बाइट्स

इनपुट के रूप में लेता है (memory)(program_counter), जहां memoryएक है Uint8Array। इस सरणी को संशोधित करके आउटपुट

M=>p=>{for(_='M[\u0011\u0011A]\u0010\u0010=\u000fc=\u000e,\u0011p]\f(n=\u000b128)\t=\u0010\b&=255\u0007,z=!(n\u0007),n&=\t;\u0006\u0006\u000b\u0005-\u0010,\u000en>=0\u0005\u0004\u0011c\b>>7,A]*2\u0005\u0003\u0011c\b&1,A]/2\u0005\u000f\u0002&&(p+=(\u0010^\t-\t;\u0001for(a=n=z=\u000ex=0;a\u0007,x\u0007,A=[i=\u0011p++],p\f\f+x][i&3],i&3&&p++,i&&A<256;)eval(`\u000ba\b\u0006\u000fa;\u000bx\b\u0006\u000fx;\u000ba&\b\u0005a|\b\u0005a^\b\u0005\u000f\u0002\u0003\u000fc*128|\u0002c|\u0003a+\b+c,\u000ea>>8\u0005++\u0010\u0005--\u0010\u0005a\u0004x\u0004++x\u0005--x\u0006\u000e1;\u000e0;1\u0001!z\u0001z\u0001!n\u0001n\u0001!c\u0001c\u0001`.split`;`[i>>2])';G=/[\u0001-\u0011]/.exec(_);)with(_.split(G))_=join(shift());eval(_)}

NB: कोड RegPack के साथ संपीड़ित है और इसमें बहुत सारे अनपेक्षित वर्ण हैं, जो स्रोत के उपरोक्त प्रतिनिधित्व में बच गए हैं।

इसे ऑनलाइन आज़माएं!

ओपकोड मानचित्रण और मामलों का परीक्षण करें

वर्चुअल मशीन इस ओपोड मैपिंग का उपयोग करती है ।

नीचे अपेक्षित परीक्षार्थियों के साथ अनूदित परीक्षण मामले हैं।

टेस्ट केस # 1

00 - LDX #$10  09 10
02 - INC $01   32 01
04 - DEX       44
05 - BNE $fb   55 fb

अपेक्षित उत्पादन:

09 20 32 01 44 55 fb

टेस्ट केस # 2

00 - (DATA)    e0 08 2a 02
04 - LDA $00   02 00
06 - ADC $02   2e 02
08 - STA $00   06 00
0a - LDA $01   02 01
0c - ADC $03   2e 03
0e - STA $01   06 01

अपेक्षित उत्पादन:

0a 0b 2a 02 02 00 2e 02 06 00 02 01 2e 03 06 01

टेस्ट केस # 3

00 - (DATA)    5e 01 28 00
04 - LDX #$10  09 10
06 - LSR $01   1e 01
08 - ROR $00   26 00
0a - BCC $0d   65 0d
0c - LDA $02   02 02
0e - CLC       4c
0f - ADC $21   2e 21
11 - STA $21   06 21
13 - LDA $03   02 03
15 - ADC $22   2e 22
17 - STA $22   06 22
19 - ASL $02   22 02
1b - ROL $03   2a 03
1d - DEX       44
1e - BPL $e6   5d e6
20 - HLT       00
21 - (DATA)    00 00

अपेक्षित उत्पादन:

00 00 00 00 09 10 1e 01  44 5d e6 00 b0 36

अनपैक्ड और स्वरूपित

क्योंकि कोड एक एल्गोरिथ्म के साथ संपीड़ित होता है जो एक ही वर्ण के साथ बार-बार तार को बदल देता है, यह समान कोड ब्लॉक का उपयोग करने के लिए अधिक कुशल होता है, जो कि हेल्पर फ़ंक्शंस को परिभाषित करने और इनवॉइस करने या M[A]अतिरिक्त चर में मध्यवर्ती परिणाम (जैसे ) संग्रहीत करने की तुलना में अधिक होता है।

M => p => {
  for(
    a = n = z = c = x = 0;
    a &= 255, x &= 255,
    A = [i = M[p++], p, M[p], M[p] + x][i & 3],
    i & 3 && p++,
    i && A < 256;
  ) eval((
    '(n = a = M[A], z = !(n &= 255), n &= 128);'                                + // LDA
    'M[A] = a;'                                                                 + // STA
    '(n = x = M[A], z = !(n &= 255), n &= 128);'                                + // LDX
    'M[A] = x;'                                                                 + // STX
    '(n = a &= M[A], z = !(n &= 255), n &= 128);'                               + // AND
    '(n = a |= M[A], z = !(n &= 255), n &= 128);'                               + // ORA
    '(n = a ^= M[A], z = !(n &= 255), n &= 128);'                               + // EOR
    '(n = M[A] = M[c = M[A] & 1, A] / 2, z = !(n &= 255), n &= 128);'           + // LSR
    '(n = M[A] = M[c = M[A] >> 7, A] * 2, z = !(n &= 255), n &= 128);'          + // ASL
    '(n = M[A] = c * 128 | M[c = M[A] & 1, A] / 2, z = !(n &= 255), n &= 128);' + // ROR
    '(n = M[A] = c | M[c = M[A] >> 7, A] * 2, z = !(n &= 255), n &= 128);'      + // ROL
    '(n = a += M[A] + c, c = a >> 8, z = !(n &= 255), n &= 128);'               + // ADC
    '(n = ++M[A], z = !(n &= 255), n &= 128);'                                  + // INC
    '(n = --M[A], z = !(n &= 255), n &= 128);'                                  + // DEC
    '(n = a - M[A], c = n >= 0, z = !(n &= 255), n &= 128);'                    + // CMP
    '(n = x - M[A], c = n >= 0, z = !(n &= 255), n &= 128);'                    + // CPX
    '(n = ++x, z = !(n &= 255), n &= 128);'                                     + // INX
    '(n = --x, z = !(n &= 255), n &= 128);'                                     + // DEX
    'c = 1;'                                                                    + // SEC
    'c = 0;'                                                                    + // CLC
    ' 1 && (p += (M[A] ^ 128) - 128);'                                          + // BRA
    '!z && (p += (M[A] ^ 128) - 128);'                                          + // BNE
    ' z && (p += (M[A] ^ 128) - 128);'                                          + // BEQ
    '!n && (p += (M[A] ^ 128) - 128);'                                          + // BPL
    ' n && (p += (M[A] ^ 128) - 128);'                                          + // BMI
    '!c && (p += (M[A] ^ 128) - 128);'                                          + // BCC
    ' c && (p += (M[A] ^ 128) - 128);')                                           // BCS
    .split`;`[i >> 2]
  )
}

प्रभावशाली :) जेएस समर्थक नहीं, इसलिए - क्या यह ओपकोड के मूल्य से कुछ "कोड सरणी" में अनुक्रमण है? अच्छा लगता है। लेकिन अगर इस o = ...पंक्ति को हर निर्देश के लिए निष्पादित किया जाता है, तो इसमें "अनपेक्षित ऑपकोड" हो सकता है?
फेलिक्स पैलमेन

2
मुझे शायद एक परीक्षण मामला जोड़ना चाहिए: ओ नाओ, मुझे लगता है कि अनपेक्षित ऑपकोड की अनुमति देना बेहतर होगा ... वैधता सिर्फ यहां बेकार बाइट्स की जांच करती है, लेकिन शायद नियमों को बदलने के लिए बहुत देर हो चुकी है :(
फेलिक्स पाम्स

खैर, मैं बिल्कुल सुझाव देने वाला था, क्योंकि यह चुनौती के लिए बहुत कुछ नहीं जोड़ता है और अब कस्टम मैपिंग के साथ जांचना कठिन है। लेकिन आपको शायद पहले @MaxYekhlakov से पूछना / चेतावनी देना चाहिए, क्योंकि उन्होंने नियम को सही तरीके से लागू किया होगा।
अरनुलद

c = M[A] >> 7 & 1<- क्या &1वास्तव में यहाँ ज़रूरत है?
फेलिक्स पैलमेन

2
मुझे पूरा यकीन है कि यह सबमिशन एक फंक्शन वैसे भी होगा, मेरा शब्द "बाइट्स की सूची [...] किसी भी समझदार प्रारूप" था और Uint8Arrayवास्तव में बाइट्स की ऐसी सूची को एनकैप्सुलेट करता है। तो अगर हेक्स स्ट्रिंग में बाइट्स डालना इनपुट का प्रतिनिधित्व करने का एक स्वीकार्य तरीका है, तो उन्हें कंटेनर ऑब्जेक्ट में क्यों रखा जाना चाहिए ...
फेलिक्स पालमेन

2

PHP, 581 585 555 532 बाइट्स (नहीं-प्रतिस्पर्धा)

function t($v){global$f,$c;$f=8*$c|4&$v>>5|2*!$v;}$m=array_slice($argv,2);for($f=0;$p>-1&&$p<$argc-1&&$i=$m[$p=&$argv[1]];$p++)$i&3?eval(explode(_,'$a=$y_$a&=$y_$a|=$y_$a^=$y_$a+=$y+$c;$c=$a>>8_$x=$y_$c=$y&1;$y>>=1_$c=($y*=2)>>8_$y+=$y+$c;$c=$y>>8_$y+=$c<<8;$c=$y&1;$y>>=1_$y--_$y++_$z=$a-$y,$c=$a<$y_$z=$x-$y,$c=$x<$y_$y=$a_$y=$x_'.$y=&$m[[0,++$p,$g=$m[$p],$g+$x][$i&3]])[$i>>=2].'$i<14&&t(${[aaaaaxyyyyyyzz][$i]}&=255);'):($i&32?$p+=($f>>$i/8-4^$i)&1?($y=$m[++$p])-($y>>7<<8):1:($i&8?$f=$f&7|8*$c=$i/4:t($x+=$i/2-9)));print_r($m);

कमांड लाइन के तर्कों से आधार 10 पूर्णांक के रूप में पीसी और ओपी कोड लेता है,
स्मृति को सूची के रूप में प्रिंट करता है [base 10 address] => base 10 value

यह अभी तक पूरी तरह से परीक्षण नहीं किया गया है ; लेकिन वहाँ एक टूटने है

अगर वहाँ कोड नक्शा और here's मेरी मानचित्रण के लिए एक सिंहावलोकन:

3-mode instructions:
00: LDA     04: AND     08: ORA     0C: EOR
10: ADC     14: LDX     18: LSR     1C: ASL
20: ROL     24: ROR     28: DEC     2C: INC
30: CMP     34: CPX     38: STA     3C: STX

+1: immediate
+2: absolute
+3: relative

implicit:
00: HLT
10: DEX 14: INX
18: CLC 1C: SEC

relative:
20: BRA         (0)
28: BNE 2C: BEQ (Z)
30: BPL 34: BMI (N)
38: BCC 3C: BCS (C)

साइड नोट:
कोड के 24परिणाम में BNV(शाखा कभी नहीं = 2 बाइट NOP);
04, 08, 0Cकल्पित नामों हैं INX, CLCऔर SEC
और कुछ भी ऊपर 3Fया तो एक दो बाइट है NOPया एकल मोड निर्देश के लिए उपनाम।

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