8bit sanal makine


31

Arka fon

Eski 8-bit 6502 çipimi seviyorum. 6502 makine kodunda PPCG'deki bazı zorlukları çözmek bile eğlenceli. Ancak basit olması gereken bazı şeyler (veri okuma veya stdout çıktısı gibi) makine kodunda gereksiz yere zahmetlidir. Yani aklımda kaba bir fikir var: 6502'den ilham alan kendi 8-bit sanal makinemi icat et, ancak tasarım zorluklar için daha kullanışlı olacak şekilde değiştirildi. Bir şeyleri uygulamaya başlayarak, VM'nin tasarımı tamamen minimuma indirilirse bunun kendisinin güzel bir zorluk olabileceğini fark ettim :)

Görev

Aşağıdaki özelliklere uygun 8 bitlik sanal bir makine uygulayın. Bu , bu yüzden en az bayt olan uygulama kazanır.

Giriş

Uygulamanız aşağıdaki girdileri almalıdır:

  • Tek bir imzasız bayt pc, bu ilk program sayacıdır (VM'nin yürütmeye başladığı bellekteki adres, 0temelli)

  • Maksimum giriş uzunluğuna sahip bir bayt listesi 256, bu sanal makine için RAM'dir (başlangıç ​​içeriği ile)

Bu girişi herhangi bir makul biçimde alabilirsiniz.

Çıktı

VM sona erdikten sonra RAM'in son içeriği olan baytların listesi (aşağıya bakınız). Sonunda yalnızca sona erdirmeye yol açan girdiler aldığınızı varsayabilirsiniz. Herhangi bir mantıklı formata izin verilir.

Sanal işlemci

Sanal işlemci var

  • 8 bitlik program sayacı,
  • adı verilen 8 bitlik bir akümülatör kaydı A ve
  • adı verilen 8 bitlik bir dizin kaydı X.

Üç durum bayrağı vardır:

  • Z - Sıfır bayrağı bazı işlemlerin sonuçlanmasından sonra ayarlanır. 0
  • N - Negatif bayrak, bazı işlemlerin negatif bir sayıyla sonuçlanmasından sonra ayarlanır (sonucun iow bit 7'si ayarlanır)
  • C - taşıma bayrağı sonucun "eksik" kısmı için toplama ve kaymalar ile belirlenir

Başlaması üzerine, bayraklar tüm program sayacı belirli bir değeri ve içeriğine ayarlanır, silinir Ave Xbelirsiz bulunmaktadır.

8 bitlik değerler

  • Bir işaretsiz arasında bir tam sayı[0..255]
  • Bir imzalı aralıkta tamsayı, 2'ye tümleme,[-128..127]

bağlama bağlı olarak. Bir işlemin üstünden veya altından geçmesi durumunda, değer etrafına sarılır (ve ilave olması durumunda taşıma bayrağı etkilenir).

Sonlandırma

Sanal makine ne zaman sona erer?

  • Bir HLTeğitime ulaşıldı
  • Mevcut olmayan bir hafıza adresine erişildi.
  • Program sayacı hafızanın dışında çalışır (VM'ye tam 256 byte hafıza verilmiş olsa bile etrafına sarılmadığını unutmayın)

Adresleme modları

  • örtülü - komutun argümanı yok, işlenen ima edildi
  • acil - işlenen komuttan hemen sonra bayt olur
  • göreceli - (yalnızca dallanma için) komut imzalandıktan sonra bayt (2'nin tamamlayıcısı) ve dal alındığında program sayacına eklenecek dengeyi belirler.0aşağıdaki talimatın yeri
  • kesin - komuttan sonraki bayt işlenenin adresidir
  • İndekslenmiş - komut artıdan sonraki bayt X(kayıt) işlenenin adresidir

Talimatlar

Her komut bir opcode (bir bayt) ve adresleme modlarında anında , göreceli , mutlak ve ikinci bir argüman baytını indekslenmiş olarak içerir . Sanal CPU bir komut çalıştırdığında, program sayacını buna göre arttırır ( 1veya2 ).

Burada gösterilen tüm kodlar hex cinsindendir.

  • LDA - içine işlenen yük A

    • Kodlar: anında:, 00mutlak:, 02dizinlenmiş:04
    • Bayraklar: Z,N
  • STA- Aoperand içine saklamak

    • Kodlar: anında:, 08mutlak:, 0adizinlenmiş:0c
  • LDX - içine işlenen yük X

    • Kodlar: anında:, 10mutlak:, 12dizinlenmiş:14
    • Bayraklar: Z,N
  • STX- Xoperand içine saklamak

    • Kodlar: anında:, 18mutlak:, 1adizinlenmiş:1c
  • AND- bitsel ve içinde Ave içine işlenenA

    • Kodlar: anında:, 30mutlak:, 32dizinlenmiş:34
    • Bayraklar: Z,N
  • ORA- bit düzeyinde ya ait Ave içine, işlenenA

    • Kodlar: anında:, 38mutlak:, 3adizinlenmiş:3c
    • Bayraklar: Z,N
  • EOR- bitsel xor (özel veya) Ave operandA

    • Kodlar: anında:, 40mutlak:, 42dizinlenmiş:44
    • Bayraklar: Z,N
  • LSR - Mantıksal sağa kayma, işlenenin tüm bitlerini bir sağa kaydırın, bit 0 devam eder

    • Kodlar: anında:, 48mutlak:, 4adizinlenmiş:4c
    • Bayraklar: Z, N,C
  • ASL - aritmetik sola kaydırma, işlenenin tüm bitlerini bir yerde sola kaydırma, bit 7 taşımaya gider

    • Kodlar: anında:, 50mutlak:, 52dizinlenmiş:54
    • Bayraklar: Z, N,C
  • ROR - sağa dönün, tüm işlenenin bitlerini bir yere sağa kaydırın, taşıma bit 7'ye gider, bit 0 taşınmaya gider

    • Kodlar: anında:, 58mutlak:, 5adizinlenmiş:5c
    • Bayraklar: Z, N,C
  • ROL - sola dönün, işlemcinin tüm bitlerini bir yerden sola kaydırın, taşıma biti 0'a, biti 7 taşımaya gider

    • Kodlar: anında:, 60mutlak:, 62dizinlenmiş:64
    • Bayraklar: Z, N,C
  • ADC- taşıma ile ekle, işlenen artı taşıma eklenir A, taşıma taşma üzerine kurulur

    • Kodlar: anında:, 68mutlak:, 6adizinlenmiş:6c
    • Bayraklar: Z, N,C
  • INC - operand bir artış

    • Kodlar: anında:, 78mutlak:, 7adizinlenmiş:7c
    • Bayraklar: Z,N
  • DEC - birer birer operand azalması

    • Kodlar: anında:, 80mutlak:, 82dizinlenmiş:84
    • Bayraklar: Z,N
  • CMP- Aoperand'ı çıkartarak operand ile karşılaştırmak A, sonucu unutmak. Taşıma işlemi akış altında temizlendi, aksi takdirde ayarlandı

    • Kodlar: anında:, 88mutlak:, 8adizinlenmiş:8c
    • Bayraklar: Z, N,C
  • CPX- Karşılaştırma X- aynı CMPiçinX

    • Kodlar: anında:, 90mutlak:, 92dizinlenmiş:94
    • Bayraklar: Z, N,C
  • HLT - sonlandır

    • Kodlar: kapalı: c0
  • INX- artış Xtarafından

    • Kodlar: kapalı: c8
    • Bayraklar: Z,N
  • DEX- eksiltme Xteker

    • Kodlar: kapalı: c9
    • Bayraklar: Z,N
  • SEC - taşıma bayrağı ayarla

    • Kodlar: kapalı: d0
    • Bayraklar: C
  • CLC - açık taşıma bayrağı

    • Kodlar: kapalı: d1
    • Bayraklar: C
  • BRA - şube her zaman

    • Opcodes: göreceli: f2
  • BNE- Zbayrak temizlenirse dal

    • Opcodes: göreceli: f4
  • BEQ- Zbayrak ayarlanmışsa dal

    • Opcodes: göreceli: f6
  • BPL- Nbayrak temizlenirse dal

    • Opcodes: göreceli: f8
  • BMI- Nbayrak ayarlanmışsa dal

    • Opcodes: göreceli: fa
  • BCC- Cbayrak temizlenirse dal

    • Opcodes: göreceli: fc
  • BCS- Cbayrak ayarlanmışsa dal

    • Opcodes: göreceli: fe

opcodes

Yukarıdaki listeden geçerli bir talimat ile eşleşmeyen herhangi bir opcode bulunursa, VM'nin davranışı tanımlanmaz.

Gereğince Jonathan Allan isteği , sen olabilir yerine gösterilen opcodes opcodes Kendi tercih kılavuzu bölümünde. Bunu yaparsan, yapmalısın Cevabınız yukarıda kullanılan opcodes tam eşleme ekleyin.

Eşleme, çiftli bir onaltılık dosya olmalıdır <official opcode> <your opcode>, örneğin, iki işlem kodunu değiştirdiyseniz:

f4 f5
10 11

Newlines burada önemli değil.

Test vakaları (resmi kodlamalar)

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

Daha sonra daha fazla test çantası ekleyebilirim.

Referans ve test

Kendi denemelerine yardımcı olmak için işte bazı (tamamen golf oynamayan) referans uygulaması - izleme bilgilerini (demonte talimatlar dahil) verebilir.stderr ve bunları dönüştürebilir.

Kaynak almanın önerilen yolu:

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

Veya ödeme şubesi challengeve birgit submodule update --init --recursive sistemimi almak için klonladıktan sonra yapın.

Aracı GNU make ile oluşturun (yalnızca yazın makeveya gmakesisteminizde varsayılan make GNU make değil).

Kullanım :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram

  • -s startpc - ilk program sayacı, varsayılan olarak 0
  • -h - giriş onaltılık (aksi durumda ikili)
  • -t - icra takibi stderr
  • -c convfile - kodları belirtilen haritaya göre dönüştür convfile
  • -d - sonuçtaki belleği ikili veri olarak çıkar
  • -x - elde edilen belleği hex olarak
  • initial_ram - başlangıçtaki RAM içeriği, onaltılı veya ikili

Dönüştürme özelliğinin çalışırken kodları değiştiren programlarda başarısız olacağını unutmayın .

Feragatname: Yukarıdaki kurallar ve şartnameler bu araç için değil meydan okuma için yetkilidir. Bu özellikle opcode dönüştürme özelliği için geçerlidir. Burada sunulan aracın teknik özelliklerde bir hata olduğunu düşünüyorsanız, lütfen bir yorumda bulunun :)


1
Talimatlar için farklı işlem kodları seçerek, muhtemelen sayısız golf oynama fırsatı olacağını hayal ediyorum, ancak işlem kodları düzeltilmiş görünüyor (komut seti makineyi tanımlayan şey olmasına rağmen). Belki de uygulamaların kendi kod sayfalarına sahip olmasına izin vermeyi düşünmeye değer mi?
Jonathan Allan,

1
@JonathanAllan iki kez düşündü, şimdi izin veriyorum ve diğer opkot gruplarını kullanarak kolayca test edilebilir çözümler üretmek için bir "dönüşüm" aracı ekleyebilirim.
Felix Palmen

1
@Bu izin vermek için gerekçemi bilmem, özel davaların miktarını azaltmaktı, bu yüzden daha iyi "golf oynayabilir" olmalıydı - her bir kod ya kapalı, göreceli bir dal ya da diğer üç adresleme moduna izin veriyor :)
Felix Palmen

1
eğer BRA( "her zaman şube") kontrol akışında bir şube tanıtmak değil, o denilen olmamalıdır JMP?
ngn

1
Her şeyden BRAönce 65C02 ve MC 68000 gibi yonga tasarımlarında (6502'nin böyle bir talimatı yoktur) JMPvardır. Fark, BRAgöreceli adresleme ve JMPmutlak adreslemedir. Bu yüzden, sadece bu tasarımları takip ettim - aslında, bu kadar mantıklı gelmiyor;)
Felix Palmen

Yanıtlar:


16

C (gcc) , 1381 1338 1255 1073 bayt

Ceilingcat ve Rogem sayesinde büyük gelişme.

#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]();}

Çevrimiçi deneyin!

Birçok tanımlayıcı derleyici bayraklarına taşındı.

Açıklama (ÇOK ungolfed):

#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] ();
}

Harika iş, anlık +1, ilk önce gerçekten bir C çözümü beklemiyordum :) 00baytların eklenmesi kuralları biraz esnetiyor olabilir ... itiraf ediyorum bu kodu analiz etmeyi denemedim ... belki bayt onaltılık yerine ikili G / Ç yapıyor? Kurallara izin
verilirdi

Yasadışı işlem kodlarının tanımsız olduğunu söyleyerek yasadışı işlem kodlarının sona ermesine yol açtığı kuralını değiştirmek istiyorum ... cevabınıza zarar verir mi?
Felix Palmen

@FelixPalmen de, sonlandırma oldukça geçerli "tanımsız" bir davranış olduğu sürece, zarar vermez (yerine golf oynamak için yeni bir olasılık açar!)
oynamak Max Yekhlakov

@MaxYekhlakov "incitmek" derken, çözümünüze haksızlık etmek istedim çünkü yasadışı bir işlem kodunun vm'yi sonlandırdığından emin olmak için "bayt harcadınız". Kural değişikliğini bir fırsat olarak kabul etmenize sevindim :) Ve yine tebrikler, tüm zamanların en sevdiğim programlama dili olan C'de bir çözüm görmeyi çok seviyorum. Nadiren C kod-golf mücadelesini kazanmanız çok üzücü - yine de, "golf" C sadece harika :)
Felix Palmen

