2 poin oleh GN⁺ 2024-05-13 | 1 komentar | Bagikan ke WhatsApp
  • 6o6 adalah proyek yang menjalankan kembali 6502 secara software di atas NMOS 6502 yang fitur proteksinya lemah, sehingga menambahkan lapisan eksekusi virtual yang dapat dikendalikan pada sistem 8-bit lama
  • Dengan mengendalikan eksekusi instruksi dan akses memori kode guest di tengah jalan, proyek ini menyediakan fitur seperti remapping alamat, pemblokiran baca/tulis ilegal, dan trap untuk jam opcode
  • Intinya adalah meminjam ALU milik host 6502 apa adanya: register dan flag guest dinaikkan ke host, instruksi yang sama dijalankan, lalu hasilnya disimpan kembali
  • Untuk verifikasi digunakan 6502 functional test suite dari Klaus Dormann dan lingkungan pengujian berbasis lib6502; konfigurasi yang dioptimalkan menjalankan 1.602.516.769 instruksi, 36,5% lebih sedikit daripada konfigurasi tanpa optimasi
  • The Incredible KIMplement 1.0 yang turut dirilis serta berbagai contoh menunjukkan cakupan penggunaan 6o6, mulai dari emulasi KIM-1, virtualisasi bertingkat, task switching, hingga sistem memori eksternal berbasis geoRAM

Apa yang dirilis 6o6 dan KIMplement

  • The Incredible KIMplement 1.0 mengemulasikan komputer single-board MOS/Commodore KIM-1 6502 berkapasitas 1KB dan 1MHz
    • Berjalan di Commodore 64 tanpa ekspansi
    • Mendukung TTY bawaan KIM, dan juga dapat diakses melalui port serial komputer sungguhan
    • Ruang alamat diperluas menjadi 16K
  • 6o6 adalah singkatan dari “6502-on-6502”, sebuah CPU virtual NMOS 6502 software penuh yang berjalan di atas CPU 6502
    • Mengendalikan eksekusi kode guest
    • Men-trap opcode yang tidak terdokumentasi dan jam opcode
    • Mengabstraksikan semua akses memori
    • Mendukung remapping alamat, intersepsi baca/tulis ilegal, dan eksekusi berbasis memori virtual
  • Di Commodore 64 dan Apple IIe, guest hello world dapat dijalankan, dan virtualisasi bertingkat yang menjalankan 6o6 lagi di dalam 6o6 juga bekerja
    • stage 1 berjalan hampir seketika
    • stage 2 lebih lambat
    • stage 3 sangat lambat tetapi berjalan

Mengapa 6502 membutuhkan virtualisasi

  • Pada komputer pribadi awal, biasanya satu program mengendalikan seluruh mesin, dan jika program berjalan keliru, masalahnya bisa diselesaikan dengan reboot
  • Dalam lingkungan multi-pengguna atau multitasking, kode yang salah dapat merusak ruang alamat lain, menjalankan instruksi berbahaya, atau memonopoli sumber daya
  • NMOS 6502 adalah CPU sederhana dengan kurang dari sekitar 4.000 transistor, sehingga kemampuan proteksinya terbatas
    • Banyak sistem NMOS 6502 historis sulit memindahkan lokasi zero page atau stack prosesor secara bebas
    • Tidak ada kemampuan untuk meremap alamat kode ke lokasi sembarang dan menjalankannya tanpa fixup
    • Akses ke lokasi memori tertentu tidak dapat dilarang secara menyeluruh
    • Jika jam atau opcode KIL yang tidak terdokumentasi dijalankan, prosesor bisa berhenti total
  • Sebagian masalah dapat dimitigasi lewat hardware
    • Jika NMI dibangkitkan secara berkala, proses yang mencoba memonopoli sistem dengan menyetel interrupt flag dapat dihentikan dari luar
    • Beberapa kernel multitasking 6502 menerapkan preemptive task switching dengan cara ini
    • In-circuit emulator seperti “Trap65” dari Eastern House Software dapat mengganti opcode yang salah dengan BRK yang bisa di-trap, tetapi mahal dan memiliki keterbatasan pada manipulasi bus yang kompleks

