패미컴 연사 패드가 힘을 숨김


평소 구독하고 있는 유튜브 채널 중 4ST란 채널이 있다. 패미컴과 같은 일본의 고전 콘솔 관련 컨텐츠를 주로 올리는 유튜버로, 패미컴 게임 바이너리 틈새로 숨겨져있는 개발자의 메세지를 다루는 영상 등을 올린 적이 있다. 이 채널에서 얼마 전 패미컴 연사 패드들의 실제 연사 성능을 벤치마킹하는 영상을 올렸다.

슈팅게임이나 액션 아케이드 게임이 많이 나오던 80년대, 순정 패드로는 버튼을 쭉 누르기만 해도 탄환이 알아서 연속으로 잘 나가는 게임들도 있긴 했지만 기본적으로는 한 번 누를 때마다 한 번의 탄환이 나가는 사양의 게임들도 많았다. 이런 게임은 버튼을 연속으로 계속 눌러줄 수밖에 없었다. 이런 게임들을 손쉽게 플레이하기 위해 누르기만 해도 여러 번 누르는 것과 같은 효과를 내주는 연사 기능을 가진 패미컴 호환 패드들이 많이 등장했다.

벤치마킹 결과 당시 여러 게임 잡지에 게재된 광고를 통해 40연사가 가능하다고 광고해온 제품들이 실제로 테스트해보면 24연사밖에 안 되는 등의 결과가 나오기도 하는 등, 실망스러운 결과가 많이 나오게 된 것. 그런데 이 채널에서 최근 올라온 영상에서 작은 반전이 나타나게 된다. 실은 패미컴에서는 애초에 30 연사가 한계였고, 그 이상은 패미컴에서 처리할 수 없었던 것.

패미컴은 초당 60회의 시그널을 처리할 수 있어서, 1/60초마다 한 번의 시그널만을 처리할 수 있다고 한다. 따라서 아무리 연사 패드에서 높은 frequency로 시그널을 내보낸다 하더라도, 패미컴에서는 버튼을 누르는 것에 1/60초, 떼는 것에 1/60초로 한 번의 연사에 최소 2/60초가 필요해 30연사를 넘길 수가 없었던 것.

그래서 이전에 벤치마킹했을 때 수치가 낮게 나온 제품들로 패미컴이 아닌 다른 환경에서 다시 테스트해보니 정말 광고의 수치와 가까운 값을 내보냈다고.

좌측의 레이저 커맨더를 제외하면, 초당 35연사라 광고한 아스키 스틱 2 터보는 실제론 43.1연사, 초당 32연사라 광고한 아스키 스틱 터보 쥬니어는 36연사, 40연사라 광고한 미니 커맨더는 43.6연사를 기록했다
다른 패드들 역시 원래 30 연사보다 높았지만 패미컴이 미처 처리하지 못했던 경우들이 있었다
그 와중에 패미컴이 처리하든 말든 초당 250연사를 하고 있었던 제품도 있었고… 그 메이커는 HAL 연구소……

이렇다보니, 실제로 매 1/60초의 윈도우마다 받은 시그널 중 최초의 시그널만을 인식했을 때 30 < f < 60인 frequency f에 대해 패미컴을 거치고 난 후의 frequency는 어떻게 나올지 궁금해서 빠르게 발코딩을 해보았다.

import math

FIRST = "first"
LAST = "last"

def famicom(f, mode=FIRST):
    sgn = 1
    res = {}
    for i in range(math.floor(20 * f) + 1):
        sec = math.floor(30 * i / f)
        if sec not in res.keys():
            res[sec] = [sgn]
        else:
            res[sec].append(sgn)
        sgn *= -1

    cur = -1
    cnt = 0
    if mode == FIRST:
        ref_idx = 0
    elif mode == LAST:
        ref_idx = -1
    else:
        raise ValueError("invalid mode")

    for sec in range(600):
        if sec in res.keys() and res[sec][ref_idx] * cur == -1:
            cnt += 1
            cur *= -1
        
    return cnt / 20

그 결과 아래와 같이 실제 측정치와 대충 들어맞는 결과를 얻을 수 있었다. 각 윈도우마다 마지막 시그널을 받도록 해도 결과는 비슷했음.

이 함수를 여러 frequency에 대해 플롯해보면 어떻게 될지도 궁금해서 체크해보았다. 그랬더니…

triangle wave 환청이 들린다

선형 그래프가 되풀이해서 나타나는 형태의 그래프가 나왔다. 씽킹 타임.

  1. 일단 0<f<30이라면 결과가 f 그대로 나오는 것은 쉽게 예상할 수 있다. 한 윈도우 내에서 두 번의 시그널이 올 일이 없기 때문에 충분히 연사 패드가 의도한대로 행동한다는걸 알 수 있음.
  2. 30<f<60인 경우. 편의상 각 윈도우의 마지막 시그널을 기록한다고 생각한다. 그러면 n번째 윈도우, 즉 n/60초의 시간이 지난 시점에서는 그 사이에 연사 패드가 \lfloor \frac{nf}{30} \rfloor회의 시그널을 보냈을 것이므로 이를 2로 나눈 값이 0이면 누르는 신호, 1이면 떼는 신호에 해당된다. 가장 이상적인 경우는 매 윈도우마다 이 값이 0, 1, 0, 1, …로 되풀이되어 나올 때 30을 달성할 수 있지만, 같은 값이 연속으로 나올 때마다 이 값은 평균적으로 1/2만큼 줄어들게 된다. 즉, 60보다 작은 n에 대해 \lfloor \frac{nf}{30} \rfloor\lfloor \frac{(n+1)f}{30} \rfloor의 기우성이 같은 경우의 수를 c라 할 때 대략적으로 30-\frac{c}{2}가 되어야 한다. 그런데 그런 경우의 수는 30<f<60이란 점 때문에 \lfloor \frac{nf}{30} \rfloor + 2 = \lfloor \frac{(n+1)f}{30} \rfloor인 경우의 수가 되는데, 이는 곧 \lfloor \frac{nf}{30} \rfloor을 세어나갈 때 어떤 정수를 스킵하는 경우의 수가 된다. 즉 최종적으로 \lfloor \frac{60f}{30} \rfloor = \lfloor 2f \rfloor까지의 양의 정수 중에서 몇 개를 스킵했냐는 것인데, 이 중에서 60개를 선택했기 때문에 스킵한 정수의 갯수는 \lfloor 2f \rfloor - 60이 된다. 즉 c=\lfloor 2f \rfloor - 60이므로 이를 대입하면 그 값이 대략 60-f가 된다.
  3. f+60f에 대해 결과가 같은 것도 위와 같은 이유로 설명이 된다. \lfloor \frac{n(f+60)}{30} \rfloor = 2 + \lfloor \frac{nf}{30} \rfloor이라 그 기우성이 같아지기 때문에.

물론 여기에는 측정기기나 연사 패드 자체의 공학적 한계에서 비롯한 측정 오차도 있고, 패미컴의 처리가 완벽한 60fps란 가정도 있기도 하는 등의 문제 때문에 실제 값과는 차이가 있을 수 있다. 이는 f가 커지면 더더욱 그렇게 되는데, 실제로 250.4연사의 케이스도 이 모델 상에서는 결과가 10.4가 나와야 하지만 실제 측정치는 20 언저리라서..

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중