요약
Commodore 64 메인보드를 핀 하나하나까지 시뮬레이션해서, 일부러 고장을 낸 뒤 증상을 볼 수 있습니다.
Redis를 만든 개발자 antirez(살바토레 산필리포)가 GitHub에 공개한 프로젝트입니다. Commodore 64(C64)가 어떻게 돌아가는지 깊이 배우고, 고장 난 실물 기판을 진단하는 데 쓰라고 만들었습니다.
왜 중요한가
- 실물을 태울 걱정 없이 칩 다리를 들거나 단락시켜 어떤 증상이 나는지 재현해 볼 수 있습니다.
- 고장 난 C64를 고칠 때 납땜을 풀어 떼어 낼 칩 후보를 줄이는 단서가 됩니다.
- 다만 README는 증상이 같아도 단서일 뿐 증거는 아니라고 경고합니다.
핵심 내용
- 메인보드(ASSY 250407)의 넷리스트(부품 사이 배선 목록)로 부품 216개와 넷 249개를 재현했습니다.
- 핀 들기, GND·+5V 단락, 칩 빼기, 저항·크리스털 끊기 같은 고장을 넣고 오실로스코프로 볼 수 있습니다.
- 6510 CPU, VIC-II, CIA, SID는 floooh가 만든 사이클 단위 칩 에뮬레이터를 쓰고, 각 칩은 자기 핀만 봅니다.
- 글루 로직, ROM, DRAM, 컬러 RAM, PLA(주소 해독 칩)는 핀 수준 모델로 따로 구현했습니다.
- 속도는 Apple M5 Max에서도 실물의 48%라서 게임용 에뮬레이터는 아니라고 밝혔습니다.
HN 반응
- 실시간 속도가 안 나오는 점을 두고 100배 최적화도 쉽다는 주장이 나왔지만, 칩 에뮬레이터 작성자는 2~3배가 한계라고 반박했습니다.
- 실물 기판을 고쳐 본 이용자들은 진단 카트리지 실행이나 타이밍 보정용 커패시터 설정 같은 기능을 제안했습니다.
정말 멋지고 훌륭한 작업입니다. C64 보드를 진단하고 고칠 때 요긴하게 쓰일 수 있겠습니다.
욕심을 내서 바라는 기능입니다.
시뮬레이션에서 진단 카트리지를 실행할 수 있게 해 주세요.
250407 보드에서 PLA 대체품을 쓰면 타이밍 경계 사례가 생기는 경우가 있었는데, /CASRAM 라인에 작은 캐패시터(약 100pF)를 달아 신호를 조금 늦추면 대개 해결됩니다. 캐패시터는 기존 82옴 저항의 왼쪽 다리와 'RAM6' 근처의 접지점 사이에 답니다. 일부 보드에는 이 캐패시터를 달 자리가 공식적으로 마련되어 있고, C204라는 번호가 붙어 있습니다. 순정 PLA 변종 중에는 실제 보드에 이미 캐패시터가 달려 있는 것도 있습니다. 이 캐패시터를 켜고 끄거나 용량을 바꿔 가며 결과를 볼 수 있으면 좋겠습니다.
정리한 글: https://linker3000.com/index.php/blog/poor64
약 10년 전에 Byuu(고인의 명복을 빕니다)가 BSNES/Higan이 왜 "느린"지 설명하는 글을 쓴 게 기억납니다. 하려는 일을 생각하면 결코 느린 게 아니라는 내용이었습니다. 칩을 정확하게 시뮬레이션할수록 동기화 같은 데 들어가는 오버헤드가 그만큼 커질 수밖에 없다는 것입니다.
그 글에서 이런 식의 Pong 회로 시뮬레이션을 언급했는데, 게임 플레이가 10FPS 정도 나왔다고 해서 깜짝 놀랐던 기억이 납니다. Pong보다 단순한 게임은 거의 없는데, 뛰어난 하드웨어에서도 가장 순수한 형태의 에뮬레이션을 원하면 여전히 플레이할 수 없는 수준이었으니까요.
대단하네요. 아쉽게도 성능 좋은 실제 하드웨어에서도 실시간으로는 돌아가지 않습니다. 코드를 최적화하면 실시간으로 돌릴 수 있지 않을까요?
README에 floooh의 구현이 언급되어 있습니다. 그쪽은 좀 더 단순화되어 있고 전체 넷리스트가 없어서, 작은 TTL 글루 칩들은 고려하지 않습니다. 그래도 실시간으로 동작합니다. 에뮬레이트된 IC들 사이의 공통 인터페이스는 64비트 정수이고, 시뮬레이션 단계마다 관심 있는 논리 레벨을 각 비트에 대응시킵니다.
https://github.com/floooh/chips/blob/master/README.md
https://github.com/floooh/chips/blob/9e88298ce56319953ac7a43213a1120359f7a3a6/chips/m6502.h#L85-L106
https://floooh.github.io/tiny8bit/player.html?id=c64&dbg=1 ("Hardware" 메뉴를 쓰면 Salvatore의 에뮬레이터와 비슷한 동작 상태 보기를 띄울 수 있습니다)
시뮬레이션이 충분히 정확해서, REU 같은 카트리지를 시뮬레이션하면 그 카트리지가 쓰는 메모리 전송 트릭까지 그대로 구현할 수 있나요?
머릿속 한구석에서 카트리지 하나를 계속 설계하고 있는데, 제 c64에서 마법의 연기(magic smoke)를 내보내기 전에 이게 도움이 된다면 정말 멋질 것 같습니다.
저도 바로 이걸 만들고 있었습니다. 실제 하드웨어를 써서, 설계 때문에 생기는 화면의 가로줄(jail bars)까지 시뮬레이션하려고 하고 있습니다.
LLM 덕분에 이런 개발에 접근하기가 정말 쉬워졌습니다.
흥미로운 건 이제 Arduino를 거쳐 실제 주변기기와 C64에 연결되는 부품을 만들 수 있게 됐다는 점입니다.
이것의 반대도 가능할지 궁금하네요. 어떤 결과가 주어지면 그걸 일으키는 고장을 찾아 대응시키는 식으로요.
정말 멋진데... 어느 시점에는 도체 내부의 간섭이나 유도, 무선 신호 송수신 같은 것까지 시뮬레이션해야 하지 않을까요?
네, 그렇다면 시뮬레이터를 돌리는 하드웨어도 특성을 파악해서 그쪽의 전도·유도·RF까지 고려해야 한다는 데 동의하십니까?
아니요. 시뮬레이션되는 컴퓨터는 현실 세계에 존재하지 않으니까요. 호스트의 물리적 특성은 호스트 컴퓨터가 오작동할 때만 시뮬레이션에 영향을 줍니다.
시뮬레이션이 느리게 돌수록 그건 덜 중요해진다고 봅니다. 시뮬레이션된 RF 간섭은 호스트 시스템보다 훨씬 느린 시간 척도로 움직일 테니까요. 호스트 시스템의 RF는 원래 시스템과 속도가 같아지고 하드웨어 주변기기와 연결하려 할 때가 되어서야 문제가 될 것 같습니다.
맞습니다! 하드웨어가 시뮬레이션을 50% 속도로 돌리는 상황에서 RF까지 시뮬레이션하려면 RF의 파장도 절반으로 줄여야 하는데, 그중 일부는 애매한 영역(mush zone)에 들어갈 수도 있겠네요.
그럴 땐 "마법/더 많은 마법" 스위치죠?
정말 멋지네요!
지난주에 아들과 같이 놀고 저도 덕질하려고 commodore 64 ultimate를 샀습니다. 회사에서 AI를 쓰면 쓸수록 직접 코딩하고 하드웨어를 온전히 내 것으로 이해하는 즐거움이 그리워집니다. 지난 주말에는 35년 만에 BASIC 프로그램을 짰는데, 아들 생일 선물로 만든 워들(Wordle)입니다. 이름을 입력하면 생일 케이크 PETSCII 아트가 나오게 했습니다. 하드웨어를 이렇게 물리적·전기적인 수준까지 파고들어 본 적은 없어서, 이걸로 놀아 보면 재미있겠습니다 :)
어디나 AI 쓰레기가 넘쳐 나고 이제 c64 커뮤니티도 예외가 아니지만, 이런 보석 같은 작품도 나오니 균형이 맞춰지리라 생각하고, 또 그러길 바랍니다.
그래서 FPGA가 여전히 쓸모 있다는 게 핵심입니다. 요즘 CPU도 겉보기에 단순한 데이터 처리 작업 중 일부는 실시간으로 감당하지 못합니다.
아니요, 이 구현이 그냥 형편없이 최적화돼 있을 뿐입니다(늘 그렇듯이). 소스를 슬쩍 보기만 해도 간단한 수정만으로 100배는 쉽게 빨라질 것 같습니다.
예를 들면 SIMD/SWAR가 어디에도 없고, 굳이 시뮬레이션할 필요가 전혀 없는 것까지 시뮬레이션하고, CPU는 명령어가 쏟아지는 괴물 같은 연쇄 구조로 에뮬레이트하면서 재정렬이나 투기적 실행 최적화는 전혀 없고(이런 건 성능만 올려 줄 뿐이고 에뮬레이트되는 프로그램은 차이를 전혀 알 수 없습니다. 보통 에뮬레이터라면 공짜로 5~20배는 나옵니다), 캐시 스래싱이 곳곳에 있고, 멀티스레딩도 전혀 없습니다(제가 본 바로는 그렇고, 자세히 확인하진 않았습니다).
Antirez는 Redis를 비롯해 여러 가지를 만든 사람이라 효율적인 코드를 쓸 줄 아는 게 분명합니다. 그래서 "쉽게" 100배가 나온다는 건 믿기 어렵네요. 다만 이번에는 Claude의 도움을 받은 것 같으니 손쉽게 고칠 만한 부분은 아마 있을 겁니다.
저는 에뮬레이터용 JIT을 만들고 있는데 끝이 보이지 않습니다. 실험은 많이 돌리고 있으니 그나마 뭔가 하고는 있는 셈이겠죠. LLM은 학습 데이터에 참고할 구현이 있지 않으면 에뮬레이터를 잘 만들지 못합니다.
그분은 Claude로 Apple 하드웨어용 가장 빠른 추론 엔진(Dwarfstar)도 만들었고, Redis의 아주 효율적인 Array 데이터 타입도 만들었습니다.
그렇게까지 말씀하셨으니 이제 같은 저수준 개념을 유지하면서 100배 빠른 개념 증명(proof-of-concept) 구현을 직접 보여 주셔야겠네요 ;)
바이브 코딩으로 만든 코드라 최적화가 정말 엉망이라면 2~3배는 가능할 것 같지만, 100배는 절대 불가능합니다.
제가 이해한 시뮬레이션 방식은 주소 디코딩과 메모리 접근 전부를 트랜지스터 수준으로 시뮬레이션하는 것입니다(이게 이 프로젝트의 핵심이죠). CPU, CIA, SID, VIC-II를 제외한 바깥에서 일어나는 모든 일이 여기에 해당합니다. 그러니까 메모리 접근 하나하나가 트랜지스터 넷리스트로 시뮬레이션되기 때문에 상당한 오버헤드가 붙습니다. visual6502.org가 CPU를 시뮬레이션하는 방식과 비슷한데, 이건 https://github.com/mist64/perfect6502 처럼 최적화된 C 시뮬레이션조차 1MHz 실제 6502보다 약 30배 느립니다.
PS: 간단히 대충 계산해 봅시다. Antirez의 시뮬레이터는 실제 C64보다 3배 느립니다. 100배 빨라지면 실제 C64보다 약 30배 빠르다는 뜻이고, 30MHz로 클럭을 올린 C64 정도입니다. 그러면 3GHz 호스트 CPU에서는 (30MHz) C64 시스템 클럭 한 사이클을 에뮬레이트하는 데 호스트 클럭 100사이클이 주어지는데, 이걸 복잡한 칩 5개(CPU + CIA 2개 + VIC-II + SID)가 나눠 써야 하니 칩당 에뮬레이트 사이클 하나에 호스트 클럭 약 20사이클입니다. 게다가 이건 주소 디코딩과 메모리 접근(Antirez의 시뮬레이터에서 가장 비싼 부분)은 포함하지 않은 수치입니다. 칩 에뮬레이터를 스레드로 옮겨도 별 도움이 안 됩니다. 에뮬레이트 클럭 사이클마다 최소 한 번은 동기화해야 하고(C64의 phi-1, phi-2 '반 사이클' 때문에 사이클당 두 번일 수도 있습니다), 그러면 스레드의 이점이 사라집니다.
행운을 빕니다 :) (예를 들어 픽셀 8개를 렌더링하고 활성 스프라이트가 최대 8개일 수 있는 VIC-II의 한 클럭 사이클 전체를 호스트 클럭 20사이클에 밀어 넣어야 하는데, 파이프라이닝이 완벽하고 SIMD를 쓰고 캐시 미스가 없다 해도 VIC-II가 사이클마다 해야 하는 일에 비하면 호스트 CPU 명령어 수가 많지 않습니다.)
(밝혀 두자면, 저는 이 실험에 쓰인 핀 기반 칩 에뮬레이터의 작성자입니다.)
그럼 당신이 만든 C++ 리눅스 커널은 얼마나 더 빠릅니까?
최적화할 여지가 있다는 소리로 들리네요. 한번 들여다볼까 합니다.