Cara eksekusi 6o6

  • Pendekatan interpreter sederhana pun bisa praktis di 6502
    • 6502 memiliki jumlah register yang sedikit
    • Instruksinya 56 dan mode pengalamatannya juga tidak banyak
    • Pelacakan status prosesor relatif mudah
  • “Virtualisasi” 6o6 terletak pada penggunaan ALU host 6502 untuk operasi internal guest
    • Accumulator dan flag guest dimuat ke CPU host
    • Instruksi yang sama yang akan dijalankan guest dijalankan di host
    • Hasil dan flag disimpan, lalu status host dibersihkan
  • Keunggulan pendekatan ini adalah tidak perlu mengimplementasikan ulang aritmetika langsung dan pemrosesan flag
    • decimal mode, yaitu aritmetika BCD, juga berjalan secara alami
    • Karena 6502 sungguhan yang menghitung, hasilnya sama dengan 6502
  • Saat membaca nilai memori atau mentransfer register, pendekatan yang sama juga digunakan untuk pemrosesan negative flag dan zero flag
  • Implementasinya menggunakan self-modifying code, sehingga perlu perhatian khusus jika ingin dimasukkan ke ROM

Struktur VM, harness, dan kernel

  • VM 6o6 berperan sebagai engine, bukan keseluruhan sistem
  • Lingkungan eksekusinya dibagi menjadi tiga bagian
    • VM: CPU virtual yang independen dari hardware dan berjalan di atas 6502 sungguhan
    • Harness: antarmuka untuk memori guest dan hardware yang dikelola
    • Kernel: loop kontrol yang memanggil VM serta menangani exception dan status guest
  • Harness menyediakan antarmuka biner lewat jump table yang distandardisasi
    • Mengimplementasikan load/store pada alamat tertentu
    • Menangani instruction fetch
    • Mempertahankan stack hardware dan stack pointer
    • VM tidak mengasumsikan ukuran page ataupun ada tidaknya memory paging
  • Harness dapat mengimplementasikan mulai dari translasi alamat berbasis penjumlahan sederhana dan bit shift sampai paged virtual memory
    • Tanpa meneruskan page fault ke luar, harness dapat melakukan paging in/out dalam proses load/store
    • Protection exception juga dapat dibangkitkan oleh harness
  • Kernel memulai eksekusi VM dan menafsirkan status code yang dikembalikan VM
    • Menangani exception yang muncul dari harness atau 6o6 sendiri
    • Memeriksa atau mengubah register guest dan PC
    • Dapat menangani routine layanan tertentu secara native
    • Di antara pemanggilan VM, CPU guest berada dalam keadaan “berhenti”, sehingga capture status atau context switching memungkinkan
  • VM tidak membangkitkan IRQ, NMI, atau reset virtual secara langsung
    • Kernel yang menentukan kapan event seperti ini terjadi
    • BRK didukung, tetapi VM tidak mengatur stack lalu berpindah ke PC baru; ia mengembalikan exception

Pemanfaatan 6o6 dalam KIMplement

  • Harness KIMplement memvirtualisasikan stack 6502 standar dan perangkat ekspansi KIM-4
    • $0000-$17ff: memori baca/tulis
    • $1800-$1fff: ROM
    • $2000-$3fff: RAM
    • $4000-$fff7: ruang tidak terpetakan yang tidak dapat ditulis
    • $1ff8-$1fff di-mirror ke $fff8-$ffff untuk vector
  • Host Commodore 64 menyimpan 16K bawah di $4000-$7fff, sementara sisanya disintesis oleh harness
  • Kernel KIMplement menangani emulasi RRIOT, tampilan LED, layanan TTY, penyisipan NMI untuk stop dan Single-Step Switch, serta sebagian trap ROM monitor KIM-1
  • Setelah eksekusi VM, kernel KIMplement memeriksa PC 6o6 untuk menentukan apakah routine saat ini akan diintersepsi
    • TTY dan sebagian fungsi diimplementasikan dengan cara ini

