Mesin virtual 8bit


31

Latar Belakang

Saya suka chip 6502 8-bit lama saya. Bahkan menyenangkan untuk menyelesaikan beberapa tantangan di sini di PPCG dalam kode mesin 6502. Tetapi beberapa hal yang seharusnya sederhana (seperti, membaca data atau output ke stdout) tidak perlu rumit untuk dilakukan dalam kode mesin. Jadi ada ide kasar di pikiran saya: Ciptakan mesin virtual 8-bit saya sendiri yang terinspirasi oleh 6502, tetapi dengan desain yang dimodifikasi agar lebih dapat digunakan untuk tantangan. Mulai menerapkan sesuatu, saya menyadari ini mungkin tantangan yang bagus jika desain VM dikurangi seminimal mungkin :)

Tugas

Menerapkan mesin virtual 8-bit yang sesuai dengan spesifikasi berikut. Ini adalah , sehingga implementasi dengan byte paling sedikit menang.

Memasukkan

Implementasi Anda harus mengambil input berikut:

  • Byte tunggal yang tidak ditandatangani pc, ini adalah penghitung program awal (alamat di memori tempat VM memulai eksekusi, 0berbasis)

  • Daftar byte dengan panjang 256entri maksimum , ini adalah RAM untuk mesin virtual (dengan konten awalnya)

Anda dapat mengambil input ini dalam format apa pun yang masuk akal.

Keluaran

Daftar byte yang merupakan isi terakhir RAM setelah VM berakhir (lihat di bawah). Anda dapat berasumsi bahwa Anda hanya mendapatkan input yang pada akhirnya akan berakhir. Format apa pun yang masuk akal diizinkan.

CPU virtual

CPU virtual memiliki

  • penghitung program 8-bit,
  • register akumulator 8-bit disebut A dan
  • register indeks 8-bit disebut X.

Ada tiga bendera status:

  • Z - bendera nol ditetapkan setelah beberapa operasi menghasilkan 0
  • N - Bendera negatif ditetapkan setelah beberapa operasi menghasilkan angka negatif (iow sedikit 7 dari hasil ditetapkan)
  • C - flag carry diatur dengan penambahan dan pergeseran untuk bit "hilang" dari hasilnya

Setelah mulai, semua bendera dibersihkan, penghitung program diatur ke nilai yang diberikan dan isi dari Adan Xtidak ditentukan.

Nilai 8-bit mewakili keduanya

  • sebuah unsigned integer dalam kisaran[0..255]
  • a ditandatangani integer, komplemen 2, dalam kisaran[-128..127]

tergantung pada konteksnya. Jika suatu operasi over atau underflow, nilainya membungkus (dan dalam hal penambahan, flag carry dipengaruhi).

Penghentian

Mesin virtual berakhir ketika

  • Sebuah HLTinstruksi tercapai
  • Alamat memori yang tidak ada diakses
  • Penghitung program berjalan di luar memori (perhatikan itu tidak membungkus bahkan jika VM diberikan 256 byte penuh memori)

