จำลองเสมือน 6502 บน 6502 ด้วย 6o6
(oldvcr.blogspot.com)- 6o6 เป็นโปรเจกต์ที่รัน 6502 อีกตัวด้วยซอฟต์แวร์บน NMOS 6502 ที่มีฟังก์ชันป้องกันอ่อนแอ เพื่อเพิ่มเลเยอร์การรันแบบเสมือนที่ควบคุมได้ให้กับระบบ 8 บิตรุ่นเก่า
- ควบคุมการรันคำสั่งและการเข้าถึงหน่วยความจำของโค้ด guest ระหว่างทาง จึงให้ฟังก์ชันอย่าง การ remap ที่อยู่, การบล็อกการอ่าน/เขียนที่ผิดกฎ และการ trap jam opcode
- หัวใจสำคัญคือการยืม ALU ของ host 6502 มาใช้โดยตรง โดยนำรีจิสเตอร์และแฟล็กของ guest ไปวางบน host รันคำสั่งเดียวกัน แล้วบันทึกผลลัพธ์กลับ
- การตรวจสอบใช้ 6502 functional test suite ของ Klaus Dormann และสภาพแวดล้อมทดสอบที่อิง lib6502 โดยคอนฟิกที่ปรับแต่งแล้วรัน 1,602,516,769 คำสั่ง ซึ่งน้อยกว่าคอนฟิกที่ไม่ปรับแต่ง 36.5%
- The Incredible KIMplement 1.0 ที่เผยแพร่มาพร้อมกัน และตัวอย่างหลายรายการ แสดงขอบเขตการใช้งานของ 6o6 ตั้งแต่การจำลอง KIM-1, nested virtualization, การสลับงาน ไปจนถึงระบบหน่วยความจำภายนอกที่ใช้ geoRAM
สิ่งที่ 6o6 และ KIMplement เปิดเผย
- The Incredible KIMplement 1.0 จำลองคอมพิวเตอร์บอร์ดเดี่ยว MOS/Commodore KIM-1 6502 ขนาด 1KB, 1MHz
- ทำงานบน Commodore 64 ที่ไม่มีส่วนขยายได้
- รองรับ TTY ในตัวของ KIM และสามารถเข้าถึงผ่านพอร์ต serial ของคอมพิวเตอร์จริงได้ด้วย
- address space ถูกขยายเป็น 16K
- 6o6 ย่อมาจาก “6502-on-6502” เป็น CPU เสมือน NMOS 6502 แบบซอฟต์แวร์เต็มรูปแบบที่รันบน CPU 6502
- ควบคุมการรันโค้ด guest
- trap opcode ที่ไม่ได้ document ไว้และ jam opcode
- ทำ abstraction ให้การเข้าถึงหน่วยความจำทั้งหมด
- รองรับการ remap ที่อยู่, การดักอ่าน/เขียนที่ผิดกฎ และการรันบน virtual memory
- การรัน
hello worldของ guest บน Commodore 64 และ Apple IIe รวมถึงการรัน 6o6 ซ้อนอยู่ใน 6o6 อีกทีแบบ nested virtualization ก็ทำงานได้- stage 1 รันแทบจะทันที
- stage 2 ช้ากว่า
- stage 3 ช้ามากแต่ทำงานได้
ทำไม 6502 จึงต้องมี virtualization
- ในคอมพิวเตอร์ส่วนบุคคลยุคแรก โดยทั่วไปโปรแกรมเดียวจะควบคุมเครื่องทั้งเครื่อง และถ้าโปรแกรมทำงานผิดพลาดก็แก้ด้วยการรีบูตได้
- ในสภาพแวดล้อมแบบหลายผู้ใช้หรือหลายงาน โค้ดที่ผิดพลาดอาจทำลาย address space อื่น รันคำสั่งอันตราย หรือผูกขาดทรัพยากรได้
- NMOS 6502 เป็น CPU เรียบง่ายที่มีทรานซิสเตอร์ไม่ถึงประมาณ 4,000 ตัว จึงมีข้อจำกัดด้านฟังก์ชันป้องกัน
- ระบบ NMOS 6502 ในอดีตจำนวนมากย้ายตำแหน่ง zero page หรือ processor stack ได้ยาก
- ไม่มีฟังก์ชัน remap ที่อยู่โค้ดไปยังตำแหน่งใดก็ได้แล้วรันโดยไม่ต้อง fixup
- ไม่สามารถห้ามการเข้าถึงตำแหน่งหน่วยความจำเฉพาะแบบครอบคลุมได้
- หาก opcode jam หรือ
KILที่ไม่ได้ document ไว้ถูกรัน โปรเซสเซอร์อาจหยุดสนิทได้
- ปัญหาบางอย่างบรรเทาได้ด้วยฮาร์ดแวร์
- หากสร้าง NMI เป็นระยะ จะสามารถหยุด process ที่พยายามผูกขาดระบบจากภายนอกได้ด้วยการตั้ง interrupt flag
- kernel แบบ multitasking บางตัวของ 6502 ใช้วิธีนี้เพื่อทำ preemptive task switching
- in-circuit emulator อย่าง “Trap65” ของ Eastern House Software สามารถเปลี่ยน opcode ที่ผิดให้เป็น BRK ที่ trap ได้ แต่มีราคาแพงและมีข้อจำกัดในการจัดการ bus ที่ซับซ้อน
วิธีการรันของ 6o6
- วิธี interpreter แบบง่ายก็อาจใช้งานได้จริงบน 6502
- 6502 มีจำนวนรีจิสเตอร์น้อย
- มีคำสั่ง 56 รายการ และ addressing mode ก็ไม่มาก
- การติดตามสถานะโปรเซสเซอร์ค่อนข้างง่าย
- “virtualization” ของ 6o6 อยู่ที่การใช้ ALU ของ host 6502 สำหรับการคำนวณภายในของ guest
- โหลด guest accumulator และแฟล็กเข้า CPU host
- รันคำสั่งเดียวกับที่ guest จะรันบน host
- บันทึกผลลัพธ์และแฟล็ก แล้วจัดการสถานะของ host ให้เรียบร้อย
- ข้อดีของวิธีนี้คือไม่ต้อง reimplement การคำนวณและการจัดการแฟล็กเอง
- decimal mode หรือการคำนวณ BCD ก็ทำงานได้อย่างเป็นธรรมชาติ
- เพราะ 6502 จริงเป็นตัวคำนวณ ผลลัพธ์จึงเหมือน 6502
- เมื่ออ่านค่าหน่วยความจำหรือถ่ายโอนรีจิสเตอร์ ก็ใช้วิธีเดียวกันเพื่อจัดการ negative flag และ zero flag
- การ implement ใช้ self-modifying code ดังนั้นหากจะใส่ใน ROM ต้องระวังเพิ่มเติม
โครงสร้าง VM, harness และ kernel
- 6o6 VM ทำหน้าที่เป็น engine ไม่ใช่ระบบทั้งหมด
- สภาพแวดล้อมการรันแบ่งเป็นสามส่วน
- VM: CPU เสมือนที่ไม่ผูกกับฮาร์ดแวร์ ทำงานบน 6502 จริง
- harness: อินเทอร์เฟซไปยังหน่วยความจำ guest และฮาร์ดแวร์ที่ถูกจัดการ
- kernel: control loop ที่เรียก VM และจัดการ exception กับสถานะ guest
- harness ให้ binary interface ผ่าน jump table มาตรฐาน
- implement load/store ของที่อยู่เฉพาะ
- จัดการ instruction fetch
- รักษา hardware stack และ stack pointer
- VM ไม่สมมติขนาด page หรือการมีอยู่ของ memory paging
- harness สามารถ implement ได้ตั้งแต่การแปลงที่อยู่แบบบวกง่าย ๆ/bit shift ไปจนถึง paged virtual memory
- harness สามารถทำ paging in/out ระหว่างกระบวนการ load/store ได้โดยไม่ส่ง page fault ออกไปภายนอก
- harness ยังสามารถสร้าง protection exception ได้
- kernel เริ่มการรัน VM และตีความ status code ที่ VM ส่งกลับ
- จัดการ exception ที่เกิดจาก harness หรือ 6o6 เอง
- ตรวจสอบหรือแก้ไขรีจิสเตอร์ guest และ PC
- จัดการ service routine บางอย่างแบบ native ได้
- ระหว่างการเรียก VM แต่ละครั้ง CPU guest อยู่ในสถานะ “หยุด” จึงสามารถจับสถานะหรือทำ context switching ได้
- VM ไม่สร้าง IRQ, NMI, reset เสมือนโดยตรง
- kernel เป็นผู้ตัดสินช่วงเวลาที่เหตุการณ์เหล่านี้เกิดขึ้น
- รองรับ BRK แต่ VM จะตั้ง stack แล้วส่งคืน exception โดยไม่ย้ายไปยัง PC ใหม่
การใช้ 6o6 ใน KIMplement
- harness ของ KIMplement จำลอง stack มาตรฐานของ 6502 และอุปกรณ์ส่วนขยาย KIM-4
$0000-$17ff: หน่วยความจำอ่าน/เขียน$1800-$1fff: ROM$2000-$3fff: RAM$4000-$fff7: พื้นที่ที่ไม่ได้ map และเขียนไม่ได้$1ff8-$1fffถูก mirror ไปที่$fff8-$ffffสำหรับ vector
- host Commodore 64 เก็บ 16K ล่างไว้ที่
$4000-$7fffและส่วนที่เหลือ harness สังเคราะห์ขึ้น - kernel ของ KIMplement จัดการ RRIOT emulation, การแสดงผล LED, บริการ TTY, การแทรก NMI สำหรับ stop และ Single-Step Switch รวมถึงการ trap บางส่วนของ KIM-1 ROM monitor
- หลังรัน VM แล้ว kernel ของ KIMplement จะตรวจ PC ของ 6o6 เพื่อตัดสินใจว่าจะดัก routine ปัจจุบันหรือไม่
- TTY และฟังก์ชันบางส่วน implement ด้วยวิธีนี้
การปรับแต่งประสิทธิภาพ
- การเรียกระหว่าง 6o6 กับ harness อาจเป็นคอขวดใหญ่
- แม้แต่คำสั่งง่าย ๆ ก็ต้อง fetch อย่างน้อยหนึ่งครั้ง
- indirect addressing อาจทำให้เกิดการเข้าถึงหน่วยความจำมากขึ้น
- ใน KIMplement 0.2 มีการ inline บางส่วนของ memory load ด้วย preprocessor macro
- เชื่อม load routine สำหรับที่อยู่เสมือนใด ๆ และ load routine ที่ปรับแต่งสำหรับ zero page เข้ากับ VM โดยตรง
- ความเร็วดีขึ้นมาก แต่ขนาด VM ใหญ่ขึ้น
- store เกิดไม่บ่อยและซับซ้อนกว่า จึงยังคงเป็นการเรียก subroutine
- ใน iteration ล่าสุด ยังได้จัดการความไม่มีประสิทธิภาพของวิธีที่ inline macro เข้าถึง program counter เพื่อปรับปรุง instruction fetch เพิ่มเติม
- KIMplement 0.3 เพิ่ม instruction fusion แบบดั้งเดิมที่เรียกว่า “extra helpings”
- คำสั่งที่ไม่แตะหน่วยความจำไม่จำเป็นต้องกลับไปหา kernel ทันที
- เป้าหมายคือคำสั่ง immediate value, คำสั่งที่เน้น accumulator, คำสั่ง implied ส่วนใหญ่ และ branch ที่ไม่เกิดการกระโดด
- หากเกิด load/store, การเปลี่ยน PC แบบไม่ต่อเนื่อง หรือ exception VM จะหยุดพยายามรวมคำสั่ง
- extra helpings ไม่ได้ทำให้ตัว VM เร็วขึ้นเอง
- หากจำกัดฟังก์ชันตามตำแหน่ง PC แบบ KIMplement ก็อาจช้าลงเล็กน้อย
- แต่ทำให้ส่วนอื่นของระบบโดยรวมเร็วขึ้น เพราะ kernel ไม่ถูกรันโดยไม่จำเป็นในทุกคำสั่งที่ไม่มีการเปลี่ยนแปลงให้สังเกต
- อาจรบกวนแอปพลิเคชันที่ต้องควบคุม PC อย่างละเอียด จึงมีตัวเลือกปิดแบบเป็นขั้นหรือปิดทั้งหมด
การตรวจสอบและผลการทดสอบ
- การตรวจสอบใช้ functional test suite ของ Klaus Dormann
- ไบนารีที่ให้มาไม่ตั้งสมมติฐานเกี่ยวกับฮาร์ดแวร์
- การจบแบบสำเร็จส่งสัญญาณด้วย infinite loop ที่ตำแหน่งเฉพาะ
- การทดสอบถูกตั้งค่าให้รันโดยตรงจาก shell โดยใช้ lib6502 CPU emulator ของ Ian Piumarta
- ตอนแรก lib6502 ล้มเหลวเพราะ edge case ของ decimal mode และผ่านหลังจาก patch แล้ว
- ไบนารีที่ Klaus ให้มาเป็น 64K ทั้งหมด จึงวางร่วมกับ 6o6 ใน address space พื้นฐานของ 6502 ไม่ได้
- เพิ่ม patch ระบบขั้นต่ำแบบ 32K bank-switched ให้ lib6502
- ใช้ช่วง
$7000-$efffและวาง 32K หน้าและ 32K หลังของ test binary ไว้ในคนละ bank
- ทดสอบด้วยสามคอนฟิก
- ไม่มี extra helpings, ไม่มี inline fetch macro
- มี inline fetch macro, ไม่มี extra helpings
- มีทั้ง inline fetch macro และ extra helpings
- ทั้งสามคอนฟิกผ่าน Klaus suite
- ผลจำนวนคำสั่งมีดังนี้
- lib6502 ที่ไม่มี 6o6: 30,646,178 คำสั่ง
- 6o6 ที่ไม่ปรับแต่ง: 2,188,322,914 คำสั่ง
- ใช้ inline fetch macro: 1,713,350,225 คำสั่ง
- ใช้ inline fetch macro และ extra helpings: 1,602,516,769 คำสั่ง
- คอนฟิก 6o6 ที่เร็วที่สุดรัน คำสั่งน้อยกว่า 36.5% เมื่อเทียบกับคอนฟิกที่ปรับแต่งน้อยที่สุด
- คอนฟิกที่เร็วที่สุดรันเฉลี่ย 52.3 คำสั่งต่อคำสั่ง guest หนึ่งคำสั่ง
- ตัวเลขนี้รวม harness, kernel และการรัน 6o6 ทั้งหมด
- เนื่องจากแต่ละคำสั่งมี cycle count ต่างกัน จึงไม่ควรตีความเป็นอัตราเร็ว
ตัวอย่างที่รวมมา
- ตัวอย่าง hello world รันโปรแกรมเดียวกันบน CPU native ก่อน แล้วจึงรันผ่าน 6o6
- บน Commodore 64 ถูก map ไปยัง routine พิมพ์ตัวอักษรที่
$ffd2ส่วนบน Apple II อยู่ที่$fded - เมื่อ kernel ตรวจพบว่า PC ชี้ไปยัง routine พิมพ์ตัวอักษร จะนำ guest accumulator มาเรียก ROM routine แบบ native แล้วดึง return address จาก stack เพื่อกลับเข้า loop
- บน Commodore 64 ถูก map ไปยัง routine พิมพ์ตัวอักษรที่
- ตัวอย่าง inception ใช้ harness และ kernel เดียวกันให้ 6o6 รันตัวเองเป็น payload
- แต่ละ stage มี zero page และ stack ของตัวเอง
- 6o6 ปัจจุบันใช้ self-modifying code จึงต้องมีสำเนา VM แยกในแต่ละ stage
- ใน stage 3 หน่วยความจำส่วนใหญ่ถูกใช้ไปกับสำเนา VM 3 ชุด และ VM ที่มี inline fetch macro มีขนาดมากกว่า 10KB ต่อชุด
- ในการรันแบบซ้อน การเรียก
CHROUTจะถูกส่งจาก stage 3 ผ่าน stage 2 และ stage 1 ก่อนถึง routine native ในท้ายสุด - การจบ payload ใช้คำสั่ง RTS เหมือนเป็น “kick”
- เพราะตอนเริ่มไม่มี return address ใน stack RTS จึงทำให้เกิด stack underflow
- เมื่อ harness รายงานสิ่งนี้เป็น exception kernel จะจัดการเป็นการจบปกติ
- แม้ใน stage ลึก ๆ ก็แพร่กลับขึ้นไปยัง kernel ชั้นบนด้วยวิธีเดียวกัน
- บน Apple II หลังรันแล้วสามารถรันซ้ำด้วย
CALL 2051ส่วนบน Commodore 64 ใช้RUN- เวอร์ชัน Apple II ใช้ถึง resident DOS area เหนือ
$9000ด้วย จึงแนะนำให้รีบูตหลังรัน
- เวอร์ชัน Apple II ใช้ถึง resident DOS area เหนือ
ตัวอย่างการสลับงาน
- ตัวอย่าง tasks เป็น kernel สำหรับ task-switching ขนาดเล็กที่สลับไปมาระหว่างงานอิสระสองงาน
- แต่ละงานมี zero page, stack และพื้นที่ที่อยู่โค้ดขนาดเล็กของตัวเอง และไม่รู้ถึงการมีอยู่ของกันและกันหรือ VM
- งานหนึ่งแสดงตัวอักษร อีกงานแสดงตัวเลข
- ตัวเลขแสดงเป็น reverse video เพื่อแยกให้เห็นชัด
- ทุกครั้งที่กดปุ่ม งานจะถูกสลับ
- ทั้งสองงานใช้ตำแหน่งเดียวกันใน zero page เพื่อเก็บสถานะ แต่เนื่องจาก zero page เป็นอิสระจากกัน แต่ละงานจึงรันต่อจากตำแหน่งล่าสุดของตัวเอง
- สิ่งที่ต้องมีสำหรับ context switching คือข้อมูลของงานปัจจุบัน และพื้นที่เก็บสถานะ A, X, Y, P, S, PC ของแต่ละงาน
- harness ดูงานที่ “on CPU” แล้วเลือกที่อยู่จริงของ zero page, stack และโค้ดที่รัน
- เมื่อสลับ kernel จะบันทึก/โหลดสถานะอื่น และทำเครื่องหมายงานอื่นเป็น “on processor”
ตัวอย่างหน่วยความจำภายนอก 64K ที่ใช้ geoRAM
- ตัวอย่าง vmgr สำหรับ Commodore 64 เท่านั้น และให้ address space 64K เป็นหน่วยความจำภายนอกโดยไม่ใช้ RAM ของระบบเอง
- geoRAM เป็นอุปกรณ์ paged RAM ที่ต่างจาก REU ทางการของ Commodore
- REU เน้น DMA และใช้ MOS 8726 REC เพื่ออ่าน/เขียน/สลับกับ main memory
- geoRAM map หน่วยความจำผ่าน window page ขนาด 256 ไบต์ในช่วง I/O
$de00 - control register อยู่ที่
$dffe,$dfff - clone ที่เข้ากันได้สมัยใหม่มีความจุได้สูงสุด 4MB
- VICE รองรับ geoRAM emulation
- ตัวอย่างใช้ ROM ของ 6502 processor module ที่เคยให้มาสำหรับ RC2014 Z80 kit computer
- ใน ROM มี monitor และ EhBASIC ของ Lee Davison
- ROM ที่ใช้เป็น pre-built ROM บน GitHub และใช้เวอร์ชัน 6551
- harness เพิกเฉยต่อ write ตั้งแต่
$c100ซึ่งเป็นพื้นที่ guest ROM- write ต่ำกว่า 16K ใช้ fast path
- สูงกว่านั้นปรับ geoRAM bank ด้วย mask และ shift
- cache geoRAM page ปัจจุบันเพื่อข้ามการตั้งค่าเมื่อเข้าถึง page เดียวกัน
- kernel และ main program ถูกรวมเป็นหนึ่งเดียวในตัวอย่างนี้
- ตรวจสอบการมีอยู่และการทำงานของ geoRAM
- คัดลอก ROM image ไปยัง geoRAM
- BRK ส่งกลับไป monitor
- illegal instruction, user-defined instruction trap ฯลฯ ถูกจัดการเหมือน BRK
- ดัก serial vector ของ RC2014 ROM เพื่อจำลอง terminal แบบง่าย
- แปลงระหว่าง PETSCII กับอักขระ terminal
- รักษา cursor ขนาดเล็ก
- ปรับรีจิสเตอร์และแฟล็กของ guest ให้ตรงกับผลลัพธ์
- สามารถ reset ระบบที่จำลองด้วย
CTRL-SHIFT-Commodoreและหน่วยความจำยังคงอยู่
- หากไม่ป้อนขนาดหน่วยความจำเองตอน EhBASIC cold start จะใช้เวลาประมาณ 1 นาทีในการหา 32768 bytes free บนชุด C64 และ geoRAM
- ROM ถูก build โดยมี hard cap ที่
$8000จึงถูกจำกัดไว้ที่ 32768 ไบต์ แม้จริง ๆ จะมีมากกว่านั้น - สามารถวางอย่างอื่นไว้ที่
$8000-$c0ffได้ - EhBASIC ไม่รับ command หรือ keyword ตัวพิมพ์เล็ก จึงต้องพิมพ์เป็นตัวพิมพ์ใหญ่ทั้งหมด
- ROM ถูก build โดยมี hard cap ที่
- ทำงานได้บน Commodore 128DCR จริงกับ 512K geoRAM cartridge ด้วย
- การคำนวณ floating-point ก็ทำงานปกติ
- bad instruction ถูกดักทันทีแบบควบคุมได้
- ยกเว้น window 256 ไบต์ ระบบบนหน้าจอไม่ได้รันอยู่ใน address space ของ 6502 เอง
- แม้ใช้ 512K geoRAM ก็สามารถวางระบบ 6502 ขนาด 64K แยกกันได้ 8 งาน
การปรับปรุงในอนาคตและการใช้งาน
- การปรับปรุงให้ 6o6 รันจาก ROM ได้เป็นไปได้ แต่ต้อง refactor และอาจช้าลง จึงเป็นเหมือนตัวเลือกมากกว่า
- มองว่า 65816 emulation อยู่นอกขอบเขต แต่การ emulate คำสั่ง CMOS บนระบบ NMOS อาจเป็นไปได้
- เนื่องจากใช้ ALU แม้ NMOS 6502 จะ emulate CMOS 65C02 แฟล็กก็จะถูกตั้งในแบบ NMOS
- ทิศทางกลับกันก็เช่นเดียวกัน
- addressing ปัจจุบันเขียนตามวิธีของ CPU NMOS
- วิธี inline memory macro มีโอกาสทำ peephole optimization
- อาจมี pass “post-preprocessor” ก่อน assembly จริง
- เพราะ toolchain จะซับซ้อนขึ้น จึงต้องยืนยันประโยชน์ทั่วไปก่อน
- หนึ่งในกรณีใช้งานที่ 6o6 ระบุไว้ชัดเจนคือการรันโค้ดที่ดาวน์โหลดมาโดยไม่ทำลายงานปัจจุบัน
- มีไอเดียใช้เป็นส่วนหนึ่งของ Gopher client เพื่อรันสิ่งที่ดาวน์โหลดมาแบบ dynamic
- หากออกแบบระบบ 6502 ใหม่เอง การ implement ฟังก์ชันที่ต้องการในฮาร์ดแวร์อาจเร็วกว่า
- ในกรณีที่ CPU NMOS มีระบบป้องกันน้อย หรืออยากลดซิลิคอนเพิ่มเติมให้น้อยที่สุด 6o6 เป็นทางเลือกที่ยืดหยุ่นและปรับตัวได้
การเผยแพร่และไลเซนส์
- The Incredible KIMplement มีให้ที่ หน้าเว็บ และ GitHub
- 6o6 มีให้ที่ GitHub และรวมตัวอย่างทั้งสี่ที่กล่าวถึงในบทความด้วย
- อัปเดต KIMplement 1.0 เน้นการจัดระเบียบสำหรับเผยแพร่และแก้บั๊กเล็ก ๆ
- KIMplement รวม Tiny PILOT ที่ Dave Hassler ให้มาด้วย
- Tiny PILOT เป็น implementation ที่ Nicholas Vrtis เขียนใน MICRO magazine ปี 1979 และเพิ่ม patch ของ Bob Applegate กับ Dave Hassler
- Dave Hassler ยัง port ELIZA จาก implementation Atari PILOT ปี 1980 ของ Carol Shaw และ Harry Stewart ด้วย
- KIMplement และ 6o6 เผยแพร่ภายใต้ Floodgap Free Software License
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
แม้จะเป็น 6502 ที่เรียบง่ายและมีข้อจำกัด แต่ก็น่าสนใจเสมอที่ได้เห็นสถาปัตยกรรมอายุเกือบ 50 ปีนี้ยังถูกผลักดันไปสู่ขีดจำกัดใหม่ ๆ อยู่เรื่อย ๆ
SoC บางตัวที่มุ่งตลาดราคาถูกมากและผลิตจำนวนมากก็ยังมีคอร์ 6502 อยู่
การเอาชนะคอร์ RISC-V ราคา 10 เซนต์ดูไม่ใช่เรื่องง่าย
ตอนแรกนึกถึง SoC แบบมัลติคอร์ที่มีคอร์ 6502 อยู่ 6502 คอร์แล้วขำ แต่ถ้าลองทำด้วย FPGA ก็น่าจะเป็นโปรเจกต์ที่สนุก
ผมใช้ 65816 ซึ่งเป็นรุ่นต่อยอด 16 บิตของ Apple 2 เสียบเป็นการ์ดขยายความเร็วปรับได้อยู่ แต่ส่วนใหญ่รันในโหมด 8 บิต เพราะมันทำงานได้ดีและโค้ดไลบรารีส่วนใหญ่ก็เป็น 8 บิต
ที่ความเร็วระดับนี้ ชิปถือว่าเร็ว โดยเฉพาะถ้านึกถึงโมเดลง่าย ๆ ที่ RAM กับ CPU ถูก clock แบบ 1:1 ในกรณีของผม ผมรันโค้ดผ่านบัส 1MHz ได้ ทำให้คำสั่งหลายไซเคิลส่วนใหญ่กลายเป็นเหมือนใช้บัสไซเคิลเดียวต่อการ fetch จากหน่วยความจำ
หรืออีกแบบคือบนการ์ดมี RAM 1MB และ RAM นี้ทำงานที่ความเร็ว CPU (0.15~16MHz) แบบนี้เร็วพอให้โปรแกรมขนาดใหญ่ที่เขียนด้วยภาษาระดับสูงรันได้ในความเร็วที่พอใช้ได้ แน่นอนว่าแอสเซมบลีเร็วแบบไม่ต้องพูดถึง
เป็นสภาพแวดล้อมที่สนุกมากสำหรับลองแฮ็กนู่นนี่
สามารถรัน GEOS ภายในหน้าต่าง GEOS ได้ไหม?
บทความนี้ลอยอยู่โดยไม่มีคอมเมนต์อยู่พักใหญ่ ผมเลยคิดว่าจะกลับมาอ่านทีหลัง แต่ประเด็นหลักคือวิธีที่ Commodore 64 อีมูเลตระบบที่ใช้ 6502 ซึ่งแตกต่างไปโดยสิ้นเชิง
“6o6” หรือ “6502-on-6502” คือ CPU NMOS 6502 แบบซอฟต์แวร์ virtualization เต็มรูปแบบที่รันบน CPU 6502 และควบคุมการรันโค้ด guest ได้อย่างสมบูรณ์ รวมถึง opcode ที่ไม่ได้บันทึกไว้ในเอกสารและ trap ของ jam opcode ทั้งยัง abstract การเข้าถึงหน่วยความจำทั้งหมด
จึงทำได้ทั้งการ remap address, ดักจับการอ่าน/เขียนที่ผิดกฎ และแม้แต่การรันด้วย virtual memory อย่างสมบูรณ์ ไม่เพียงผ่านการทดสอบฟังก์ชันทั้งหมดเท่านั้น แต่ถึงขั้น virtualize ตัวมันเองที่กำลัง virtualize ตัวมันเองได้ด้วย นับเป็นงานที่ยอดเยี่ยมไม่ใช่แค่จาก มุมมองของ 6502 แต่จากทุกมุมมองเลย
ยังนึกถึงวิดีโอที่บอกว่า Zilog Z80 มี protected mode ด้วย: https://www.youtube.com/watch?v=DLSUAVPKeYk
ทำให้นึกถึงตอนที่เริ่มเรียน แอสเซมบลี 6502 ด้วยตัวเองครั้งแรก มีหนังสือชื่อ “The Visual Computer” และมีอีมูเลเตอร์แถมมาในฟลอปปีดิสก์ เป็นประสบการณ์ที่เปิดโลกจริง ๆ
หา PDF ของหนังสือเจอแล้ว [1] แต่ไม่รู้ว่าซอฟต์แวร์ในฟลอปปียังหลงเหลืออยู่ที่ไหนหรือเปล่า
[1] https://files.commodore.software/reference-material/books/c6...
“Visual 6502” เป็นชื่อของซิมูเลเตอร์ 6502 ระดับเกตและทรานซิสเตอร์ยุคใหม่: http://visual6502.org/JSSim/index.html
https://archive.fo/2u3Y8