Optimasi performa

  • Pemanggilan antara 6o6 dan harness dapat menjadi bottleneck besar
    • Instruksi sederhana pun membutuhkan setidaknya satu fetch
    • Pengalamatan tidak langsung dapat membuat lebih banyak akses memori
  • Pada KIMplement 0.2, sebagian memory load di-inline sebagai macro preprocessor
    • Routine load untuk alamat virtual sembarang dan routine load teroptimasi zero page dihubungkan langsung ke VM
    • Kecepatan meningkat besar, tetapi ukuran VM bertambah
    • store dipertahankan sebagai pemanggilan subroutine karena frekuensinya lebih rendah dan lebih kompleks
  • Pada iterasi terbaru, inefisiensi cara inline macro mengakses program counter juga dirapikan sehingga instruction fetch makin membaik
  • KIMplement 0.3 menambahkan instruction fusion primitif yang disebut “extra helpings
    • Instruksi yang tidak menyentuh memori tidak perlu segera kembali ke kernel
    • Targetnya adalah instruksi immediate, instruksi yang berpusat pada accumulator, sebagian besar instruksi implied, dan branch yang tidak diambil
    • Jika load/store, perubahan PC yang tidak berurutan, atau exception terjadi, VM menghentikan upaya pengelompokan instruksi
  • extra helpings tidak membuat VM itu sendiri lebih cepat
    • Jika fungsi dibatasi berdasarkan posisi PC seperti pada KIMplement, ini bahkan bisa sedikit lebih lambat
    • Sebaliknya, bagian lain dari keseluruhan sistem menjadi lebih cepat karena kernel tidak dieksekusi tanpa perlu untuk setiap instruksi yang tidak memiliki perubahan untuk diamati
    • Karena dapat mengganggu aplikasi yang harus mengendalikan PC secara presisi, tersedia opsi untuk menonaktifkannya secara bertahap atau sepenuhnya

Verifikasi dan hasil pengujian

  • Untuk verifikasi digunakan functional test suite dari Klaus Dormann
    • Biner yang disediakan tidak membuat asumsi terhadap hardware
    • Penyelesaian sukses ditandai dengan loop tak hingga pada lokasi tertentu
  • Pengujian disusun agar dapat dijalankan langsung dari shell menggunakan lib6502 CPU emulator dari Ian Piumarta
  • lib6502 awalnya gagal karena edge case decimal mode, lalu lolos setelah dipatch
  • Biner yang disediakan Klaus berukuran 64K penuh, sehingga tidak dapat ditempatkan bersama 6o6 di dalam ruang alamat 6502 dasar
    • Patch sistem minimal bank-switched 32K ditambahkan ke lib6502
    • Area $7000-$efff digunakan, dan 32K depan serta 32K belakang biner pengujian ditempatkan pada bank yang berbeda
  • Pengujian dilakukan dengan tiga konfigurasi
    • Tanpa extra helpings, tanpa inline fetch macro
    • Dengan inline fetch macro, tanpa extra helpings
    • Dengan inline fetch macro dan extra helpings
  • Ketiga konfigurasi lolos Klaus suite
  • Hasil jumlah instruksinya adalah sebagai berikut
    • lib6502 tanpa 6o6: 30.646.178 instruksi
    • 6o6 tanpa optimasi: 2.188.322.914 instruksi
    • Dengan inline fetch macro: 1.713.350.225 instruksi
    • Dengan inline fetch macro dan extra helpings: 1.602.516.769 instruksi
  • Konfigurasi 6o6 tercepat menjalankan 36,5% lebih sedikit instruksi dibanding konfigurasi yang paling sedikit dioptimalkan
  • Konfigurasi tercepat menjalankan rata-rata 52,3 instruksi per satu instruksi guest
    • Angka ini mencakup harness, kernel, dan eksekusi 6o6 semuanya
    • Karena cycle count berbeda-beda untuk tiap instruksi, angka ini tidak boleh ditafsirkan sebagai rasio kecepatan