Mode adressing

  • implisit - instruksi tidak memiliki argumen, operan tersirat
  • segera - operan adalah byte langsung setelah instruksi
  • relatif - (hanya untuk percabangan) byte setelah instruksi ditandatangani (komplemen 2's) dan menentukan offset untuk ditambahkan ke penghitung program jika cabang diambil. 0adalah lokasi instruksi berikut
  • absolut - byte setelah instruksi adalah alamat operan
  • diindeks - byte setelah instruksi plus X(register) adalah alamat operan

Instruksi

Setiap instruksi terdiri dari opcode (satu byte) dan, dalam mode pengalamatan segera , relatif , absolut dan diindeks byte argumen kedua. Ketika CPU virtual menjalankan instruksi, itu menambah penghitung program yang sesuai (oleh 1atau2 ).

Semua opcode yang ditampilkan di sini adalah dalam hex.

  • LDA - muat operan ke A

    • Opcode: langsung:, 00absolut 02:, diindeks:04
    • Flags: Z,N
  • STA- simpan Ake dalam operan

    • Opcode: langsung:, 08absolut 0a:, diindeks:0c
  • LDX - muat operan ke X

    • Opcode: langsung:, 10absolut 12:, diindeks:14
    • Flags: Z,N
  • STX- simpan Xke dalam operan

    • Opcode: langsung:, 18absolut 1a:, diindeks:1c
  • AND- bitwise dan of Adan operan keA

    • Opcode: langsung:, 30absolut 32:, diindeks:34
    • Flags: Z,N
  • ORA- bitwise atau dari Adan operan keA

    • Opcode: langsung:, 38absolut 3a:, diindeks:3c
    • Flags: Z,N
  • EOR- bitwise xor (eksklusif atau) dari Adan operan keA

    • Opcode: langsung:, 40absolut 42:, diindeks:44
    • Flags: Z,N
  • LSR - Pergeseran logis ke kanan, geser semua bit operan satu tempat ke kanan, bit 0 pergi untuk dibawa

    • Opcode: langsung:, 48absolut 4a:, diindeks:4c
    • Flags: Z, N,C
  • ASL - Aritmatika bergeser ke kiri, menggeser semua bit operan satu tempat ke kiri, bit 7 pergi untuk membawa

    • Opcode: langsung:, 50absolut 52:, diindeks:54
    • Flags: Z, N,C
  • ROR - Putar ke kanan, geser semua bit operan satu tempat ke kanan, carry pergi ke bit 7, bit 0 pergi ke carry

    • Opcode: langsung:, 58absolut 5a:, diindeks:5c
    • Flags: Z, N,C
  • ROL - Putar ke kiri, geser semua bit operan satu tempat ke kiri, bawa pergi ke bit 0, bit 7 pergi untuk membawa

    • Opcode: langsung:, 60absolut 62:, diindeks:64
    • Flags: Z, N,C
  • ADC- tambahkan dengan carry, operan plus carry ditambahkan ke A, carry diset pada overflow

    • Opcode: langsung:, 68absolut 6a:, diindeks:6c
    • Flags: Z, N,C
  • INC - operan kenaikan satu

    • Opcode: langsung:, 78absolut 7a:, diindeks:7c
    • Flags: Z,N
  • DEC - operan decrement oleh satu

    • Opcode: langsung:, 80absolut 82:, diindeks:84
    • Flags: Z,N
  • CMP- bandingkan Adengan operan dengan mengurangi operan dari A, lupakan hasil. Carry dibersihkan saat underflow, atur sebaliknya

    • Opcode: langsung:, 88absolut 8a:, diindeks:8c
    • Flags: Z, N,C
  • CPX- bandingkan X- sama seperti CMPuntukX

    • Opcode: langsung:, 90absolut 92:, diindeks:94
    • Flags: Z, N,C
  • HLT -- mengakhiri

    • Opcode: tersirat: c0
  • INX- selisih Xsatu

    • Opcode: tersirat: c8
    • Flags: Z,N
  • DEX- pengurangan Xsatu per satu

    • Opcode: tersirat: c9
    • Flags: Z,N
  • SEC - atur flag carry

    • Opcode: tersirat: d0
    • Bendera: C
  • CLC - Membawa bendera yang jelas

    • Opcode: tersirat: d1
    • Bendera: C
  • BRA - cabang selalu

    • Opcode: relatif: f2
  • BNE- Cabang jika Zbendera dibersihkan

    • Opcode: relatif: f4
  • BEQ- cabang jika Zflag diatur

    • Opcode: relatif: f6
  • BPL- Cabang jika Nbendera dibersihkan

    • Opcode: relatif: f8
  • BMI- cabang jika Nflag diatur

    • Opcode: relatif: fa
  • BCC- Cabang jika Cbendera dibersihkan

    • Opcode: relatif: fc
  • BCS- cabang jika Cflag diatur

    • Opcode: relatif: fe

Opcode

Perilaku VM tidak terdefinisi jika ada opcode ditemukan yang tidak memetakan ke instruksi yang valid dari daftar di atas.

Sesuai permintaan Jonathan Allan , Anda dapat memilih set opcodes Anda sendiri alih-alih opcode yang diperlihatkan di bagian Instruksi . Jika Anda melakukannya, Anda harus melakukannya menambahkan pemetaan penuh ke opcodes yang digunakan di atas dalam jawaban Anda.

Pemetaan harus berupa file hex berpasangan <official opcode> <your opcode>, misalnya jika Anda mengganti dua opcode:

f4 f5
10 11

Baris baru tidak masalah di sini.

Test case (opcodes resmi)

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

Saya mungkin menambahkan lebih banyak testcases nanti.

Referensi dan pengujian

Untuk membantu dengan eksperimen sendiri, berikut ini beberapa implementasi referensi (yang sama sekali tidak golf) - ia dapat menampilkan informasi penelusuran (termasuk instruksi yang dibongkar) kestderr dan mengonversi opcode saat berjalan.

Cara yang disarankan untuk mendapatkan sumber:

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

Atau checkout cabang challengedan lakukan agit submodule update --init --recursive setelah kloning, untuk mendapatkan sistem build kustom saya.

Bangun alat dengan GNU make (ketik saja make, atau gmakejika di sistem Anda, make default bukan GNU make).

Penggunaan :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram

  • -s startpc - penghitung program awal, default ke 0
  • -h - input dalam hex (jika tidak biner)
  • -t - lacak eksekusi ke stderr
  • -c convfile - Mengonversi opcodes sesuai dengan pemetaan yang diberikan di convfile
  • -d - dump memori yang dihasilkan sebagai data biner
  • -x - dump memori yang dihasilkan sebagai hex
  • initial_ram - Konten RAM awal, baik hex atau biner

Perhatikan bahwa fitur konversi akan gagal pada program yang memodifikasi opcodes saat menjalankan.

Penafian: Aturan dan spesifikasi di atas adalah otoritatif untuk tantangan, bukan alat ini. Ini terutama berlaku untuk fitur konversi opcode. Jika menurut Anda alat yang disajikan di sini memiliki bug yang terkait dengan spesifikasi, silakan laporkan dalam komentar :)


