처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.08.04 55

펌웨어 표준을 포기한 ZX 스펙트럼, 개발자들은 왜 ROM 내부를 직접 호출했나

Hacker News 원문 보기

8비트 시대의 개인용 컴퓨터를 기계어 수준에서 다루려면 대개 제조사가 정해 둔 진입점, 이른바 점프 테이블에 의존했다. 코모도어의 8비트 계열은 초기 PET부터 C128까지 KERNAL ROM 끝단에 하위 호환 점프 테이블을 유지했고, 그 덕에 파일 처리나 디스크 접근 같은 작업도 기종을 넘나들며 거의 동일하게 동작했다. MSX는 아예 하나의 기계가 아니라 여러 제조사가 준수하는 산업 표준이었고, 그 핵심에는 인터럽트 벡터 근처에 정의된 잘 짜인 점프 테이블이 있었다. IBM PC의 BIOS도, 다소 비자발적이긴 했지만 비슷한 역할을 했다.

싱클레어의 ZX 스펙트럼은 이런 흐름에서 벗어나 있었다. 일관되고 구조화된 펌웨어 인터페이스를 노출하는 데 최소한의 노력만 기울였기 때문에, 공식적으로 정의된 진입점은 몇 개에 불과했다. 결과적으로 개발자들은 원하는 동작을 얻기 위해 시스템 ROM 내부의 특정 주소를 직접 호출하는 방식에 기댔다. 이는 그 자체로 위험을 안고 있었다. 팀텍스 싱클레어 2068은 호환되지 않는 ROM을 탑재했고, 그 탓에 사실상 어떤 스펙트럼 소프트웨어도 돌아가지 않아 미국 시장 실패의 주요 원인으로 지목되곤 한다. 원저자는 같은 시기 MSX, 코모도어 16, 콜레코 아담도 함께 무너졌다는 점을 들어 이 인과에 다소 회의적이지만, 2068 실패 이후 나온 128 기종이 ROM의 관련 부분을 48K판과 세심하게 호환되도록 유지한 것을 보면 싱클레어가 교훈을 얻은 것으로 해석한다.

ROM은 흔들려도 시스템 변수는 안정적이다

기계어 코드를 작성할 때 시스템 ROM이 소유한 자원, 즉 인터럽트와 셰도우 레지스터, 그리고 IY 레지스터는 건드릴 수 없다. 다만 IY는 특별하다. 값을 바꿀 수는 없지만 읽는 것은 자유롭고, 실제로 자주 읽게 된다. IY는 항상 $5C3A를 가리키며, 여기에는 싱클레어가 '시스템 변수'라 부르는, 현재 시스템 상태에 관한 데이터 묶음의 기준 주소가 담겨 있다. 흥미롭게도 이 시스템 변수의 위치는 서로 호환되지 않는 변종들 사이에서도 ROM보다 훨씬 일관되게 유지된다. ROM 자체는 이 점에 무심해서 시스템 변수를 절대 주소로 직접 참조하기도 하고, 필요에 따라 IY를 인덱싱해 접근하기도 한다. 인덱스 연산은 비용이 더 들지만 유연하기 때문에, 기준 주소를 정확히 아는 상황에서도 두 방식을 오가는 코드가 흔하다.

텍스트 출력의 구조와 함정

스펙트럼은 화면을 22행의 상단 창과 2행의 하단 창으로 나눈다. 이는 BASIC의 산물이 아니라 기계어 수준에서도 존재하는 구분이다. 상단 창에 텍스트를 찍으려면 IY+2에 위치한 TVFLAG에 0을 써서 출력을 그쪽으로 향하게 한 뒤, 주소 $10의 ROM 루틴(RST $10)을 호출해 A 레지스터의 문자를 출력한다. 스펙트럼은 ASCII를 쓰므로 일반 텍스트는 익숙하게 다룰 수 있다. 원저자는 BASIC에 여유를 남기면서도 16K 기종에 들어갈 수 있도록 대개 $7000을 원점 주소로 삼는다.