Contoh yang disertakan

  • Contoh hello world menjalankan program yang sama terlebih dahulu di CPU native, lalu kemudian melalui 6o6
    • Di Commodore 64, ini dipetakan ke routine output karakter di $ffd2; di Apple II, ke $fded
    • Ketika kernel mendeteksi PC menunjuk ke routine output karakter, kernel mengambil accumulator guest, memanggil routine ROM native, mengambil return address dari stack, lalu kembali ke loop
  • Contoh inception menjalankan 6o6 dengan dirinya sendiri sebagai payload menggunakan harness dan kernel yang sama
    • Setiap stage memiliki zero page dan stack sendiri
    • Karena 6o6 saat ini menggunakan self-modifying code, tiap stage membutuhkan salinan VM
    • Pada stage 3, sebagian besar memori digunakan untuk 3 salinan VM, dan VM dengan inline fetch macro berukuran lebih dari 10KB per salinan
  • Dalam eksekusi bertingkat, pemanggilan CHROUT diteruskan dari stage 3 ke stage 2, stage 1, lalu akhirnya ke routine native
  • Pengakhiran payload menggunakan instruksi RTS seperti “kick”
    • Karena saat mulai tidak ada return address di stack, RTS memicu stack underflow
    • Ketika harness melaporkannya sebagai exception, kernel menanganinya sebagai selesai normal
    • Pada stage yang dalam pun, cara yang sama dipropagasikan hingga kernel di atasnya
  • Di Apple II, setelah eksekusi dapat dijalankan lagi dengan CALL 2051; di Commodore 64 dengan RUN
    • Versi Apple II bahkan menggunakan area resident DOS di atas $9000, sehingga disarankan reboot setelah menjalankannya

Contoh task switching

  • Contoh tasks adalah kernel task-switching kecil yang bolak-balik di antara dua task independen
  • Setiap task memiliki zero page, stack, dan area alamat kode kecil sendiri, serta tidak mengetahui keberadaan satu sama lain maupun VM
  • Satu task menampilkan alfabet, dan task lain menampilkan angka
    • Angka ditampilkan dengan reverse video agar dapat dibedakan secara visual
    • Setiap kali tombol ditekan, task beralih
  • Kedua task menggunakan lokasi yang sama di zero page untuk menyimpan status, tetapi karena zero page-nya independen, masing-masing melanjutkan dari posisi terakhirnya
  • Yang dibutuhkan untuk context switching adalah informasi task saat ini dan area penyimpanan status A, X, Y, P, S, PC tiap task
    • Harness melihat task yang “on CPU” lalu memilih alamat fisik untuk zero page, stack, dan kode eksekusi
    • Saat beralih, kernel menyimpan dan memuat status lain serta menandai task lain sebagai “on processor”