1
Saya membayangkan bahwa mungkin ada banyak peluang golf yang bisa didapat dengan memilih opcodes yang berbeda untuk instruksinya tetapi tampaknya opcodes telah diperbaiki (walaupun set instruksi adalah apa yang mendefinisikan mesin). Mungkin ada baiknya mempertimbangkan mengizinkan implementasi untuk memiliki halaman kode mereka sendiri?
Jonathan Allan

1
@JonathanAllan berpikir dua kali tentang hal itu, saya mengizinkannya sekarang dan mungkin menambahkan alat "konversi" untuk membuat solusi menggunakan set opcode lain yang mudah diuji.
Felix Palmen

1
@Arnauld btw alasan saya untuk mengizinkan ini adalah untuk mengurangi jumlah kasus khusus, jadi itu harus lebih baik "golfable" - setiap opcode baik implisit, cabang relatif atau memungkinkan ketiga mode pengalamatan lainnya :)
Felix Palmen

1
jika BRA("cabang selalu") tidak memperkenalkan cabang dalam aliran kontrol, bukankah seharusnya disebut JMP?
ngn

1
@ ngn baik, BRAada dalam desain chip kemudian (6502 tidak memiliki instruksi seperti itu) seperti 65C02 dan MC 68000. JMPada juga. Perbedaannya adalah yang BRAmenggunakan pengalamatan relatif dan JMPmenggunakan pengalamatan absolut. Jadi, saya hanya mengikuti desain ini - memang, itu tidak terdengar masuk akal;)
Felix Palmen

Jawaban:


16

C (gcc) , 1381 1338 1255 1073 byte

Peningkatan besar berkat ceilingcat dan Rogem.

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

Cobalah online!

Banyak definisi yang dipindahkan ke flag compiler.

Penjelasan (SANGAT tidak diserang):

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