Gönderilecek bayraklar bölümünü ekleyebilir misiniz?
l4m2

8

APL (Dyalog Classic) , 397 332 330 bayt

teşekkürler -8 bayt için Adám

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←⍵}

Çevrimiçi deneyin!

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


Bu çözüm istenmeyen kodlara sahip mi ve olmasa da baytları onlardan uzaklaştırarak mı geçirdiniz?
Felix Palmen

@FelixPalmen Şimdi bahsettiğinize göre, evet :( İlk başta bu kuralı gözetmiştim, ancak golf oynadığım sırada yanlışlıkla 4, 5 ve muhtemelen diğerlerini de geçerli opcod'lar yaptım.
ngn

2
Şimdi bittiğine göre, ilk bakışta en iyi karar olmadığının farkındayım ve @ MaxYekhlakov ne yazık ki kural değişikliği hakkında bir şey söylemek zorunda değildi.
Felix Palmen

İhtiyacın var f←
Outgolfer Erik,

8

C (gcc) , 487 , 480 , 463 , 452 , 447 , 438 bayt

Bu talimat eşlemesini kullanır . Talimatlara yapılan güncelleme 9 bayt ve potansiyel olarak daha da ileri gitti. İlk argümanın ( M) gösterdiği hafızayı değiştirerek döner . Bazı baytları tıraş ettiğiniz için @ceilingcat'e teşekkür ederiz.

Bayraklarla derlenmelidir -DO=*o -DD=*d -DI(e,a,b)=if(e){a;}else{b;} -Du="unsigned char"(zaten baytlara dahil).

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

Çevrimiçi deneyin!

Preprocessor

-DO=*o -DD=*d

Bu ikisi, işaretçilerden vazgeçmenin daha kısa bir yolunu sağlar.

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

İf-elses ve tip bildirimleri için gereken bayt sayısını azaltın.

kod

Aşağıda, önişlemci direktifleri genişletilmiş ve okunabilirlik için yeniden adlandırılmış değişkenlerle kodun insan tarafından okunabilen bir versiyonu verilmiştir.

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

Talimatlar

Talimatlar aşağıdaki gibi yapılandırılmıştır:

  • 6-7 Bit, komutun arititesini gösterir ( 00Nullary, Unary 01, 10Binary, 11Binary)

  • 0-2 Bit, işleneni (ler) R=0belirler : seçer Ave R=1seçer X. OP=00register'ı bir operand olarak kullanır, OP=01anında operand'ı OP=10seçer, mutlak operand'ı OP=11seçer ve indekslenmiş operand'ı seçer.

    • Fark etmiş olabileceğiniz gibi, bu Xnormalde şartname başına kullanılamamış olsalar bile, her iki kayıtta da herhangi bir işlem yapılmasına izin verir (yine de yalnızca dizinden dizin oluşturabilirsiniz ). Örneğin INC A, ADC X, 10ve ASL Xtüm işler.
  • Bit 3-5, dallanma koşulunu belirler: bitlerden birine sahip olmak, hangi bayrağın test edileceğini gösterir (bit 3-> C, bit 4-> N, bit 5-> Z). Sadece bir bit ayarlanmışsa, talimat bir set bayrağını test eder. Belirsiz bir bayrağı test etmek için, bitlerin tamamlayıcısını alın. Örneğin , set taşıma için 110ve 001set taşıma için testler . 111ve 000her zaman şube.

  • Ayrıca, bir yazmaçta depolanan bir adres ofsetine dallanabilir, bu sayede fonksiyon yazmanıza izin verebilir veya standart indeksleme modlarını kullanabilirsiniz. OP=01şartname dalı gibi davranır.

+-----+----------+-------+-----------------------------------------------+
| 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

JavaScript (ES6), 361 bayt

Olarak girdi Alır (memory)(program_counter)nerede, memorybir olduğunu Uint8Array. Bu diziyi değiştirerek çıktı verir .

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(_)}

