6o6 ile 6502 üzerinde 6502 sanallaştırma
(oldvcr.blogspot.com)- 6o6, koruma özellikleri zayıf olan NMOS 6502 üzerinde 6502’yi yeniden yazılım olarak çalıştırarak eski 8 bit sistemlere denetlenebilir bir sanal yürütme katmanı ekleyen bir projedir
- Misafir kodun komut yürütmesini ve bellek erişimini arada denetleyerek adres yeniden eşleme, yasadışı okuma/yazma engelleme ve jam opcode tuzağı gibi işlevler sunar
- Temel fikir, ana makine 6502’nin ALU’sunu doğrudan ödünç almaktır; misafir kayıtları ve bayrakları ana makineye yüklenir, aynı komut çalıştırılır ve sonuç tekrar kaydedilir
- Doğrulamada Klaus Dormann’ın 6502 functional test suite’i ve lib6502 tabanlı test ortamı kullanıldı; optimize edilmiş yapılandırma 1.602.516.769 komutla, optimize edilmemiş yapılandırmadan %36,5 daha az komut yürüttü
- Birlikte yayımlanan The Incredible KIMplement 1.0 ve çeşitli örnekler, KIM-1 emülasyonu, iç içe sanallaştırma, görev değiştirme ve geoRAM tabanlı harici bellek sistemine kadar 6o6’ın kullanım alanını gösteriyor
6o6 ve KIMplement’in duyurduğu yenilikler
- The Incredible KIMplement 1.0, 1KB, 1MHz MOS/Commodore KIM-1 6502 tek kart bilgisayarı emüle ediyor
- Genişletmesiz Commodore 64 üzerinde çalışıyor
- KIM’in yerleşik TTY’sini destekliyor ve gerçek bilgisayarın seri portu üzerinden erişim de mümkün
- Adres alanı 16K’ye genişletildi
- 6o6, “6502-on-6502” kısaltmasıdır; 6502 CPU üzerinde çalışan tamamen yazılım tabanlı bir NMOS 6502 sanal CPU’sudur
- Misafir kod yürütmesini denetler
- Belgelenmemiş opcode’ları ve jam opcode’ları tuzağa düşürür
- Tüm bellek erişimini soyutlar
- Adres yeniden eşleme, yasadışı okuma/yazma yakalama ve sanal bellek tabanlı yürütmeyi destekler
- Commodore 64 ve Apple IIe üzerinde misafir
hello worldçalıştırılıyor; ayrıca 6o6’ın tekrar 6o6 içinde çalıştırıldığı iç içe sanallaştırma da çalışıyor- stage 1 neredeyse anında çalışır
- stage 2 daha yavaştır
- stage 3 çok yavaştır ama çalışır
6502’de neden sanallaştırmaya ihtiyaç var?
- İlk kişisel bilgisayarlarda genellikle tek bir program tüm makineyi kontrol ederdi ve program bozulursa çözüm yeniden başlatmaktı
- Çok kullanıcılı veya çok görevli ortamlarda hatalı kod başka adres alanlarını bozabilir, tehlikeli komutlar çalıştırabilir veya kaynakları tekeline alabilir
- NMOS 6502, yaklaşık 4.000’den az transistörden oluşan basit bir CPU olduğu için koruma özellikleri sınırlıdır
- Tarihsel birçok NMOS 6502 sisteminde zero page veya işlemci stack konumunu serbestçe taşımak zordu
- Kod adreslerini rastgele konumlara fixup olmadan yeniden eşleyip çalıştırma özelliği yoktu
- Belirli bellek konumlarına erişimi kapsamlı biçimde yasaklamak mümkün değildi
- Belgelenmemiş jam veya
KILopcode çalıştırılırsa işlemci tamamen kilitlenebiliyordu
- Bazı sorunlar donanımla hafifletilebilir
- NMI periyodik olarak üretilirse, interrupt flag ayarlayarak sistemi tekeline almak isteyen süreç dışarıdan durdurulabilir
- Bazı 6502 çok görevli çekirdekleri bu yöntemle kesmeli görev değiştirme uygular
- Eastern House Software’in “Trap65” gibi in-circuit emulator’ları bozuk opcode’ları tuzağa düşürülebilen BRK’ye dönüştürebilirdi, ancak pahalıydı ve karmaşık bus işlemlerinde sınırlıydı
6o6 nasıl çalışıyor?
- Basit yorumlayıcı yaklaşımı 6502 üzerinde de pratik olabilir
- 6502’nin kayıt sayısı azdır
- 56 komutu vardır ve adresleme kipleri de fazla değildir
- İşlemci durumunu izlemek görece kolaydır
- 6o6’ın “sanallaştırma” tarafı, ana makine 6502’nin ALU’sunu misafir iç işlemler için kullanmasında yatar
- Misafir accumulator ve bayrakları ana makine CPU’suna yüklenir
- Misafirin çalıştıracağı aynı komut ana makinede çalıştırılır
- Sonuçlar ve bayraklar kaydedilir, ardından ana makine durumu temizlenir
- Bu yaklaşımın avantajı, aritmetik ve bayrak işleyişini baştan yeniden uygulamak zorunda kalmamaktır
- decimal mode yani BCD aritmetiği de doğal olarak çalışır
- Hesabı gerçek 6502 yaptığı için sonuçlar 6502 ile aynıdır
- Bellek değeri okurken veya kayıt aktarımı yaparken de negatif bayrak ve zero bayrağı işleyişi için aynı yöntem kullanılır
- Uygulama kendini değiştiren kod kullandığından ROM’a koymak için ayrıca dikkat gerekir
VM, harness ve kernel yapısı
- 6o6 VM, tam bir sistem değil bir motor görevi görür
- Yürütme ortamı üç parçaya ayrılır
- VM: gerçek 6502 üzerinde çalışan, donanımdan bağımsız sanal CPU
- Harness: misafir belleği ve yönetilen donanım için arayüz
- Kernel: VM’i çağıran ve istisnalarla misafir durumunu işleyen denetim döngüsü
- Harness, standartlaştırılmış bir jump table ile ikili arayüz sağlar
- Belirli adreslerde load/store işlemlerini uygular
- Instruction fetch’i yönetir
- Donanım stack’ini ve stack pointer’ı korur
- VM, sayfa boyutu veya bellek sayfalaması varlığını varsaymaz
- Harness, basit toplama ve bit kaydırma tabanlı adres dönüşümünden sayfalı sanal belleğe kadar uygulanabilir
- page fault dışarı aktarılmadan harness load/store sırasında paging in/out yapabilir
- Koruma istisnaları da harness tarafından üretilebilir
- Kernel, VM yürütmesini başlatır ve VM’in döndürdüğü durum kodlarını yorumlar
- Harness veya 6o6’ın kendisinden gelen istisnaları işler
- Misafir kayıtlarını ve PC’yi inceler ya da değiştirir
- Belirli servis rutinlerini yerel olarak işleyebilir
- VM çağrıları arasında misafir CPU “durmuş” durumda olduğundan durum yakalama veya context switch yapılabilir
- VM, sanal IRQ, NMI veya reset’i doğrudan üretmez
- Bu olayların ne zaman üretileceğine kernel karar verir
- BRK desteklenir, ancak VM stack’i ayarlayıp yeni PC’ye gitmek yerine istisna döndürür
6o6’ın KIMplement içindeki kullanımı
- KIMplement’in harness’i standart 6502 stack’ini ve KIM-4 genişletme aygıtını sanallaştırır
$0000-$17ff: okuma/yazma bellek$1800-$1fff: ROM$2000-$3fff: RAM$4000-$fff7: eşlenmemiş ve yazılamayan alan$1ff8-$1fff, vektörler için$fff8-$ffffüzerine mirror edilir
- Ana makine Commodore 64, alt 16K’yi
$4000-$7fffiçinde tutar; geri kalanı harness sentezler - KIMplement kernel’i RRIOT emülasyonu, LED gösterimi, TTY hizmeti, stop ve Single-Step Switch için NMI ekleme ve KIM-1 ROM monitor’ünün bazı bölümlerini tuzağa düşürme işlerini yapar
- VM yürütmesinden sonra KIMplement kernel’i 6o6’ın PC’sini inceleyerek mevcut rutini yakalayıp yakalamayacağına karar verir
- TTY ve bazı işlevler bu şekilde uygulanır
Performans optimizasyonu
- 6o6 ile harness arasındaki çağrılar büyük darboğaz olabilir
- Basit komutlar bile en az bir fetch gerektirir
- Dolaylı adresleme daha fazla bellek erişimi oluşturabilir
- KIMplement 0.2’de bazı bellek load işlemleri önişlemci makrolarıyla inline hale getirildi
- Rastgele sanal adresler için load rutini ve zero page için optimize edilmiş load rutini doğrudan VM’e bağlandı
- Hız önemli ölçüde arttı ama VM boyutu büyüdü
- Store işlemleri daha seyrek ve daha karmaşık olduğundan alt yordam çağrısı olarak bırakıldı
- Son yinelemelerde, inline makroların program counter’a erişim biçimindeki verimsizlik de temizlenerek instruction fetch daha da iyileştirildi
- KIMplement 0.3’te “extra helpings” adlı ilkel bir instruction fusion eklendi
- Belleğe dokunmayan komutlar için kernel’e hemen dönmek gerekmez
- Immediate komutlar, accumulator ağırlıklı komutlar, implied komutların çoğu ve dallanmayan branch’ler buna dahildir
- Load/store, sıralı olmayan PC değişimi veya istisna oluşursa VM komut birleştirme denemesini durdurur
- extra helpings, VM’in kendisini doğrudan hızlandırmaz
- KIMplement gibi PC konumuna göre işlev kısıtlayan yapılarda biraz yavaşlatabilir
- Ama kernel’in gözlemleyeceği bir değişiklik olmayan komutlar için gereksiz yere çalışmasını önleyerek sistemin diğer bölümlerini hızlandırır
- PC’yi hassas biçimde denetlemesi gereken uygulamalar için engel olabilir; bu nedenle kademeli veya tamamen devre dışı bırakma seçeneği vardır
Doğrulama ve test sonuçları
- Doğrulama için Klaus Dormann’ın functional test suite paketi kullanıldı
- Sağlanan ikili dosya donanım hakkında varsayım yapmaz
- Başarılı bitiş, belirli bir konumdaki sonsuz döngü ile sinyallenir
- Testler, Ian Piumarta’nın lib6502 CPU emulator kullanılarak shell üzerinden doğrudan çalışacak şekilde kuruldu
- lib6502, decimal mode’daki edge case nedeniyle başlangıçta başarısız oldu, yamadan sonra geçti
- Klaus’un sağladığı ikili dosya tam 64K olduğundan, varsayılan 6502 adres alanında 6o6 ile birlikte tutulamadı
- lib6502’ye 32K bank-switched minimal sistem yaması eklendi
$7000-$efffalanı kullanıldı ve test ikilisinin ilk 32K’si ile son 32K’si farklı bank’lara yerleştirildi
- Testler üç yapılandırmada yapıldı
- extra helpings yok, inline fetch macro yok
- inline fetch macro var, extra helpings yok
- hem inline fetch macro hem extra helpings var
- Her üç yapılandırma da Klaus paketini geçti
- Komut sayısı sonuçları şöyle oldu
- 6o6’sız lib6502: 30.646.178 komut
- Optimizasyonsuz 6o6: 2.188.322.914 komut
- inline fetch macro uygulanmış: 1.713.350.225 komut
- inline fetch macro ve extra helpings uygulanmış: 1.602.516.769 komut
- En hızlı 6o6 yapılandırması, en az optimize edilen yapılandırmadan %36,5 daha az komut yürüttü
- En hızlı yapılandırma, misafir komut başına ortalama 52,3 komut çalıştırdı
- Bu sayı harness, kernel ve 6o6 yürütmesinin tümünü içerir
- Komutların cycle count’ları farklı olduğundan bunu doğrudan hız katsayısı olarak yorumlamamak gerekir
Dahil edilen örnekler
- hello world örneği, aynı programı önce yerel CPU’da, sonra 6o6 üzerinden çalıştırır
- Commodore 64’te
$ffd2, Apple II’de$fdedadresindeki karakter çıktı rutiniyle eşlenir - Kernel, PC’nin karakter çıktı rutinini gösterdiğini algılarsa misafir accumulator’ı alır, yerel ROM rutinini çağırır ve stack’ten return address’i çekip döngüye geri döner
- Commodore 64’te
- inception örneği, aynı harness ve kernel ile 6o6’ın kendi kendisini payload olarak çalıştırmasını gösterir
- Her stage kendi zero page’ine ve stack’ine sahiptir
- Mevcut 6o6 kendini değiştiren kod kullandığından her stage için ayrı bir VM kopyası gerekir
- stage 3’te belleğin çoğu üç VM kopyasına gider; inline fetch macro içeren VM kopyası başına 10KB’den fazladır
- İç içe yürütmede
CHROUTçağrısı stage 3’ten stage 2 ve stage 1 üzerinden geçip en sonunda yerel rutine iletilir - Payload sonlandırma için RTS komutu bir “kick” gibi kullanılır
- Başlangıçta stack’te return address olmadığından RTS, stack underflow üretir
- Harness bunu istisna olarak bildirirse kernel bunu normal sonlanma sayar
- Aynı yöntem daha derin stage’lerde de üst kernel’lere kadar yayılır
- Apple II’de çalıştırma sonrası
CALL 2051, Commodore 64’te iseRUNile yeniden başlatılabilir- Apple II sürümü
$9000üzerindeki yerleşik DOS alanını da kullandığından çalıştırma sonrası yeniden başlatma önerilir
- Apple II sürümü
Görev değiştirme örneği
- tasks örneği, iki bağımsız görev arasında geçiş yapan küçük bir task-switching kernel’idir
- Her görev kendi zero page’ine, stack’ine ve küçük bir kod adres alanına sahiptir; birbirlerinden ve VM’in varlığından habersizdirler
- Bir görev alfabeyi, diğeri sayıları gösterir
- Sayılar görsel ayrım için reverse video ile gösterilir
- Her tuşa basıldığında görev değişir
- İki görev de durum saklamak için zero page’de aynı konumu kullanır, ancak zero page bağımsız olduğu için her biri kaldığı yerden devam eder
- Context switch için gerekenler, geçerli görev bilgisi ile her görevin A, X, Y, P, S ve PC durum saklama alanıdır
- Harness “on CPU” görevine bakarak zero page, stack ve çalışan kodun fiziksel adresini seçer
- Kernel geçiş sırasında diğer durumu kaydeder/yükler ve başka görevi “on processor” olarak işaretler
geoRAM tabanlı 64K harici bellek örneği
- vmgr örneği Commodore 64’e özeldir ve sistemin kendi RAM’ini kullanmayan 64K adres alanını harici bellek olarak sunar
- geoRAM, Commodore’un resmî REU’sundan farklı bir paged RAM aygıtıdır
- REU, DMA merkezlidir ve MOS 8726 REC kullanarak ana bellekle okuma, yazma ve değişim işlemleri yapar
- geoRAM, belleği
$de00I/O aralığındaki 256 baytlık window page üzerinden eşler - Denetim kayıtları
$dffeve$dfffadreslerindedir - Modern uyumlu klonlar 4MB’a kadar kapasite sunabilir
- VICE, geoRAM emülasyonunu destekler
- Örnek, RC2014 Z80 kit computer için sağlanan 6502 processor module ROM’unu kullanır
- ROM’da monitor ve Lee Davison’ın EhBASIC’i bulunur
- Kullanılan ROM, GitHub’daki hazır derlenmiş ROM’dur ve 6551 sürümünü kullanır
- Harness, misafir ROM alanı olan
$c100ve sonrasına yapılan write işlemlerini yok sayar- 16K’den küçük yazmalar hızlı yolu kullanır
- Bunun üzerindekiler için mask ve shift ile geoRAM bank ayarlanır
- Geçerli geoRAM page önbelleğe alınır ve aynı page’e erişimde ayarlama işlemi atlanır
- Bu örnekte kernel ile ana program birleştirilmiştir
- geoRAM’in varlığını ve çalışmasını doğrular
- ROM imajını geoRAM’e kopyalar
- BRK, monitor’e geri döner
- illegal instruction, user-defined instruction trap vb. BRK gibi işlenir
- RC2014 ROM’unun serial vector’ü yakalanarak basit bir terminal emüle edilir
- PETSCII ile terminal karakterleri arasında dönüşüm yapılır
- Küçük bir imleç tutulur
- Misafir kayıtları ve bayrakları sonuca uygun biçimde ayarlanır
CTRL-SHIFT-Commodoreile emüle edilen sistem resetlenebilir ve bellek korunur
- EhBASIC cold start sırasında bellek boyutu elle girilmezse, C64 ve geoRAM birleşiminde 32768 bayt free bulması yaklaşık 1 dakika sürer
- ROM,
$8000hard cap ile derlendiğinden gerçekte daha fazlası olsa da 32768 baytla sınırlıdır $8000-$c0ffaralığına başka şeyler konabilir- EhBASIC küçük harfli command veya keyword kabul etmediğinden her şey büyük harfle girilmelidir
- ROM,
- Gerçek Commodore 128DCR ve 512K geoRAM cartridge üzerinde de çalışır
- Kayan nokta işlemleri de düzgün çalışır
- bad instruction’lar denetimli biçimde anında yakalanır
- 256 baytlık window dışında ekrandaki sistem 6502’nin kendi adres alanında çalışmaz
- 512K geoRAM üzerinde 64K’lık 6502 sistem görevlerinden 8 tanesi ayrı ayrı tutulabilir
Gelecekteki iyileştirmeler ve kullanım alanları
- 6o6’ı ROM’dan çalıştırmayı mümkün kılacak geliştirmeler yapılabilir, ancak bunun için yeniden düzenleme gerekir ve daha yavaş olabilir; bu yüzden daha çok bir seçenek niteliğindedir
- 65816 emülasyonu kapsam dışında görülse de, NMOS sistemlerde CMOS komutlarını emüle etmek mümkün olabilir
- ALU kullanıldığı için, NMOS 6502 CMOS 65C02’yi emüle etse bile bayraklar NMOS tarzında ayarlanır
- Tersi yönde de aynısı geçerlidir
- Adresleme şu anda NMOS CPU tarzına göre yazılmıştır
- inline memory macro yaklaşımında peephole optimization fırsatları vardır
- Gerçek assembly öncesine bir “post-preprocessor” geçişi eklenebilir
- Araç zinciri daha karmaşık hale geleceğinden genel faydayı doğrulamak gerekir
- İndirilen kodu mevcut işi bozmadan çalıştırmak, 6o6’ın açıkça belirtilen kullanım alanlarından biridir
- İndirilen şeyi dinamik olarak çalıştıran bir Gopher client bileşeni olarak kullanma fikri vardır
- Yeni bir 6502 sistemi sıfırdan tasarlanacaksa, gerekli özellikleri donanımda uygulamak daha hızlı olabilir
- NMOS CPU’da koruma mekanizmalarının az olduğu ya da ek silikonun minimum tutulmak istendiği durumlarda, 6o6 esnek ve uyarlanabilir bir alternatif olur
Dağıtım ve lisans
- The Incredible KIMplement, ana sayfasında ve GitHub üzerinde sunuluyor
- 6o6, GitHub üzerinde sunuluyor ve yazıda ele alınan dört örnek de buna dahil
- KIMplement 1.0 güncellemesi, yayına hazırlık düzenlemeleri ve küçük hata düzeltmelerine odaklanıyor
- KIMplement içinde Dave Hassler’ın sağladığı Tiny PILOT da bulunuyor
- Tiny PILOT, Nicholas Vrtis’in 1979 tarihli MICRO magazine uygulamasına Bob Applegate ve Dave Hassler yamalarının eklenmiş hâlidir
- Dave Hassler ayrıca Carol Shaw ve Harry Stewart’ın 1980 Atari PILOT uygulamasından ELIZA’yı da taşıdı
- KIMplement ve 6o6’ın ikisi de Floodgap Free Software License ile dağıtılıyor
1 yorum
Hacker News yorumları
Basit ve kısıtlı bir 6502 olsa da, neredeyse 50 yıllık bir mimarinin sürekli yeni sınırlarına kadar zorlandığını görmek her zaman ilgi çekici
Ultra düşük maliyetli, yüksek hacimli pazarı hedefleyen bazı SoC'lerde hâlâ 6502 çekirdeği bulunuyor
10 sentlik bir RISC-V çekirdeğini geçmek kolay görünmüyor
İlk başta içinde 6502 adet 6502 çekirdeği olan çok çekirdekli bir SoC hayal edip güldüm ama FPGA ile denense eğlenceli bir proje olabilir gibi geliyor
Apple 2'de değişken hızlı bir genişleme kartı olarak 16 bitlik halefi 65816 kullanıyorum ama çoğunlukla 8 bit modunda çalıştırıyorum. Çünkü iyi çalışıyor ve kütüphane kodlarının çoğu da 8 bit
Bu hızlarda çip hızlı. Özellikle RAM ile CPU'nun 1:1 saatlendiği basit modeller düşünülürse daha da öyle. Benim durumumda kodu 1MHz veri yolu üzerinden çalıştırabildiğim için, çok çevrimli işlemlerin çoğu bellek fetch'i başına tek veri yolu çevrimi gibi oluyor
Ya da kartta 1MB RAM var ve bu RAM CPU hızında (0.15~16MHz) çalışıyor. Bu da yüksek seviyeli dillerde yazılmış büyük programların bile kullanılabilir hızlarda çalışmasına yetecek kadar hızlı olmasını sağlıyor. Tabii assembly inanılmaz hızlı
Bir şeyleri kurcalayıp hacklemek için oldukça eğlenceli bir ortam
GEOS'u bir GEOS penceresinin içinde çalıştırabilir misiniz?
Bu yazı epey süre yorumsuz duruyordu, ben de sonra dönüp bakarım diye düşünmüştüm ama asıl mesele Commodore 64'ün tamamen farklı bir 6502 tabanlı sistemi nasıl emüle ettiğinde yatıyor
“6o6”, yani “6502-on-6502”, 6502 CPU üzerinde çalışan, konuk kod çalıştırmayı tamamen kontrol eden tam sanallaştırılmış bir NMOS 6502 CPU yazılımı; buna belgelenmemiş opcode'lar ve jam opcode trap'leri de dahil, ayrıca tüm bellek erişimlerini soyutluyor
Bu sayede adres yeniden eşleme, geçersiz okuma-yazmaları yakalama, hatta tam sanal bellek yürütmesi bile mümkün oluyor. Sadece tüm işlev testlerini geçmekle kalmıyor, kendisini sanallaştıran kendisini bile sanallaştırabiliyor; bu, 6502 bakış açısından da ötesinde her açıdan etkileyici bir iş
Bana Zilog Z80'de korumalı mod olduğunu anlatan şu videoyu da hatırlattı: https://www.youtube.com/watch?v=DLSUAVPKeYk
İlk kez 6502 assembly'yi kendi kendime öğrendiğim zamanı hatırlattı. “The Visual Computer” diye bir kitap vardı ve disketin içinde bir emülatör de geliyordu; gerçekten ufuk açıcı bir deneyimdi
Kitabın PDF'ini buldum ama [1], disketle gelen yazılımın bir yerde durup durmadığını bilmiyorum
[1] https://files.commodore.software/reference-material/books/c6...
“Visual 6502” ise modern bir kapı/transistör düzeyi 6502 simülatörünün adı: http://visual6502.org/JSSim/index.html
https://archive.fo/2u3Y8