Kerja bagus, +1 instan, benar-benar tidak mengharapkan solusi C dulu :) penambahan 00byte Anda mungkin sedikit membengkokkan aturannya ... Saya akui saya tidak mencoba menganalisis kode ini ... mungkin Anda bisa menyimpan byte melakukan I / O dalam biner, bukan hex? Akan diizinkan oleh aturan :)
Felix Palmen

Saya ingin mengganti aturan bahwa opcode ilegal mengarah pada penghentian hanya dengan mengatakan perilaku opcode ilegal tidak ditentukan ... apakah ini akan melukai jawaban Anda atau Anda tidak keberatan dengan itu?
Felix Palmen

@ Feliksmen juga, selama pemutusan cukup "tidak terdefinisi" perilaku, itu tidak akan sakit (itu membuka kemungkinan baru untuk bermain golf sebagai gantinya!)
Max Yekhlakov

@ Maxyekhlakov oleh "sakit" Maksud saya tidak adil terhadap solusi Anda karena Anda mungkin "menghabiskan byte" untuk memastikan opcode ilegal mengakhiri vm. Saya senang Anda menyambut perubahan aturan sebagai kesempatan :) Dan sekali lagi, selamat, saya hanya senang melihat solusi dalam C, yang merupakan bahasa pemrograman favorit saya sepanjang masa. Sayang sekali Anda jarang akan memenangkan tantangan kode-golf di C - namun, "golf" C itu keren :)
Felix Palmen

Bisakah Anda menambahkan bagian bendera untuk dikirim?
14m2

8

APL (Dyalog Classic) , 397 332 330 byte

terima kasih @ Adm untuk -8 byte

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

Cobalah online!

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


Apakah solusi ini memiliki opcode yang tidak diinginkan dan jika tidak, apakah Anda menghabiskan byte untuk menghindarinya? Lihat komentar ini karena alasan yang saya tanyakan ...
Felix Palmen

@ Feliksmenen Sekarang Anda menyebutkannya, ya :( Awalnya saya mengamati aturan itu, tapi ketika saya bermain golf saya tidak sengaja membuat 4, 5, dan mungkin yang lain, opcode yang valid. Jadi keputusan untuk membuat perilaku mereka tidak terdefinisi akan disambut dengan sangat baik :)
ngn

2
selesai sekarang, saya menyadari itu bukan keputusan terbaik di tempat pertama dan @MaxYekhlakov sayangnya tidak perlu mengatakan apa-apa tentang perubahan aturan.
Felix Palmen

Apakah Anda perlu f←?
Erik the Outgolfer

8

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

Gunakan pemetaan instruksi ini . Pembaruan untuk petunjuk berkurang 9 byte, dan berpotensi lebih banyak di masa depan. Mengembalikan dengan memodifikasi memori yang ditunjukkan oleh argumen pertama (M ). Terima kasih kepada @ceilingcat untuk mencukur beberapa byte.

Harus dikompilasi dengan flag -DO=*o -DD=*d -DI(e,a,b)=if(e){a;}else{b;} -Du="unsigned char"(sudah termasuk dalam bytes).

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

Cobalah online!

Preprosesor

-DO=*o -DD=*d

Keduanya menyediakan cara yang lebih pendek untuk merujuk referensi.

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

Kurangi jumlah byte yang dibutuhkan untuk if-elses dan ketik deklarasi.

Kode

Di bawah ini adalah versi kode yang dapat dibaca manusia, dengan arahan preprosesor diperluas, dan variabel diubah namanya agar mudah dibaca.

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

Instruksi

