에뮬레이터는 어떻게 작동합니까? NES / SNES 또는 C64 에뮬레이터를 보면 놀랍습니다.
특정 조립 지침을 해석하여 해당 기계의 프로세서를 에뮬레이션해야합니까? 다른 것은 무엇입니까? 그것들은 일반적으로 어떻게 설계됩니까?
에뮬레이터 (특히 게임 시스템) 작성에 관심이있는 사람에게 조언을 줄 수 있습니까?
에뮬레이터는 어떻게 작동합니까? NES / SNES 또는 C64 에뮬레이터를 보면 놀랍습니다.
특정 조립 지침을 해석하여 해당 기계의 프로세서를 에뮬레이션해야합니까? 다른 것은 무엇입니까? 그것들은 일반적으로 어떻게 설계됩니까?
에뮬레이터 (특히 게임 시스템) 작성에 관심이있는 사람에게 조언을 줄 수 있습니까?
답변:
에뮬레이션은 다양한 영역입니다. 기본 아이디어와 기능적 구성 요소는 다음과 같습니다. 나는 그것을 조각으로 나눈 다음 편집을 통해 세부 사항을 채울 것입니다. 제가 설명하고자하는 많은 것들이 프로세서의 내부 작동에 대한 지식을 요구할 것입니다. 조립 지식이 필요합니다. 특정 사항에 대해 너무 모호한 경우이 답변을 계속 개선 할 수 있도록 질문하십시오.
에뮬레이션은 프로세서와 개별 구성 요소의 동작을 처리하여 작동합니다. 시스템의 각 개별 부분을 구축 한 다음 하드웨어에서 와이어와 매우 유사한 방식으로 연결합니다.
프로세서 에뮬레이션을 처리하는 세 가지 방법이 있습니다.
이러한 모든 경로를 통해 동일한 전체 목표를 달성 할 수 있습니다. 코드를 실행하여 프로세서 상태를 수정하고 '하드웨어'와 상호 작용합니다. 프로세서 상태는 주어진 프로세서 대상에 대한 프로세서 레지스터, 인터럽트 핸들러 등의 집합체입니다. 6502의 경우, 레지스터를 나타내는 8 비트 정수의 번호를 가지고 것 : A
, X
, Y
, P
, 그리고 S
; 16 비트 PC
레지스터도 있습니다.
해석하면 IP
(명령 포인터- PC
프로그램 카운터 라고도 함) 에서 시작하여 메모리에서 명령을 읽습니다. 코드는이 명령어를 구문 분석하고이 정보를 사용하여 프로세서에서 지정한대로 프로세서 상태를 변경합니다. 해석의 핵심 문제는 매우 느리다는 것입니다. 주어진 명령어를 처리 할 때마다이를 해독하고 필요한 작업을 수행해야합니다.
동적 재 컴파일을 사용하면 해석과 매우 유사한 코드를 반복하지만 opcode를 실행하는 대신 작업 목록을 작성합니다. 분기 명령어에 도달하면이 작업 목록을 호스트 플랫폼의 기계어 코드로 컴파일 한 다음이 컴파일 된 코드를 캐시하고 실행합니다. 그런 다음 지정된 명령어 그룹을 다시 누르면 캐시에서 코드 만 실행하면됩니다. (BTW, 대부분의 사람들은 실제로 명령 목록을 작성하지 않고 즉시 기계 코드로 컴파일합니다. 이는 최적화하기가 더 어렵지만 충분한 사람들이 관심이 없다면이 답변의 범위를 벗어납니다)
정적 재 컴파일을 사용하면 동적 재 컴파일과 동일한 작업을 수행하지만 분기를 따릅니다. 프로그램의 모든 코드를 나타내는 코드 청크를 작성하면 더 이상 간섭없이 실행할 수 있습니다. 다음과 같은 문제가 아니라면 훌륭한 메커니즘이 될 것입니다.
99 %의 경우 정적 재 컴파일이 완전히 불가능 해집니다. 자세한 내용을 보려면 Michael Steil이 정적 재 컴파일에 대한 훌륭한 연구를 수행했습니다.
프로세서 에뮬레이션의 다른 측면은 하드웨어와 상호 작용하는 방식입니다. 이것은 실제로 두 가지 측면이 있습니다.
특정 플랫폼, 특히 NES, SNES 등과 같은 구형 콘솔에서는 에뮬레이터가 완전히 호환되도록 엄격한 타이밍이 필요합니다. NES를 사용하면 CPU가 정확한 순간에 메모리에 픽셀을 넣어야하는 PPU (픽셀 처리 장치)가 있습니다. 해석을 사용하면 사이클을 쉽게 계산하고 적절한 타이밍을 에뮬레이션 할 수 있습니다. 동적 / 정적 재 컴파일을 사용하면 / lot /이 더 복잡합니다.
인터럽트는 CPU가 하드웨어와 통신하는 기본 메커니즘입니다. 일반적으로 하드웨어 구성 요소는 CPU에 어떤 인터럽트가 필요한지 알려줍니다. 코드가 주어진 인터럽트를 던지면 인터럽트 처리기 테이블을보고 적절한 콜백을 호출합니다.
주어진 하드웨어 장치를 에뮬레이트하는 데는 두 가지 측면이 있습니다.
하드 드라이브의 경우를 생각해보십시오. 이 기능은 백업 스토리지, 읽기 / 쓰기 / 포맷 루틴 등을 생성하여 에뮬레이션됩니다.이 부분은 일반적으로 매우 간단합니다.
장치의 실제 인터페이스는 조금 더 복잡합니다. 이것은 일반적으로 메모리 매핑 된 레지스터 (예 : 장치가 신호 변경을 감시하는 메모리의 일부)와 인터럽트의 일부 조합입니다. 하드 드라이브의 경우 읽기 명령, 쓰기 등을 배치 한 다음이 데이터를 다시 읽는 메모리 매핑 영역이있을 수 있습니다.
더 자세히 설명 하겠지만, 백만 가지 방법을 사용할 수 있습니다. 여기에 구체적인 질문이 있으시면 언제든지 문의하십시오. 정보를 추가하겠습니다.
내가 소개 여기 꽤 좋은를 제공했다고 생각하지만, 거기 톤 추가 영역은. 궁금한 점이 있으면 기꺼이 도와 드리겠습니다. 나는 단지 엄청난 복잡성 때문에 이것의 대부분에서 매우 모호했습니다.
이 답변이 제출 된 지 1 년이 지났으며 모든 관심을 끌면서 몇 가지 사항을 업데이트 할 시간이라고 생각했습니다.
아마도 에뮬레이션에서 가장 흥미로운 것은 앞에서 언급 한 Michael Steil이 시작한 libcpu 일 것입니다 . 재 컴파일 (정적 및 동적)에 LLVM을 사용하는 많은 수의 CPU 코어를 지원하기위한 라이브러리입니다. 큰 잠재력을 가지고 있으며 에뮬레이션에 큰 도움이 될 것이라고 생각합니다.
emu-docs도주의를 끌었습니다. 여기에는 훌륭한 시스템 문서 저장소가 들어있어 에뮬레이션에 매우 유용합니다. 나는 거기에서 많은 시간을 보냈지 않지만 많은 훌륭한 자원을 가지고있는 것처럼 보입니다.
이 게시물이 도움이 되었기 때문에 기쁘고 내년 초 / 내년 초까지 내 엉덩이를 벗고 주제에 관한 책을 마무리하기를 바라고 있습니다.
Victor Moya del Barrio라는 사람이이 주제에 대한 논문을 썼습니다. 152 페이지에 대한 많은 좋은 정보. 여기 에서 PDF를 다운로드 할 수 있습니다 .
scribd 에 등록하지 않으려면 Google에서 PDF 제목 "에뮬레이션 프로그래밍 기술 연구"를 검색하십시오 . PDF에 대한 몇 가지 소스가 있습니다.
에뮬레이션은 어려운 것처럼 보이지만 실제로는 시뮬레이션보다 훨씬 쉽습니다.
모든 프로세서에는 일반적으로 상태, 상호 작용 등을 설명하는 잘 작성된 사양이 있습니다.
성능에 전혀 신경 쓰지 않았다면 매우 우아한 객체 지향 프로그램을 사용하여 대부분의 구형 프로세서를 쉽게 에뮬레이션 할 수 있습니다. 예를 들어, X86 프로세서는 레지스터 상태를 유지하기위한 것 (easy), 메모리 상태를 유지하기위한 것 (easy) 및 들어오는 각 명령을 받아 머신의 현재 상태에 적용하는 것이 필요합니다. 정말로 정확성을 원한다면 메모리 변환, 캐싱 등을 에뮬레이트하지만 가능합니다.
실제로 많은 마이크로 칩 및 CPU 제조업체는 칩의 에뮬레이터와 칩 자체에 대해 프로그램을 테스트하여 칩 사양이나 하드웨어에서 실제 칩 구현에 문제가 있는지 확인할 수 있습니다. 예를 들어, 교착 상태가 발생하는 칩 사양을 작성할 수 있으며, 하드웨어에서 최종 기한이 발생하면 해당 칩이 사양에서 재현 될 수 있는지 확인하는 것이 중요합니다. 이는 칩 구현의 문제보다 더 큰 문제를 나타 내기 때문입니다.
물론 비디오 게임용 에뮬레이터는 일반적으로 성능에 신경을 쓰므로 순진한 구현을 사용하지 않으며 호스트 시스템의 OS와 인터페이스하는 코드 (예 : 드로잉 및 사운드 사용)도 포함합니다.
오래된 비디오 게임 (NES / SNES 등)의 성능이 매우 느리다는 점을 고려하면 현대 시스템에서는 에뮬레이션이 매우 쉽습니다. 사실,이 시스템이 대중화되었을 때 모든 카트리지에 무료로 액세스 할 수있게되면 꿈이 실현 될 것이라는 점을 고려하여 모든 SNES 게임 또는 Atari 2600 게임을 다운로드 할 수 있다는 것이 훨씬 더 놀라운 일입니다.
이 질문이 약간 오래되었다는 것을 알고 있지만 토론에 무언가를 추가하고 싶습니다. 여기에있는 대부분의 대답은 에뮬레이터를 중심으로 그들이 에뮬레이션하는 시스템의 기계 명령어를 해석합니다.
그러나 "UltraHLE"( WIKIpedia article ) 이라는 매우 잘 알려진 예외가 있습니다. 가장 유명한 에뮬레이터 중 하나 인 UltraHLE는 상업적인 Nintendo 64 게임 (가정용 컴퓨터에서 적절한 성능을 발휘 함)이 불가능하다고 여겨지는시기에 에뮬레이션했습니다. 사실, Nintendo는 UltraHLE이 만들어 졌을 때 여전히 Nintendo 64의 새로운 타이틀을 제작하고있었습니다!
처음으로, 나는 인쇄 잡지에서 에뮬레이터에 관한 기사를 보았습니다. 이전에는 웹에서 논의 된 것을 보았습니다.
UltraHLE의 개념은 머신 레벨 호출 대신 C 라이브러리 호출을 에뮬레이션하여 불가능을 가능하게하는 것이 었습니다.
살펴볼 가치가있는 것은 Imran Nazar의 JavaScript 로 게임 보이 에뮬레이터 를 작성하려는 시도 입니다.
80 년대 BBC 마이크로 컴퓨터 (Google에 VBeeb 유형)의 에뮬레이터를 직접 만들었을 때 알아야 할 것이 많습니다.
실제로 말해서, 당신은 일반적으로 속도와 충실도 에뮬레이션을 작성하려고합니다. 대상 시스템의 소프트웨어가 소스 시스템의 원래 하드웨어보다 느리게 실행될 수 있기 때문입니다. 프로그래밍 언어, 컴파일러, 대상 시스템 등의 선택을 제한 할 수 있습니다.
또한, 예를 들어 마이크로 프로세서에서 트랜지스터의 전압 상태를 에뮬레이션 할 필요는 없지만 에뮬레이션 할 준비가 된 것을 포경해야합니다. 마이크로 프로세서의 레지스터 세트 상태를 에뮬레이션합니다.
일반적으로 에뮬레이션의 세부 수준이 작을수록 원래 시스템에 대한 충실도가 높아집니다.
마지막으로 구형 시스템에 대한 정보가 불완전하거나 존재하지 않을 수 있습니다. 따라서 원래 장비를 확보하는 것이 필수적이거나 다른 사람이 작성한 다른 좋은 에뮬레이터를 최소한으로 압류합니다!
예, 전체 바이너리 머신 코드 엉망을 "손으로"해석해야합니다. 뿐만 아니라 대부분의 경우 대상 컴퓨터에 해당하지 않는 이국적인 하드웨어를 시뮬레이션해야합니다.
간단한 접근 방식은 지침을 하나씩 해석하는 것입니다. 그것은 잘 작동하지만 느립니다. 더 빠른 접근 방법은 소스 컴퓨터 코드를 대상 컴퓨터 코드로 변환하는 재 컴파일입니다. 대부분의 명령어가 일대일로 매핑되지 않으므로 더 복잡합니다. 대신 추가 코드가 포함 된 정교한 해결 방법을 만들어야합니다. 그러나 결국 훨씬 빠릅니다. 대부분의 현대 에뮬레이터가이를 수행합니다.
... --- ...
-이 세 가지 모스 부호는 세 글자 S, O, S를 나타냅니다." 문자 "S"를 나타내는 코드 이기 때문 ...
입니다 . 아니?
에뮬레이터를 개발할 때 시스템이 작업중인 프로세서 어셈블리 (Z80, 8080, PS CPU 등)를 해석합니다.
또한 시스템에있는 모든 주변 장치 (비디오 출력, 컨트롤러)를 에뮬레이션해야합니다.
당신은 좋은 오래된 게임 보이 (Z80 프로세서를 사용하고, 난 착각하지 않습니다) 나 C64를위한 단순한 시스템을위한 에뮬레이터를 작성해야합니다 .
에뮬레이터는 시뮬레이션해야 할 많은 해킹 (비정상적인 효과와 같은), 타이밍 문제 등이 있기 때문에 만들기가 매우 어렵습니다.
이에 대한 예는 http://queue.acm.org/detail.cfm?id=1755886을 참조하십시오 .
또한 1MHz CPU를 에뮬레이션하기 위해 다중 GHz CPU가 필요한 이유를 보여줍니다.
또한 Darek Mihocka의 Emulators.com 에서 JIT의 명령어 수준 최적화에 대한 유용한 조언과 효율적인 에뮬레이터 구축에 대한 다른 많은 장점을 확인하십시오 .
게임 콘솔을 에뮬레이트하는 것만 큼 멋진 일은 한 번도하지 않았지만 Andrew Tanenbaums Structured Computer Organization에 설명 된 기계에 대한 에뮬레이터를 작성하는 과정을 한 번 수강했습니다 . 그것은 나에게 많은 아하 순간을 준 재미이었다. 실제 에뮬레이터를 작성하기 전에 그 책을 집어 들기를 원할 수도 있습니다.
실제 시스템이나 자신의 것을 모방하는 것에 대한 조언? 에뮬레이터가 ENTIRE 하드웨어를 에뮬레이트하여 작동한다고 말할 수 있습니다. 어쩌면 회로로 내려 가지 않을 수도 있습니다 (HW처럼 비트를 이동하는 것처럼 바이트를 이동하는 것이 최종 결과이므로 바이트를 복사하는 것이 좋습니다). 에뮬레이터는 시뮬레이션해야 할 많은 해킹 (비정상적인 효과와 같은), 타이밍 문제 등이 있기 때문에 만들기가 매우 어렵습니다. 하나의 (입력) 부분이 잘못되면 전체 시스템이 다운되거나 버그 / 글리치가 발생할 수 있습니다.
공유 소스 장치 에뮬레이터 포켓 PC / 스마트 폰 에뮬레이터에 빌드 할 소스 코드를 포함 (Windows의 비주얼 스튜디오, 실행 필요). 바이너리 릴리스의 V1 및 V2에서 작업했습니다.
많은 에뮬레이션 문제를 해결합니다.-게스트 가상에서 게스트 물리적으로 호스트 가상으로의 효율적인 주소 변환-게스트 코드의 JIT 컴파일-네트워크 어댑터, 터치 스크린 및 오디오와 같은 주변 장치 시뮬레이션-호스트 키보드 및 마우스 용 UI 통합-저장 / 저전력 모드에서 이력서 시뮬레이션을위한 상태 복원
@Cody Brocious가 제공하는 답변을 추가하려면
가상 시스템에 새 시스템 (CPU, I / O 등)을 에뮬레이션하는 가상화 환경에서 다음과 같은 범주의 에뮬레이터를 볼 수 있습니다.
해석 : bochs는 인터프리터의 한 예입니다. x86 PC 에뮬레이터입니다. 게스트 시스템에서 각 명령을 가져 와서 의도 한 효과를 내기 위해 다른 명령 세트 (호스트 ISA의 다른 명령)로 변환합니다. 모든 명령이 동일한주기를 거치도록 아무것도 캐시하지 않습니다.
동적 에뮬레이터 : Qemu는 동적 에뮬레이터입니다. 게스트 명령의 즉석 변환도 결과를 캐시합니다. 가장 좋은 점은 호스트 시스템에서 가능한 많은 명령을 직접 실행하여 에뮬레이션 속도를 높이는 것입니다. 또한 Cody에서 언급했듯이 코드를 블록으로 분할합니다 (1 개의 단일 실행 흐름).
정적 에뮬레이터 : 지금까지 가상화에 도움이되는 정적 에뮬레이터가 없다는 것을 알고 있습니다.
에뮬레이션을 시작하는 방법
1. 저수준 프로그래밍을 기반으로 한 책을 얻으십시오. Nintendo ... 게임 소년의 "가장"운영 체제에 필요한 책이 필요합니다.
2. 에뮬레이션에 대한 서적과 OS 개발에 대한 책을 얻으십시오. (당신은 OS를 만들지 않을 것이지만 가장 가깝습니다.
3. 일부 오픈 소스 에뮬레이터, 특히 에뮬레이터를 만들려는 시스템을 살펴보십시오.
4. 복잡한 코드의 스 니펫을 IDE / 컴파일러에 복사합니다. 이렇게하면 긴 코드를 작성하지 않아도됩니다. 이것은 OS 개발을 위해하는 일이며 Linux 지구를 사용합니다.
JavaScript로 Chip-8 시스템을 에뮬레이트하는 기사를 작성했습니다 .
시스템이 그리 복잡하지 않기 때문에 시작하기에 좋은 곳이지만 여전히 opcode, 스택, 레지스터 등이 어떻게 작동하는지 배웁니다.
NES를위한 더 긴 가이드를 곧 작성할 것입니다.