Not: Kod, RegPack ile sıkıştırılmıştır ve tümü kaynağın yukarıdaki sunumunda kaçan yazdırılamayan çok sayıda karakter içerir.

Çevrimiçi deneyin!

Opcode eşleme ve test durumları

Sanal makine bu opcode eşlemesini kullanır .

Beklenen çıktılar ile birlikte çevrilmiş test senaryoları aşağıdadır.

Test durumu # 1

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

Beklenen çıktı:

09 20 32 01 44 55 fb

Sınama durumu # 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

Beklenen çıktı:

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

Test durumu # 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

Beklenen çıktı:

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

Ambalajsız ve biçimlendirilmiş

Kod, sık sık tekrarlanan dizeleri tek bir karakterle değiştiren bir algoritma ile sıkıştırıldığından, aynı kod bloklarını tekrar tekrar kullanmak, yardımcı fonksiyonları tanımlamak ve çağırmaktan veya ara sonuçları (örneğin M[A]) ek değişkenlerde saklamaktan daha etkilidir .

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]
  )
}

Etkileyici :) Bir JS uzmanı değil, yani - bu, opcode değerine göre bazı "kod dizileri" ne endeksliyor mu? güzel gözüküyor. Ancak, bu o = ...satır her talimat için gerçekleştirilirse, "istenmeyen işlem kodları" olabilir mi?
Felix Palmen