Petunjuk disusun sebagai berikut:

  • Bits 6-7 menunjukkan arity instruksi ( 00Nullary, 01Unary, 10Binary, 11Binary)

  • Bit 0-2 menentukan operan: R=0memilih Adan R=1memilih X. OP=00menggunakan register sebagai operan, OP=01memilih operan langsung, OP=10memilih operan absolut dan OP=11memilih operan yang diindeks.

    • Seperti yang mungkin telah Anda perhatikan, ini memungkinkan operasi apa pun dilakukan pada kedua register (meskipun Anda masih dapat hanya mengindeks dari X) bahkan ketika mereka biasanya tidak dapat digunakan sesuai spesifikasi. Misalnya INC A, ADC X, 10dan ASL Xsemua pekerjaan.
  • Bits 3-5 menentukan kondisi untuk percabangan: memiliki salah satu bit mengindikasikan flag mana yang akan diuji (bit 3-> C, bit 4-> N, bit 5-> Z). Jika hanya satu bit yang diatur, instruksi akan menguji flag yang ditetapkan. Untuk menguji flag yang belum disetel, ambil komplemen bit. Misalnya 110tes untuk carry yang tidak disetel dan 001untuk carry yang diset. 111dan 000cabang selalu.

  • Anda juga dapat bercabang ke offset alamat yang disimpan dalam register, memungkinkan Anda untuk menulis fungsi, atau Anda dapat menggunakan mode pengindeksan standar. OP=01berperilaku seperti cabang spesifikasi.

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

Mengambil input sebagai (memory)(program_counter), di mana memoryanUint8Array . Output dengan memodifikasi array ini.

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: Kode ini dikompres dengan RegPack dan berisi banyak karakter yang tidak patut, yang semuanya lolos dalam representasi sumber di atas.

Cobalah online!

Pemetaan opcode dan uji kasus

Menggunakan mesin virtual pemetaan opcode ini .

Di bawah ini adalah test case yang diterjemahkan, bersama dengan output yang diharapkan.

Uji kasus # 1

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

Output yang diharapkan:

09 20 32 01 44 55 fb

Uji kasus # 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

Output yang diharapkan:

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

Uji kasus # 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

Output yang diharapkan:

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

Dibongkar dan diformat

Karena kode dikompresi dengan algoritma yang menggantikan string yang sering diulang dengan karakter tunggal, lebih efisien untuk menggunakan blok kode yang sama berulang kali daripada mendefinisikan dan menjalankan fungsi pembantu, atau menyimpan hasil antara (seperti M[A]) dalam variabel tambahan.

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

Mengesankan :) Bukan pro JS, jadi - apakah pengindeksan ini menjadi beberapa "array kode" dengan nilai opcode? terlihat bagus. Tetapi jika o = ...baris ini dieksekusi untuk setiap instruksi, ini mungkin memiliki "opcodes yang tidak diinginkan"?
Felix Palmen

2
Saya mungkin harus menambahkan test case: o Sekarang, saya pikir akan lebih baik untuk mengizinkan opcodes yang tidak disengaja ... memeriksa validitas hanya membuang byte di sini, tapi mungkin terlambat untuk mengubah aturan :(
Felix Palmen

Yah, saya akan menyarankan hal itu, karena tidak menambah banyak tantangan dan sekarang sulit untuk memeriksa dengan pemetaan khusus. Tetapi Anda mungkin harus bertanya / memperingatkan @MaxYekhlakov terlebih dahulu, karena mereka mungkin telah menerapkan aturan dengan benar.
Arnauld

c = M[A] >> 7 & 1<- apa yang &1benar - benar dibutuhkan di sini?
Felix Palmen

2
Saya cukup yakin itu sebagai kiriman Anda adalah fungsi anyways, kata-kata saya adalah "daftar byte [...] format yang masuk akal" dan yang Uint8Arraymemang hanya merangkum daftar byte tersebut. Jadi, jika menempatkan byte dalam string hex adalah cara yang dapat diterima untuk mewakili input, mengapa harus meletakkannya dalam objek kontainer dilarang ...
Felix Palmen

2

PHP, 581 585 555 532 byte (tidak bersaing)

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

mengambil kode OP dan PC sebagai basis 10 integer dari argumen baris perintah,
mencetak memori sebagai daftar [base 10 address] => base 10 value.

Ini belum diuji secara menyeluruh ; tapi ada gangguan .

Ada peta kode dan inilah ikhtisar untuk pemetaan saya:

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)

catatan samping: hasil
kode 24dalam BNV(cabang tidak pernah = 2 byte NOP);
04, 08, 0CAdalah alias untuk INX, CLCdan SEC
dan apa pun di atas 3Fadalah baik dua byte NOPatau alias untuk petunjuk mode single.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.