2013-06-29 4 views
0

현재 C++에서 여름 학기 프로젝트로 NES 에뮬레이터를 작성하여 다음 학년도 가을 학기에 준비하고 있습니다 (아직 코딩하지 않았습니다). 이미 Chip8 에뮬레이터를 작성 했으므로 다음 단계는 NES 에뮬레이터를 작성하고 작성하는 것이 될 것이라고 생각했습니다.가변 길이 opcode 및 CPU 타이밍 가져 오기

어쨌든, 나는 고생하고있다. 내 opcode 테이블에 대해 this 웹 사이트를 사용하고 있는데 도로 블록을 실행 중입니다. The Chip8에서 모든 opcode는 2 바이트 길이 였으므로 가져 오기가 쉬웠습니다. 그러나 NES는 CPU가 어떤 어드레싱 모드에 있는지에 따라 2 또는 3 바이트 opcode가있는 것으로 보입니다. 각 opcode에 대해 읽어야하는 바이트 수를 알아내는 쉬운 방법을 생각할 수 없습니다 (내 유일한 아이디어는 opcode의 첫 번째 바이트를 검사하여 얼마나 많은 바이트를 읽을지를 확인하는 정말 긴 if 문을 생성합니다.

사이클을 계산하는 방법도 알아내는 데 어려움이 있습니다. 모든 것이 동기화되도록 프로그래밍 언어에서 시계를 만드는 방법은 무엇입니까?

관련없는 쪽 노트에서 NES가 리틀 엔디안이므로 programCounter + 1을 읽고 올바른 opcode를 얻기 위해 programCounter를 읽어야합니까?

+0

조회 테이블이 여기에 적합 할 것 같습니다. –

답변

0

나는 약 25 년 이상 6502 년 동안 에뮬레이터를 썼다.

꽤 간단한 프로세서이므로 256 개의 항목이있는 함수 포인터 또는 스위치가 있습니다. [256 개 항목 모두에 유효한 opcode가 없기 때문에 스위치가 조금 짧아 질 수 있습니다. 200 개의 opcode가 실제로 사용됩니다].

이제 지침의 타이밍을 정확히 시뮬레이트하는 시뮬레이터를 작성하려면 더욱 재미있을 것입니다. 기본적으로 각 구성 요소의 작동 방식을 훨씬 더 시뮬레이트해야하며 시계를 사용하여 장치를 통해 "파문을 일으 킵니다". 이것은 상당히 많은 작업이므로 가능하면 타이밍을 무시하고 시스템의 속도를 에뮬레이터 속도에 의존하게 할 것입니다.

1

그러나 NES에는 CPU가있는 주소 지정 모드에 따라 2 또는 3 바이트 opcode가있는 것으로 보입니다. 각 바이트에 대해 읽어야하는 바이트 수를 계산하는 쉬운 방법은 생각할 수 없습니다. opcode.

opcode는 여전히 1 바이트입니다. 여분의 바이트는 명시 적 피연산자가있는 명령어의 피연산자를 지정합니다. 디코딩을 수행하려면 256 가지 경우로 switch -block을 만들 수 있습니다 (실제로 일부 opcode는 불법이므로 256 가지 경우가 아님). 그것은이 같은 것을 볼 수 있었다 : 당신은 매우 효율적 (이기는하지만 약간 비 대한) 코드를하게 될 겁니다 있도록

opcode = ReadByte(PC++); 
switch (opcode) { 
... 
case 0x4C:    // JMP abs 
    address = ReadByte(PC++); 
    address |= (uint16_t)ReadByte(PC) << 8; 
    PC = address; 
    cycles += 3; 
    break; 
... 
} 

컴파일러는 일반적으로 경우에 점프 테이블을 생성합니다.

또 다른 대안은 opcode 당 하나의 항목으로 배열을 만드는 것입니다. 이것은 단순히 opcode 당 하나의 함수를 가진 함수 포인터의 배열 일 수 있습니다. 또는 테이블에는 피연산자를 반입하기위한 하나의 함수에 대한 포인터, 실제 연산을 수행하기위한 포인터 및 명령에 필요한 사이클 수에 대한 정보가 포함될 수 있습니다. 이렇게하면 많은 코드를 공유 할 수 있습니다. 예 :

const Instruction INSTRUCTIONS[] = 
{ 
    ... 
    // 0x4C: JMP abs 
    {&jmp, &abs_operand, 3}, 
    ... 
}; 

는 또한주기를 계산하는 방법 계산에 문제가 있어요. 모든 것이 동기화되도록 프로그래밍 언어에서 시계를 만드는 방법은 무엇입니까?

CPU주기를 계산하는 것은 위의 코드 예제에서 보여 주었던 것처럼 카운터를 증가시키는 것입니다.

비디오를 CPU와 동기화하려면 가장 간단한 방법은 단일 스캔 라인의 활성 디스플레이 기간에 해당하는 사이클 양을 실행 한 다음 하나의 스캔 라인을 그린 다음 사이클 양만큼 CPU를 실행하는 것입니다 수평 블랭킹 기간에 해당하며 다시 시작합니다.

오디오 관련 작업을 시작하면 사용중인 오디오 API에 따라 작업을 동기화하는 방법이 달라질 수 있습니다. 예를 들어, 일부 API는 버퍼를 샘플로 채우고 생성 된 샘플 수를 반환하여 응답하는 콜백을 보낼 수 있습니다. 이 경우 이전 콜백 이후 에뮬레이션 된 CPU 사이클 수를 계산하고이를 기반으로 생성 할 샘플 수를 결정할 수 있습니다. NES는 리틀 엔디안이기 때문에 관련이없는 보조 노트에


는, 내가 올바른 op 코드를 얻을 수 programCounter를 읽을 다음 programCounter + 1을 읽고해야합니까?

opcode는 단일 바이트이고 6502의 명령어는 다른 CPU 아키텍처에서와 같은 단어로 묶이지 않기 때문에 엔디안 니스는 별 문제가되지 않습니다. 이 16 비트 피연산자와 관련이 있지만, 반면 PC와 대부분의 휴대 전화는 리틀 엔디안 CPU를 기반으로합니다.