Contoh memori eksternal 64K berbasis geoRAM

  • Contoh vmgr khusus Commodore 64, dan menyediakan ruang alamat 64K sebagai memori eksternal tanpa memakai RAM sistem itu sendiri
  • geoRAM adalah perangkat paged RAM yang berbeda dari REU resmi Commodore
    • REU berpusat pada DMA, dan menggunakan MOS 8726 REC untuk melakukan operasi baca, tulis, dan tukar dengan main memory
    • geoRAM memetakan memori sebagai window page 256 byte di rentang I/O $de00
    • Register kontrol berada di $dffe, $dfff
    • Clone kompatibel modern ada yang berkapasitas hingga 4MB
    • VICE mendukung emulasi geoRAM
  • Contoh ini menggunakan ROM dari 6502 processor module yang disediakan untuk RC2014 Z80 kit computer
    • ROM berisi monitor dan EhBASIC dari Lee Davison
    • ROM yang digunakan adalah pre-built ROM di GitHub, memakai versi 6551
  • Harness mengabaikan penulisan ke area ROM guest mulai $c100
    • Penulisan di bawah 16K menggunakan jalur cepat
    • Di atas itu, mask dan shift digunakan untuk menyesuaikan bank geoRAM
    • Page geoRAM saat ini di-cache sehingga konfigurasi dilewati saat mengakses page yang sama
  • Kernel dan main program digabung menjadi satu dalam contoh ini
    • Memeriksa keberadaan dan operasi geoRAM
    • Menyalin image ROM ke geoRAM
    • BRK dikembalikan ke monitor
    • illegal instruction, user-defined instruction trap, dan lainnya diperlakukan seperti BRK
  • Serial vector RC2014 ROM diintersepsi untuk mengemulasikan terminal sederhana
    • Melakukan konversi antara PETSCII dan karakter terminal
    • Mempertahankan kursor kecil
    • Menyesuaikan register dan flag guest sesuai hasil
    • Sistem yang diemulasikan dapat di-reset dengan CTRL-SHIFT-Commodore, dan memorinya tetap dipertahankan
  • Jika ukuran memori tidak dimasukkan langsung saat EhBASIC cold start, kombinasi C64 dan geoRAM membutuhkan sekitar 1 menit untuk menemukan 32768 byte free
    • ROM dibangun dengan hard cap $8000, sehingga meski sebenarnya ada lebih banyak, kapasitasnya dibatasi menjadi 32768 byte
    • Area $8000-$c0ff dapat diisi hal lain
    • EhBASIC tidak menerima command atau keyword huruf kecil, sehingga semuanya harus diketik dengan huruf besar
  • Ini juga berjalan di Commodore 128DCR sungguhan dengan cartridge geoRAM 512K
    • Operasi floating-point juga berjalan normal
    • bad instruction langsung diintersepsi secara terkendali
    • Kecuali window 256 byte, sistem di layar tidak berjalan di ruang alamat 6502 itu sendiri
    • Bahkan dengan geoRAM 512K, 8 task sistem 6502 berkapasitas 64K dapat ditempatkan secara terpisah

Perbaikan dan penggunaan ke depan

  • Perbaikan agar 6o6 dapat berjalan dari ROM dimungkinkan, tetapi membutuhkan refactoring dan bisa menjadi lebih lambat, sehingga lebih merupakan opsi
  • Emulasi 65816 dipandang di luar cakupan, tetapi mengemulasikan instruksi CMOS di sistem NMOS mungkin saja memungkinkan
    • Karena menggunakan ALU, jika NMOS 6502 mengemulasikan CMOS 65C02, flag tetap disetel dengan cara NMOS
    • Arah sebaliknya juga sama
    • Pengalamatan saat ini ditulis dengan cara CPU NMOS
  • Pendekatan inline memory macro memiliki peluang peephole optimization
    • Pass “post-preprocessor” dapat ditempatkan sebelum assembly sebenarnya
    • Karena toolchain menjadi lebih kompleks, keuntungan umumnya perlu dipastikan
  • Salah satu penggunaan eksplisit 6o6 adalah menjalankan kode yang diunduh tanpa merusak task saat ini
    • Ada ide untuk menggunakannya sebagai bagian dari Gopher client agar hal yang diunduh dapat dijalankan secara dinamis
  • Jika merancang sendiri sistem 6502 baru, mengimplementasikan fitur yang dibutuhkan di hardware mungkin lebih cepat
  • Jika proteksi pada CPU NMOS minim atau ingin meminimalkan silikon tambahan, 6o6 menjadi alternatif yang fleksibel dan mudah diadaptasi

Distribusi dan lisensi

  • The Incredible KIMplement tersedia di homepage dan GitHub
  • 6o6 tersedia di GitHub, dan juga mencakup empat contoh yang dibahas dalam tulisan
  • Pembaruan KIMplement 1.0 terutama berfokus pada perapian untuk rilis publik dan perbaikan bug kecil
  • KIMplement juga menyertakan Tiny PILOT yang disediakan oleh Dave Hassler
    • Tiny PILOT adalah implementasi yang ditulis Nicholas Vrtis di majalah MICRO pada 1979, dengan patch dari Bob Applegate dan Dave Hassler ditambahkan
    • Dave Hassler juga mem-port ELIZA dari implementasi Atari PILOT 1980 karya Carol Shaw dan Harry Stewart
  • KIMplement dan 6o6 keduanya didistribusikan dengan Floodgap Free Software License