그래픽 문자는 오히려 기계어에서 다루기가 더 쉽다. BASIC에서 그래픽 입력 모드로 전환해 키를 눌러야 했던 번거로움 없이, 확장 문자 코드를 그대로 출력 루틴에 넘기면 된다. 16종의 세미그래픽은 $80~$8F 범위에 곧바로 배치되어 있고, 비트 1·2·4·8이 각각 우상·좌상·우하·좌하 사분면에 대응하도록 정연하게 정렬돼 있다. 사용자 정의 그래픽(UDG)은 $90~$A4에 그래픽 A부터 U까지 놓이며(128은 두 자리가 적어 $A2까지), 그 위치는 IY+65의 16비트 포인터 UDG로 알 수 있다. 이는 BASIC 함수 USR "A"가 돌려주던 값과 동일해, 기계어에서도 같은 버퍼에 데이터를 채워 넣을 수 있다.

실무적으로 가장 주의할 지점은 속성 제어다. 문자 코드 $10~$17은 각각 INK, PAPER, FLASH, BRIGHT, INVERSE, OVER, AT, TAB에 대응하는 제어 코드로, AT만 두 바이트, 나머지는 한 바이트 인자를 뒤에 받는다. 동작 방식은 BASIC과 같지만 결정적 차이가 있다. BASIC에서는 PRINT 문이 끝나면 속성이 초기 상태로 돌아갔지만, 기계어에서는 그렇지 않다. 줄바꿈(이 시스템에서는 단순 캐리지 리턴 $0D)조차 상태를 되돌리지 않는다.

리셋을 원한다면 문서화되지 않은 ROM으로 내려가야 한다

이 초기화를 제대로 하려면 싱클레어가 애초에 들여다보길 기대하지 않았던 영역으로 내려가야 한다. 공개된 디스어셈블리가 TEMPS라 부르는 $0D4D의 내부 ROM 루틴은 '영구' 속성값을 '임시' 영역으로 복사해 속성 리셋을 수행한다. 인자는 없고 A와 HL을 훼손하지만 언제든 호출할 수 있다. 영구 속성은 주로 $0D6B의 CLS가 사용하는데, 이 루틴은 성가시게도 BORDERCR를 확인하면서도 실제 보더는 설정하지 않고, 출력 위치를 하단 창으로 되돌려 버린다. 그래서 원저자는 A에 속성 바이트를 받아 상·하단 창과 보더까지 한꺼번에 지우고 출력을 상단 창으로 복귀시키는 자체 헬퍼 함수를 쓴다. 이때 출력 채널을 '제대로' 열기 위해 $1601의 CHAN-OPEN을 호출해 장치 2(상단 창)를 선택한다.

이 모든 작업의 전제는 하나다. 정식 BIOS 인터페이스가 없으니, 주석이 달린 ROM 디스어셈블리를 곁에 두고 작업해야 한다는 것이다. 원저자는 skoolkid 사이트의 하이퍼링크판 디스어셈블리에 의존한다. 표준화된 펌웨어 계층을 포기한 대가로 개발자는 기종별 ROM 주소의 미세한 차이와 문서화되지 않은 내부 동작까지 스스로 감당해야 했다. 오늘날 이식성과 안정적 API를 당연시하는 실무자에게 스펙트럼의 사례는, 잘 정의된 인터페이스 하나가 플랫폼의 소프트웨어 생태계와 수명에 얼마나 큰 차이를 만드는지 보여주는 반면교사다.

이 뉴스가 유용했나요?

TTJ 코딩클래스 정규반

월급 외 수입,
코딩으로 만들 수 있습니다

17가지 수익 모델을 직접 실습하고, 1,300만원 상당의 자동화 도구와 소스코드를 받아가세요.

144+실전 강의
17개수익 모델
4.9수강생 평점
정규반 자세히 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.