2
Muhtemelen bir test örneği eklemeliyim: o Şimdi, istenmeyen kodlara izin vermenin daha iyi olacağını düşünüyorum ... geçerlilik burada sadece bayt israflarını denetler, ancak kuralları değiştirmek için muhtemelen çok geç :(
Felix Palmen

Tam da bunu önermek üzereydim, çünkü bu zorluğa fazla bir şey katmıyor ve artık özel haritalamalarla kontrol etmek zor. Ancak, ilk önce @MaxYekhlakov'a sormalı / uyarmalı, çünkü kuralı doğru uygulamış olabilirler.
Arnauld,

c = M[A] >> 7 & 1<- edilmektedir &1gerçekten burada gerekli?
Felix Palmen

2
Gönderiminiz zaten bir işlev olduğu için eminim, ifadelerim "bir bayt listesi [...] herhangi bir makul biçim" ve Uint8Arraygerçekten de sadece bir bayt listesini kapsıyordu. Öyleyse, baytları onaltılık bir dizgeye koymak, girdiyi temsil etmenin kabul edilebilir bir yoluysa, neden onları bir konteyner nesnesine koymak yasaklanmalı ...
Felix Palmen

2

PHP, 581 585 555 532 bayt (rekabet etmeyen)

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

PC ve ve OP kodlarını komut satırı argümanlarından 10 tamsayı olarak alır,
hafızayı bir liste olarak yazdırır [base 10 address] => base 10 value.

Bu henüz tam olarak test edilmedi ; ama bir arıza var .

There's kod haritası ve here's benim haritalama bir bakış:

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)

not:
kod 24, bir sonuç verir BNV(dal asla = 2 bayt NOP);
04, 08, 0CRumuzudur INX, CLCve SEC
ve bir şey üzerinde 3Fya da bir, iki baytlık bir NOPya da tek modlu talimatları için bir diğer.

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.