Kami menulis emulator yang tidak diperlukan oleh siapa pun

Hari yang baik


Sudah lama sekali ada keinginan untuk menulis emulator dari beberapa prosesor.
Dan apa yang bisa lebih baik daripada menciptakan sepeda?


Nama sepeda adalah V16, dari perekatan kata Virtual dan, pada kenyataannya, kedalaman bit.



Di mana untuk memulai?


Dan Anda harus memulai, tentu saja, dengan deskripsi prosesor.


Pada awalnya, saya berencana untuk menulis emulator DCPU-16, tetapi ada lebih dari cukup keajaiban seperti itu di Internet, jadi saya memutuskan untuk fokus hanya pada "menjilati" yang paling mendasar dengan DCPU-16 1.1.


Arsitektur


Memori dan porta


  • V16 membahas 128Kb (65536 kata) RAM, yang juga dapat digunakan sebagai buffer perangkat dan stack.
  • Tumpukan dimulai dengan alamat FFFF, oleh karena itu, RSP memiliki nilai standar 0xFFFF
  • Port I / O V16 memiliki 256, semuanya memiliki panjang 16 bit. Membaca dan menulis dari mereka dilakukan melalui instruksi IN b, a AND OUT b, a .

Daftar


V16 memiliki dua set register tujuan umum: primer dan alternatif.
Sebuah prosesor dapat bekerja hanya dengan satu set, sehingga Anda dapat beralih antar set menggunakan instruksi XCR .


Instruksi


Semua instruksi memiliki panjang maksimal tiga kata dan sepenuhnya didefinisikan terlebih dahulu
Kata pertama dibagi menjadi tiga nilai: byte rendah adalah opcode, byte tinggi dalam bentuk dua nilai 4-bit adalah deskripsi operan.


Gangguan


Interupsi di sini tidak lebih dari sebuah tabel dengan alamat di mana prosesor menduplikasi instruksi CALL . Jika nilai alamat adalah nol, maka interupsi tidak melakukan apa-apa, itu hanya me-reset bendera HF.


Rentang nilaiDeskripsi
0x0 ... 0x3Kasing sebagai nilai
0x4 ... 0x7Daftarkan sebagai nilai di
0x8 ... 0xBDaftarkan + konstan sebagai nilai pada alamat
0xCKonstan sebagai nilai pada
0xDKonstan sebagai nilai
0xEDaftar RIP sebagai nilai hanya baca
0xFDaftar RSP sebagai nilai

Contoh pseudocode dan kata-kata di mana semua ini harus diterjemahkan:


 MOV RAX, 0xABCD ; 350D ABCD MOV [RAX], 0x1234 ; 354D 1234 

Siklus


V16 dapat menjalankan satu instruksi dalam 1, 2 atau 3 ukuran. Setiap akses memori adalah satu siklus jam yang terpisah. Instruksi bukanlah kebijaksanaan!


Ayo mulai menulis!