1 komentar

 
GN⁺ 2024-05-13
Opini Hacker News
  • Meski 6502 itu sederhana dan terbatas, selalu menarik melihat arsitektur yang sudah hampir 50 tahun ini terus didorong hingga batas-batas baru
    Beberapa SoC yang ditujukan untuk pasar sangat murah dan produksi massal masih menyertakan core 6502

    • Kenapa begitu? Mungkin karena mereka mempertahankan desain yang sudah berfungsi baik untuk menghemat biaya riset dan pengembangan
      Rasanya tidak mudah mengalahkan core RISC-V seharga 10 sen
      Awalnya saya tertawa membayangkan SoC multicore berisi 6502 core 6502, tapi kalau dicoba dengan FPGA sepertinya bisa jadi proyek yang menarik
    • Ada sangat banyak kode yang bisa dipakai untuk hampir semua hal yang diinginkan
      Saya memakai 65816, penerus 16-bit untuk Apple 2, sebagai kartu ekspansi berkecepatan variabel, tetapi sebagian besar waktu menjalankannya dalam mode 8-bit. Karena itu berjalan baik dan sebagian besar kode library juga 8-bit
      Pada kecepatan ini, chip-nya cepat. Terutama jika memikirkan model sederhana di mana RAM dan CPU di-clock 1:1. Dalam kasus saya, kode bisa dijalankan melalui bus 1MHz, sehingga sebagian besar operasi multi-siklus terasa seperti satu siklus bus per fetch memori
      Atau, kartu itu memiliki RAM 1MB, dan RAM ini berjalan pada kecepatan CPU (0,15–16MHz). Dengan begitu, program besar yang ditulis dalam bahasa tingkat tinggi pun bisa berjalan cukup cepat untuk dipakai. Tentu saja assembly-nya luar biasa cepat
      Ini lingkungan yang cukup menyenangkan untuk diutak-atik
  • Bisakah GEOS dijalankan di dalam jendela GEOS?

  • Tulisan ini sempat cukup lama mengambang tanpa komentar, jadi saya pikir akan membacanya sekilas nanti; inti utamanya ada pada cara Commodore 64 mengemulasikan sistem berbasis 6502 yang sama sekali berbeda
    “6o6”, yaitu “6502-on-6502”, adalah CPU NMOS 6502 dalam bentuk perangkat lunak virtualisasi penuh yang berjalan di atas CPU 6502, mencakup opcode yang tidak terdokumentasi dan trap untuk opcode jam, memberikan kendali penuh atas eksekusi kode guest, dan juga mengabstraksikan seluruh akses memori
    Karena itu, remapping alamat, intersepsi baca/tulis ilegal, bahkan eksekusi memori virtual penuh menjadi mungkin. Bukan hanya lulus seluruh uji fungsional, ia bahkan bisa memvirtualisasikan dirinya sendiri yang sedang memvirtualisasikan dirinya sendiri; ini karya yang hebat dari semua sudut pandang, bukan hanya dari perspektif 6502
    Saya juga teringat video tentang adanya protected mode pada Zilog Z80: https://www.youtube.com/watch?v=DLSUAVPKeYk

  • Saya jadi teringat saat pertama kali belajar sendiri assembly 6502. Ada buku berjudul “The Visual Computer” dan disertai emulator di floppy disk; itu benar-benar pengalaman yang membuka mata
    Saya menemukan PDF bukunya [1], tetapi tidak tahu apakah perangkat lunak yang ada di floppy itu masih tersimpan di suatu tempat
    [1] https://files.commodore.software/reference-material/books/c6...

  • https://archive.fo/2u3Y8