- 6o6 एक ऐसा प्रोजेक्ट है जो सीमित सुरक्षा सुविधाओं वाले NMOS 6502 के ऊपर सॉफ़्टवेयर के रूप में फिर से 6502 चलाता है, ताकि पुराने 8-बिट सिस्टमों में नियंत्रित किया जा सकने वाला एक वर्चुअल execution layer जोड़ा जा सके
- यह guest code के instruction execution और memory access को बीच में नियंत्रित करके address remapping, अवैध read/write को रोकना, और jam opcode trap जैसी सुविधाएँ देता है
- इसका मूल विचार host 6502 के ALU को ज्यों का त्यों उधार लेना है: guest registers और flags को host पर लोड किया जाता है, वही instruction चलाया जाता है, फिर परिणाम वापस सहेज लिया जाता है
- सत्यापन के लिए Klaus Dormann की 6502 functional test suite और lib6502-आधारित test environment का उपयोग किया गया, और optimized configuration ने 1,602,516,769 instructions चलाए, जो non-optimized configuration की तुलना में 36.5% कम है
- साथ में जारी The Incredible KIMplement 1.0 और कई उदाहरण KIM-1 emulation, nested virtualization, task switching, और geoRAM-आधारित external memory system तक 6o6 के उपयोग का दायरा दिखाते हैं
6o6 और KIMplement ने क्या जारी किया
- The Incredible KIMplement 1.0 1KB, 1MHz MOS/Commodore KIM-1 6502 single-board computer का emulation करता है
- यह बिना किसी expansion के Commodore 64 पर चलता है
- यह KIM के built-in TTY को support करता है, और वास्तविक computer के serial port के जरिए access भी संभव है
- address space को 16K तक बढ़ाया गया है
- 6o6 “6502-on-6502” का संक्षिप्त रूप है, और यह 6502 CPU पर चलने वाला पूर्ण software NMOS 6502 virtual CPU है
- यह guest code execution को नियंत्रित करता है
- undocumented opcode और jam opcode को trap करता है
- सभी memory access को abstract करता है
- address remapping, अवैध read/write interception, और virtual-memory-आधारित execution को support करता है
- Commodore 64 और Apple IIe पर guest
hello worldचलाना, और फिर 6o6 के अंदर दोबारा 6o6 चलाना, यानी nested virtualization, भी काम करता है- stage 1 लगभग तुरंत चलता है
- stage 2 अधिक धीमा है
- stage 3 बहुत धीमा है, लेकिन काम करता है
6502 को virtualization की ज़रूरत क्यों है
- शुरुआती personal computers में आम तौर पर एक ही program पूरे machine को नियंत्रित करता था, और program के खराब होने पर reboot करके समस्या हल की जा सकती थी
- multi-user या multitasking environment में गलत code दूसरे address space को नुकसान पहुँचा सकता है, खतरनाक instructions चला सकता है, या resources पर कब्ज़ा कर सकता है
- NMOS 6502 लगभग 4,000 से कम transistors वाला एक सरल CPU है, इसलिए इसकी protection capabilities सीमित हैं
- कई ऐतिहासिक NMOS 6502 systems में zero page या processor stack की स्थिति को स्वतंत्र रूप से बदलना कठिन था
- code addresses को मनचाही जगह remap करके बिना fixup के चलाने की सुविधा नहीं थी
- कुछ memory locations तक पहुँच को व्यापक रूप से प्रतिबंधित नहीं किया जा सकता था
- undocumented jam या
KILopcode चलने पर processor पूरी तरह रुक सकता था
- कुछ समस्याओं को hardware से कम किया जा सकता है
- periodic NMI देने से interrupt flag सेट करके system पर कब्ज़ा करने की कोशिश कर रही process को बाहर से रोका जा सकता है
- कुछ 6502 multitasking kernels ने इसी तरीके से preemptive task switching लागू किया
- Eastern House Software का “Trap65” जैसा in-circuit emulator गलत opcodes को trap किए जा सकने वाले BRK में बदल सकता था, लेकिन यह महँगा था और जटिल bus manipulation में इसकी सीमाएँ थीं
6o6 कैसे execute करता है
- साधारण interpreter तरीका भी 6502 पर व्यावहारिक हो सकता है
- 6502 में registers कम हैं
- instructions केवल 56 हैं और addressing modes भी बहुत अधिक नहीं हैं
- processor state को track करना अपेक्षाकृत आसान है
- 6o6 का “virtualization” host 6502 के ALU को guest की internal operations के लिए इस्तेमाल करने में है
- guest accumulator और flags को host CPU में लोड किया जाता है
- guest जो instruction चलाना चाहता है, वही instruction host पर चलाया जाता है
- परिणाम और flags को सहेजा जाता है, फिर host state को साफ किया जाता है
- इस तरीके का लाभ यह है कि arithmetic और flag handling को सीधे reimplement नहीं करना पड़ता
- decimal mode, यानी BCD arithmetic, भी स्वाभाविक रूप से काम करता है
- क्योंकि गणना असली 6502 करता है, इसलिए परिणाम भी 6502 जैसा ही होता है
- memory value पढ़ते समय या registers transfer करते समय भी negative flag और zero flag के लिए यही तरीका अपनाया जाता है
- implementation में self-modifying code का उपयोग होता है, इसलिए इसे ROM में रखने के लिए अलग सावधानी चाहिए
VM, harness, और kernel की संरचना
- 6o6 VM पूरे system की जगह engine की भूमिका निभाता है
- execution environment को तीन हिस्सों में बाँटा गया है
- VM: hardware-independent virtual CPU जो वास्तविक 6502 पर चलता है
- harness: guest memory और नियंत्रित hardware के लिए interface
- kernel: VM को कॉल करने और exceptions व guest state को संभालने वाला control loop
- harness standardized jump table के जरिए binary interface देता है
- यह खास addresses के load/store को implement करता है
- instruction fetch को संभालता है
- hardware stack और stack pointer को बनाए रखता है
- VM page size या memory paging की मौजूदगी के बारे में कोई धारणा नहीं बनाता
- harness साधारण addition/bit-shift आधारित address translation से लेकर paged virtual memory तक implement कर सकता है
- page fault को बाहर भेजने की बजाय harness load/store के दौरान paging in/out कर सकता है
- protection exceptions भी harness ही उत्पन्न कर सकता है
- kernel VM execution शुरू करता है और VM से लौटे status code की व्याख्या करता है
- harness या 6o6 से उठे exceptions को संभालता है
- guest registers और PC को देख या बदल सकता है
- कुछ service routines को native तरीके से संभाल सकता है
- VM calls के बीच guest CPU “रुकी हुई” स्थिति में रहता है, इसलिए state capture या context switching संभव है
- VM virtual IRQ, NMI, या reset सीधे उत्पन्न नहीं करता
- ऐसे events कब होंगे, यह kernel तय करता है
- BRK supported है, लेकिन VM stack सेट करके नए PC पर जाने की बजाय exception लौटाता है
KIMplement में 6o6 का उपयोग
- KIMplement का harness standard 6502 stack और KIM-4 expansion devices का virtualization करता है
$0000-$17ff: read/write memory$1800-$1fff: ROM$2000-$3fff: RAM$4000-$fff7: unmapped write-protected space- vectors के लिए
$1ff8-$1fffको$fff8-$ffffपर mirror किया गया है
- host Commodore 64 निचले 16K को
$4000-$7fffमें रखता है, और बाकी harness synthesize करता है - KIMplement kernel RRIOT emulation, LED display, TTY service, stop और Single-Step Switch के लिए NMI injection, और KIM-1 ROM monitor के कुछ traps को संभालता है
- VM execution के बाद KIMplement kernel 6o6 के PC को देखकर तय करता है कि मौजूदा routine को intercept करना है या नहीं
- TTY और कुछ अन्य सुविधाएँ इसी तरीके से implement की गई हैं
performance optimization
- 6o6 और harness के बीच calls बड़ा bottleneck बन सकती हैं
- साधारण instruction को भी कम से कम एक fetch चाहिए
- indirect addressing और अधिक memory accesses पैदा कर सकती है
- KIMplement 0.2 में कुछ memory load को preprocessor macros से inline किया गया
- arbitrary virtual addresses के लिए load routine और zero page optimized load routine को VM से सीधे जोड़ा गया
- speed में बड़ा सुधार हुआ, लेकिन VM का आकार बढ़ गया
- store कम बार होते हैं और अधिक जटिल हैं, इसलिए उन्हें subroutine calls के रूप में रखा गया
- बाद की iteration में inline macros के program counter access की inefficiency को भी सुधारा गया, जिससे instruction fetch और बेहतर हुआ
- KIMplement 0.3 में “extra helpings” नाम का एक आदिम instruction fusion जोड़ा गया
- memory को न छूने वाले instructions को हर बार तुरंत kernel में लौटने की ज़रूरत नहीं होती
- immediate instructions, accumulator-केंद्रित instructions, अधिकतर implied instructions, और non-taken branches इसमें आते हैं
- load/store, PC में non-sequential बदलाव, या exceptions आने पर VM instruction bundling की कोशिश रोक देता है
- extra helpings VM को अपने आप तेज़ नहीं बनाता
- KIMplement की तरह अगर PC position के आधार पर features सीमित किए जाएँ, तो यह थोड़ा धीमा भी हो सकता है
- लेकिन जिन instructions पर kernel को कोई बदलाव देखने की ज़रूरत नहीं, उन पर बेकार execution से बचकर पूरा system तेज़ होता है
- जिन applications को PC पर बहुत सूक्ष्म नियंत्रण चाहिए, उनके लिए इसे चरणबद्ध या पूरी तरह बंद करने का विकल्प है
validation और test results
- सत्यापन के लिए Klaus Dormann की functional test suite का उपयोग किया गया
- दिया गया binary hardware के बारे में कोई मान्यता नहीं रखता
- सफल समाप्ति का संकेत किसी खास location पर infinite loop से दिया जाता है
- tests को Ian Piumarta के lib6502 CPU emulator पर shell से सीधे चलने लायक बनाया गया
- decimal mode के edge cases की वजह से lib6502 शुरू में fail हुआ, लेकिन patch के बाद pass हो गया
- Klaus द्वारा दिया गया binary पूरा 64K था, इसलिए उसे default 6502 address space के भीतर 6o6 के साथ नहीं रखा जा सका
- lib6502 में 32K bank-switched minimal system patch जोड़ा गया
$7000-$efffक्षेत्र का उपयोग किया गया, और test binary के पहले 32K और बाद के 32K को अलग-अलग banks में रखा गया
- तीन configurations पर test किया गया
- extra helpings नहीं, inline fetch macro नहीं
- inline fetch macro है, extra helpings नहीं
- inline fetch macro और extra helpings दोनों हैं
- तीनों configurations ने Klaus suite pass की
- instruction count के परिणाम इस प्रकार थे
- 6o6 के बिना lib6502: 30,646,178 instructions
- बिना optimization वाला 6o6: 2,188,322,914 instructions
- inline fetch macro के साथ: 1,713,350,225 instructions
- inline fetch macro और extra helpings के साथ: 1,602,516,769 instructions
- सबसे तेज़ 6o6 configuration ने सबसे कम optimized configuration की तुलना में 36.5% कम instructions चलाए
- सबसे तेज़ configuration ने guest के प्रति instruction औसतन 52.3 instructions चलाए
- इसमें harness, kernel, और 6o6 execution सभी शामिल हैं
- क्योंकि हर instruction का cycle count अलग है, इसे सीधे speed multiplier की तरह नहीं पढ़ना चाहिए
शामिल examples
- hello world example पहले वही program native CPU पर चलाता है, फिर 6o6 के जरिए चलाता है
- Commodore 64 पर इसे
$ffd2, और Apple II पर$fdedcharacter output routine से map किया गया है - kernel यह पहचानता है कि PC character output routine की ओर इशारा कर रहा है, guest accumulator लेता है, native ROM routine को कॉल करता है, फिर stack से return address निकालकर loop में लौटता है
- Commodore 64 पर इसे
- inception example में 6o6 अपने ही payload के रूप में खुद को उसी harness और kernel के साथ execute करता है
- हर stage का अपना zero page और stack होता है
- मौजूदा 6o6 self-modifying code का उपयोग करता है, इसलिए हर stage के लिए VM की अलग copy चाहिए
- stage 3 में अधिकतर memory VM की 3 copies में चली जाती है, और inline fetch macro वाला VM प्रति copy 10KB से अधिक लेता है
- nested execution में
CHROUTcall stage 3 से stage 2, फिर stage 1 से होकर अंत में native routine तक पहुँचती है - payload समाप्ति के लिए RTS instruction को “kick” की तरह इस्तेमाल किया जाता है
- शुरुआत में stack पर कोई return address नहीं होता, इसलिए RTS stack underflow पैदा करता है
- harness इसे exception के रूप में report करता है, और kernel इसे सामान्य समाप्ति मानता है
- गहरे stages में भी यही तरीका ऊपर के kernels तक propagate होता है
- Apple II पर execution के बाद
CALL 2051, और Commodore 64 परRUNके जरिए इसे फिर से चलाया जा सकता है- Apple II version
$9000के ऊपर के resident DOS क्षेत्र तक उपयोग करता है, इसलिए execution के बाद reboot की सलाह दी जाती है
- Apple II version
task switching example
- tasks example दो स्वतंत्र tasks के बीच आने-जाने वाला एक छोटा task-switching kernel है
- हर task का अपना zero page, stack, और छोटा code address region है, और वे एक-दूसरे या VM के अस्तित्व से अनजान रहते हैं
- एक task alphabet दिखाता है, और दूसरा numbers दिखाता है
- numbers को visually अलग दिखाने के लिए reverse video में दिखाया जाता है
- हर key press पर task switch होता है
- दोनों tasks state save करने के लिए zero page की वही locations इस्तेमाल करते हैं, लेकिन zero page अलग-अलग होने से वे अपनी पिछली स्थिति से आगे बढ़ते हैं
- context switching के लिए केवल current task information और हर task के A, X, Y, P, S, PC state-save क्षेत्र चाहिए
- harness “on CPU” task देखकर zero page, stack, और executing code का physical address चुनता है
- kernel switch के समय दूसरे state को save/load करता है और दूसरी task को “on processor” के रूप में चिह्नित करता है
geoRAM-आधारित 64K external memory example
- vmgr example केवल Commodore 64 के लिए है, और system की अपनी RAM का उपयोग किए बिना 64K address space को external memory के रूप में उपलब्ध कराता है
- geoRAM, Commodore की आधिकारिक REU से अलग एक paged RAM device है
- REU DMA-केंद्रित है, और MOS 8726 REC का उपयोग करके main memory के साथ read/write/exchange operations करती है
- geoRAM memory को I/O range
$de00की 256-byte window page में map करता है - control registers
$dffe,$dfffपर हैं - आधुनिक compatible clones की क्षमता 4MB तक हो सकती है
- VICE geoRAM emulation को support करता है
- example, RC2014 Z80 kit computer के लिए दिए गए 6502 processor module की ROM का उपयोग करता है
- ROM में monitor और Lee Davison का EhBASIC शामिल है
- उपयोग की गई ROM GitHub पर उपलब्ध pre-built ROM है, और 6551 version का उपयोग करती है
- harness guest ROM क्षेत्र
$c100से आगे की writes को अनदेखा करता है- 16K से कम writes के लिए fast path का उपयोग होता है
- उससे ऊपर mask और shift से geoRAM bank समायोजित किया जाता है
- मौजूदा geoRAM page को cache किया जाता है, ताकि उसी page पर बार-बार setup न करना पड़े
- इस example में kernel और main program को एक में मिला दिया गया है
- यह geoRAM की मौजूदगी और कामकाज की जाँच करता है
- ROM image को geoRAM में कॉपी करता है
- BRK monitor पर लौटता है
- illegal instruction, user-defined instruction trap आदि को भी BRK की तरह संभाला जाता है
- RC2014 ROM के serial vector को intercept करके एक साधारण terminal emulation किया गया है
- PETSCII और terminal characters के बीच conversion होता है
- एक छोटा cursor बनाए रखा जाता है
- guest registers और flags को परिणाम के अनुसार समायोजित किया जाता है
CTRL-SHIFT-Commodoreसे emulated system को reset किया जा सकता है और memory बनी रहती है
- EhBASIC cold start में अगर memory size हाथ से न दी जाए, तो C64 और geoRAM संयोजन में 32768 bytes free खोजने में लगभग 1 मिनट लगता है
- ROM
$8000hard cap के साथ build किया गया है, इसलिए वास्तव में अधिक memory होने पर भी सीमा 32768 bytes रहती है $8000-$c0ffमें कुछ और रखा जा सकता है- EhBASIC lowercase command या keyword स्वीकार नहीं करता, इसलिए सब कुछ UPPERCASE में दर्ज करना पड़ता है
- ROM
- यह वास्तविक Commodore 128DCR और 512K geoRAM cartridge पर भी चलता है
- floating-point operations भी सही काम करती हैं
- bad instructions को नियंत्रित तरीके से तुरंत intercept किया जाता है
- 256-byte window को छोड़कर screen पर दिख रहा system 6502 के अपने address space में नहीं चल रहा होता
- 512K geoRAM में 64K वाले 6502 system के 8 tasks अलग-अलग रखे जा सकते हैं
आगे के सुधार और उपयोग
- 6o6 को ROM से चलाने योग्य बनाने वाले सुधार संभव हैं, लेकिन उसके लिए refactoring चाहिए और यह और धीमा हो सकता है, इसलिए यह एक विकल्प भर है
- 65816 emulation को दायरे से बाहर माना गया है, लेकिन NMOS systems पर CMOS instructions का emulation संभव हो सकता है
- ALU का उपयोग होने के कारण, अगर NMOS 6502 CMOS 65C02 का emulation करे, तब भी flags NMOS तरीके से सेट होंगे
- उलटी दिशा में भी यही बात लागू होती है
- addressing अभी NMOS CPU शैली में लिखा गया है
- inline memory macro पद्धति में peephole optimization के अवसर हैं
- वास्तविक assembly से पहले “post-preprocessor” pass रखा जा सकता है
- लेकिन toolchain अधिक जटिल हो जाएगी, इसलिए सामान्य लाभ की पुष्टि करनी होगी
- डाउनलोड किए गए code को मौजूदा task को बिगाड़े बिना चलाना 6o6 के स्पष्ट उपयोगों में से एक है
- इसे Gopher client के हिस्से के रूप में उपयोग कर डाउनलोड किए गए code को dynamically चलाने का विचार है
- अगर कोई नया 6502 system सीधे डिजाइन किया जा रहा हो, तो ज़रूरी features को hardware में लागू करना अधिक तेज़ हो सकता है
- लेकिन NMOS CPU में जहाँ protection कम हो, या अतिरिक्त silicon को न्यूनतम रखना हो, वहाँ 6o6 एक लचीला और अनुकूलनीय विकल्प बनता है
वितरण और लाइसेंस
- The Incredible KIMplement होमपेज और GitHub पर उपलब्ध है
- 6o6 GitHub पर उपलब्ध है, और इसमें लेख में बताए गए चार examples भी शामिल हैं
- KIMplement 1.0 update सार्वजनिक रिलीज़ के लिए cleanup और छोटे bug fixes पर केंद्रित है
- KIMplement में Dave Hassler द्वारा दिया गया Tiny PILOT भी शामिल है
- Tiny PILOT, 1979 की MICRO magazine में Nicholas Vrtis द्वारा लिखे गए implementation पर Bob Applegate और Dave Hassler के patches जोड़कर बना है
- Dave Hassler ने Carol Shaw और Harry Stewart के 1980 Atari PILOT implementation से ELIZA भी port किया है
- KIMplement और 6o6 दोनों Floodgap Free Software License के तहत वितरित किए जाते हैं
1 टिप्पणियां
Hacker News की राय
6502 इतना सरल और सीमित होने के बावजूद, लगभग 50 साल पुरानी architecture को लगातार नई सीमाओं तक धकेला जाता देखना हमेशा दिलचस्प है
बेहद कम लागत और mass production वाले market को लक्ष्य करने वाले कुछ SoC में अब भी 6502 core मौजूद है
10-cent वाले RISC-V core को मात देना आसान नहीं लगता
पहले तो 6502 cores वाले 6502 multicore SoC की कल्पना करके हंसी आई, लेकिन FPGA पर करके देखें तो यह मजेदार project हो सकता है
मैं Apple 2 में 16-bit successor 65816 को variable-speed expansion card के रूप में लगाकर इस्तेमाल कर रहा हूं, लेकिन ज्यादातर यह 8-bit mode में ही चलता है। क्योंकि यह ठीक काम करता है और library code भी अधिकतर 8-bit है
इस speed पर chip तेज है। खासकर अगर RAM और CPU के 1:1 clock होने वाले सरल model को सोचें, तो और भी। मेरे मामले में मैं 1MHz bus के जरिए code execute कर सकता हूं, इसलिए ज्यादातर multi-cycle operations memory fetch के प्रति single bus cycle जैसे हो जाते हैं
या card में 1MB RAM है और यह RAM CPU speed (0.15~16MHz) पर काम करती है। इससे high-level language में लिखे बड़े programs भी इस्तेमाल लायक speed पर चलाने जितनी तेजी मिल जाती है। जाहिर है, assembly तो बेहिसाब तेज है
तरह-तरह की hacking करके देखने के लिए यह काफी मजेदार environment है
क्या GEOS को GEOS window के अंदर चला सकते हैं?
यह post काफी देर तक बिना comments के दिख रही थी, इसलिए सोचा था बाद में देखूंगा; असली बात इस तरीके में है कि Commodore 64 एक पूरी तरह अलग 6502-based system को emulate करता है
“6o6”, यानी “6502-on-6502”, 6502 CPU पर चलने वाला एक पूरा virtualization software NMOS 6502 CPU है; यह undocumented opcodes और jam opcode traps तक सहित guest code execution पर पूरा control रखता है, और memory access को भी पूरी तरह abstract करता है
इसलिए address remapping, illegal reads/writes को intercept करना, और यहां तक कि पूरी तरह virtual memory execution भी संभव है। यह न सिर्फ पूरा feature test pass करता है, बल्कि खुद को virtualize करने वाले खुद को भी virtualize कर सकता है—यह 6502 के नजरिए से परे, हर नजरिए से कमाल का काम है
Zilog Z80 में protected mode होने वाला video भी याद आया: https://www.youtube.com/watch?v=DLSUAVPKeYk
जब मैंने पहली बार खुद से 6502 assembly सीखना शुरू किया था, वह याद आ गया। “The Visual Computer” नाम की एक किताब थी और floppy disk में emulator साथ आता था; सचमुच आंखें खोल देने वाला अनुभव था
किताब का PDF मिल गया [1], लेकिन floppy में जो software था, वह कहीं बचा है या नहीं, पता नहीं
[1] https://files.commodore.software/reference-material/books/c6...
“Visual 6502” आधुनिक gate और transistor-level 6502 simulator का नाम है: http://visual6502.org/JSSim/index.html
https://archive.fo/2u3Y8