Implementasi struktur prosesor dasar


  1. Satu set register. Hanya ada empat register, tetapi situasinya membaik bahwa ada dua set seperti itu di dalam prosesor. Perpindahan terjadi menggunakan instruksi XCR .


     typedef struct Regs { uint16_t rax, rbx; //Primary Accumulator, Base Register uint16_t rcx, rdx; //Counter Register, Data Register } regs_t; 

  2. Bendera Tidak seperti DCPU-16, V16 memiliki lompatan bersyarat, panggilan subrutin, dan kembali dari yang sama. Saat ini, prosesor memiliki 8 flag, 5 di antaranya adalah flag kondisi.


     //  ,    stdbool.h typedef struct Flags { bool IF, IR, HF; bool CF, ZF; bool EF, GF, LF; } flags_t; 

  3. Sebenarnya, prosesor itu sendiri. Ini juga menggambarkan tabel alamat interupsi, yang dapat disebut deskriptor dan menemukan referensi lain ke x86.


     typedef struct CPU { //CPU Values uint16_t ram[V16_RAMSIZE]; //Random Access Memory uint16_t iop[V16_IOPSIZE]; //Input-Output Ports uint16_t idt[V16_IDTSIZE]; //Interrupt vectors table (Interrupt Description Table) flags_t flags; //Flags regs_t reg_m, reg_a; //Main and Alt register files regs_t * reg_current; //Current register file uint16_t rip, rsp, rex; //Internal Registers: Instruction Pointer, Stack Pointer, EXtended Accumulator //Emulator values bool reg_swapped; //Is current register file alt bool running; //Is cpu running uint32_t cycles; //RAM access counter } cpu_t; 

  4. Operan. Saat mendapatkan nilai, kita harus terlebih dahulu membaca, lalu mengubah, dan kemudian menulis nilai kembali ke tempat kita mendapatkannya.


     typedef struct Opd { uint8_t code : 4; uint16_t value; uint16_t nextw; } opd_t; 


Fungsi untuk bekerja dengan struktur


Ketika semua struktur dideskripsikan, muncul kebutuhan untuk fungsi-fungsi yang akan memberkahi struktur ini dengan kekuatan magis kode yang padam.


 cpu_t * cpu_create(void); //   void cpu_delete(cpu_t *); //   void cpu_load(cpu_t *, const char *); // ROM   void cpu_rswap(cpu_t *); //   uint16_t cpu_nextw(cpu_t *); //RAM[RIP++]. Nuff said void cpu_getop(cpu_t *, opd_t *, uint8_t); //  void cpu_setop(cpu_t *, opd_t *, uint16_t); //  void cpu_tick(cpu_t *); //   void cpu_loop(cpu_t *); // ,    

Juga, saya tidak menyebutkan enumerasi besar dengan kode operasi, tetapi ini tidak perlu dan hanya perlu untuk memahami apa yang terjadi di semua kekacauan ini.


Centang () fungsi


Juga, ada panggilan ke fungsi statis yang hanya dimaksudkan untuk panggilan dari tick() .


 void cpu_tick(cpu_t *cpu) { //    HLT,      if(cpu->flags.HF) { //      ,      if(!cpu->flags.IF) { cpu->running = false; } return; } //       uint16_t nw = cpu_nextw(cpu); uint8_t op = ((nw >> 8) & 0xFF); uint8_t ob = ((nw >> 4) & 0x0F); uint8_t oa = ((nw >> 0) & 0x0F); //     //   opd_t opdB = { 0 }; opd_t opdA = { 0 }; //    cpu_getop(cpu, &opdB, ob); cpu_getop(cpu, &opdA, oa); //        -  uint16_t B = opdB.value; uint16_t A = opdA.value; uint32_t R = 0xFFFFFFFF; //    bool clearf = true; //       ? //   ! switch(op) { //     . ,   ,    R } //   if(clearf) { cpu->flags.EF = false; cpu->flags.GF = false; cpu->flags.LF = false; } //  ,  32-   16-  //  0xFFFF0000,   0xFFFF << 16 //        32-  if(R != 0xFFFFFFFF) { cpu_setop(cpu, &opdB, (R & 0xFFFF)); cpu->rex = ((R >> 16) & 0xFFFF); cpu->flags.CF = (cpu->rex != 0); cpu->flags.ZF = (R == 0); } return; } 

Apa yang harus dilakukan selanjutnya?


Dalam upaya menemukan jawaban untuk pertanyaan ini, saya menulis ulang emulator lima kali dari C ke C ++, dan sebaliknya.


Namun, tujuan utama dapat diidentifikasi sekarang:


  • Kencangkan interupsi normal (Alih-alih hanya memanggil fungsi dan melarang menerima interupsi lainnya, buat panggilan fungsi dan tambahkan interupsi baru ke antrian).
  • Sekrup perangkat, serta cara berkomunikasi dengan mereka, manfaat opcodes bisa 256.
  • Untuk mengajar Jangan menulis sendiri bid'ah tentang Habr Prosesor ini beroperasi pada kecepatan clock spesifik 200 MHz.

Kesimpulan


Saya harap "artikel" ini bermanfaat bagi seseorang, seseorang akan meminta mereka untuk menulis sesuatu yang serupa.


Pai saya dapat dilihat di github .


Juga, tentang horor, saya memiliki assembler untuk versi lama emulator ini (Tidak, bahkan tidak mencoba, emulator setidaknya akan mengeluh tentang format ROM yang salah)

Source: https://habr.com/ru/post/id457144/


All Articles