원문 캡처 · github.com
원문 캡처 · github.com

Classetta는 클래식 Mac OS(System 1~7)용 68k 애플리케이션을 에뮬레이션해 윈도우·macOS·리눅스에서 실행하는 런타임이다. PyQt6 프런트엔드가 앱의 창을 데스크톱에 표시하며, Color QuickDraw와 앱 간 Apple 이벤트는 아직 지원하지 않는다.

Classetta는 애플리케이션의 68k 코드를 에뮬레이션된 CPU에서 실행하고 A-line 트랩을 가로채 파이썬으로 작성한 Toolbox 루틴을 호출한다. QuickDraw가 그렸을 화면은 Qt 위젯으로 표시한다. 코드를 역어셈블하거나 다른 코드로 변환하는 단계 없이, 앱은 맥에서처럼 실행되고 Classetta의 Toolbox를 호출한다. 각 앱의 창은 호스트 데스크톱의 다른 프로그램 창과 나란히 놓인다.

프로젝트는 TeachText, Microsoft Excel, MacWrite, MacPaint, 1984년의 Word 1.0, Creepy Castle과 Glider+ 같은 게임을 실제 68k 앱 실행 사례로 소개한다. 시험해 본 앱 목록은 실행해 본 앱에서 확인할 수 있다.

실행하려면 먼저 설치한다.

text
python -m pip install -e .

이후 MacBinary, BinHex, AppleDouble 등의 파일을 지정해 실행하거나 디스크 이미지에서 앱을 고를 수 있다.

text
python -m macrt App.bin # MacBinary, BinHex, AppleDouble…
python -m macrt "System 6.0.8.dsk" --app TeachText
python -m macrt One.bin Two.hqx # several, side by side

설치와 실행, 앱의 리소스 포크를 Classetta가 읽을 수 있는 형태로 준비하는 방법, 실행이 멈췄을 때 메시지를 읽는 방법은 실행 안내에 정리돼 있다.

실행과 표시 설정

파일을 지정하지 않으면 macrt는 같은 Toolbox API를 사용하는 파이썬 게스트 앱 두 개를 실행한다. Sampler는 Windows 메뉴에서 각종 창을 열고, 시계인 Ticker는 앱 전환을 보여준다. Ctrl 키는 ⌘(Command), Alt 키는 Option 키 대신 사용한다.

text
pip install -e '.[dev]'
python -m macrt # host windows, one global menu bar
python -m macrt --menubar window # each window carries its app's menu bar
python -m macrt --host screen # everything inside one 1-bit Mac screen
python -m macrt --host screen --size 512x342 --classic-menus

표시 설정은 메뉴 막대의 위치와 창을 표시할 호스트 방식을 각각 고른다. menu_bar="global"은 화면 위에 하나의 메뉴 막대를 두고, menu_bar="window"는 각 앱 창의 제목 표시줄 아래에 메뉴를 둔다. host="desktop"에서는 맥 창들이 호스트 창으로 표시되고, host="screen"에서는 하나의 호스트 창 안에 맥 데스크톱 화면을 표시한다. 전자는 전역 메뉴 막대 또는 창별 메뉴 막대를, 후자는 클래식 메뉴 막대 또는 창별 메뉴 막대를 조합할 수 있다.

전역 메뉴 막대는 앞에 있는 앱의 메뉴를 표시한다. 다른 앱 창을 클릭하면 MultiFinder 방식으로 해당 앱이 앞으로 오고, suspend·resume 이벤트를 받으며 메뉴 막대도 바뀐다. 이때 클릭 자체는 mouseDown 이벤트로 전달하지 않는다. 앱이 둘 이상 실행 중이면 System 7의 Application 메뉴가 오른쪽에 나타나며, Hide App, Hide Others, Show All과 앱 목록을 제공한다. application_menu="always"|"never"로 표시 여부를 덮어쓸 수 있다.

창별 메뉴 막대는 각 문서 창의 제목 표시줄 아래에 표시된다. 대화상자가 앞에 있어도 앱의 다른 창을 통해 메뉴를 사용할 수 있다. 문서 창이 없는 앱에는 앱 이름을 제목으로 한 작은 메뉴 전용 창이 생겨 메뉴가 사라지지 않는다. 그 밖에 화면 크기를 정하는 screen_size, 제목을 빠르게 클릭하면 메뉴가 열린 채로 남는(기본값) sticky_menus, System 7의 컬러 애플 로고를 쓰는 theme.RAINBOW_APPLE 설정이 있다. sticky_menus를 끄면 누르고 끌어 놓는 방식으로 메뉴를 조작한다.

실제 앱은 다음과 같이 실행할 수 있다.

text
python -m macrt App.bin # MacBinary
python -m macrt App.hqx --list # BinHex; list the resources and exit
python -m macrt Disk.dsk # the only application on a disk image
python -m macrt Disk.dsk --app "Disk:Games:App" # or pick one by name or Mac path
python -m macrt App.bin --view # its menus and windows, without its code
python -m macrt App.bin --trace-traps # log every trap it calls
python -m macrt App.bin --system System.bin # with a real System file
unar App.sit.hqx # an archive: unpack it first, then
python -m macrt "App Folder/App" # run what came out (App or App.rsrc)

실행 안내에는 Mac 파일 형식, 아카이브와 디스크 이미지, 명령행 옵션, 작동 범위, 시스템 오류 ID가 포함돼 있다. 68k 코드가 없는 PowerPC 전용 앱이나 --view 옵션을 사용한 앱은 코드 대신 viewer.py의 ResourceViewer로 연다. ResourceViewer는 앱의 시작 동작을 대신해 같은 Toolbox 호출을 사용한다. 메뉴 막대는 GetNewMBar 또는 GetMenu로, 첫 WIND 창은 GetNewWindow로 불러온다. Resources 메뉴에서 다른 창과 대화상자, 경고창을 열 수 있다.

호스트 플랫폼에는 제약이 있다. Wayland는 앱이 창 위치를 직접 정하도록 허용하지 않는다. host="desktop"에서 창을 끌 때는 컴포지터의 startSystemMove를 사용하지만 위치를 정확히 지정할 수는 없다. 정확한 배치가 필요하면 QT_QPA_PLATFORM=xcb(XWayland) 또는 host="screen"을 사용한다. macOS에는 화면 위에 전역 메뉴 막대가 이미 있으므로 --menubar window나 --host screen을 사용한다. host="desktop"의 창 모양과 1픽셀 그림자는 컴포지터 없이도 동작하는 setMask에 의존한다.

에뮬레이션과 Toolbox

Classetta는 unicorn의 68020을 24비트 모드로 구동한다. 기본 설정에서는 앱에 Mac Plus의 68000으로 보이며, --model ii를 지정하면 Mac II의 68020과 68881로 보인다. 에뮬레이터는 해당 프로세서의 예외 프레임을 쌓고 추적 모드에서는 명령어를 하나씩 실행한다. Segment Loader는 앱의 A5 세계와 점프 테이블을 만들고, 앱이 GetResource에 설치한 패치를 통해 세그먼트를 읽는다. Multiversal 인터페이스에서 생성한 트랩 디스패처는 파스칼 스택과 레지스터를 사용하는 호출을 파이썬으로 전달한다.

Memory, Resource, Window, Menu, Event, Control, Dialog, List, Scrap, File, Process Manager와 TextEdit, Standard File 등 700개가 넘는 루틴을 지원한다. Apple Event Manager는 Open Application 이벤트와 앱이 자기 자신에게 보내는 이벤트를 처리한다. QuickDraw는 흑백 모드에서 실제 영역(region), 그림, 아이콘을 지원한다. SANE의 산술·변환·기본 함수, International Utilities의 날짜·시간·문자열 정렬, 로마자 텍스트용 Script Manager도 포함한다. Printing Manager는 PDF로 인쇄한다. Sound Driver의 세 신시사이저, Sound Manager의 사운드 채널과 샘플 음원, Mac Plus의 사운드 버퍼를 지원하며 호스트 컴퓨터 스피커로 소리를 재생한다. 여러 앱을 동시에 실행하는 전환은 MultiFinder 방식으로 처리한다.

아직 지원하지 않는 기능은 Color QuickDraw와 앱 사이의 Apple 이벤트다. 이 기능이 필요한 앱은 지원되는 범위까지 실행된다.

구조상 Machine(m68k/runtime.py) 하나가 CPU와 트랩 스텁을 공유하며, 각 68k 앱은 별도 스레드의 Runner를 사용한다. MultiFinder처럼 한 번에 하나의 앱만 CPU를 점유한다. 앱은 코드 실행 중 CPU를 사용하고 WaitNextEvent, GetNextEvent 등 대기 호출에서 CPU를 내놓는다. 다음 앱이 실행될 때는 그 앱의 레지스터와 저메모리 전역값을 복원한다.

cpu.run()은 A-line 트랩마다 반환한다. 디스패처는 Multiversal 인터페이스 파일에서 생성된 m68k/trapdefs.py의 서명을 찾아 인수가 파스칼 스택과 레지스터 중 어디에 있는지, 반환값과 _Pack3, _OSDispatch 같은 디스패치 트랩의 선택자가 무엇인지 확인한다. 이어 @trap으로 등록된 파이썬 루틴을 호출한다. 알려진 트랩에 구현이 없으면 기록하고 건너뛰지만, 전혀 모르는 트랩은 시스템 오류 12로 앱을 중단한다.

SetTrapAddress로 트랩 주소를 패치할 수 있고, GetTrapAddress가 반환한 주소를 통해 트랩을 호출할 수도 있다. OS 트랩 패치는 맥 디스패처가 진입할 때처럼 스택에 저장된 레지스터를 받으며 실행된다. 일부 패치는 이 프레임을 직접 되감기 때문이다. GetTrapAddress가 _OSDispatch를 호출하는 방식은 구현되지 않았지만 동작은 한다. Microsoft 앱을 포함해 이 방식으로 MultiFinder를 확인하는 프로그램은 이후 MultiFinder의 비공개 호출을 한다. Process Manager나 임시 메모리를 필요로 하는 앱은 Gestalt에 요청한다.

프로세서는 68k 코드 블록을 변환해 실행한다. 이미 변환한 블록에 저장이 발생하면 이를 감지하지만, 현재 실행 중인 블록에 대한 저장은 감지하지 않는다. 한 페이지는 실행 또는 쓰기 중 한쪽으로만 매핑되며 둘 다로 매핑되지 않는다. 실행 중인 페이지에 저장하면 프로세서는 저장 지점에서 다시 시작한다(m68k/cpu.py). Microsoft Word는 몇 개 명령어 앞에 트랩 워드를 설정한 뒤 실행한다. Toolbox가 호출하는 필터 프로시저, CDEF, 사용자 항목, 동작 프로시저, 패치된 트랩도 68k 코드다. Runner.call()은 해당 루틴이 반환할 때까지 중첩 CPU 루프를 실행한다.

게스트 메모리가 상태의 기준이다. 창은 앱 힙 안에 있는 156바이트 WindowRecord이며 제목, 포트 사각형, 영역, 강조 상태와 확대 상태를 저장한다. MacWindowView는 레코드에 기록된 내용을 그린다. 68k 코드처럼 레코드 값을 바꾸면 다음 동기화 때 위젯에 반영된다. Qt는 클릭을 자체적으로 처리하지 않는다. 클릭은 소유 앱의 이벤트 큐에 mouseDown으로 들어가고, 앱이 FindWindow와 TrackGoAway 등을 호출해야 닫기 상자가 눌린 모습으로 바뀐다. 앱이 DragWindow를 호출하지 않으면 창도 움직이지 않는다.

MenuSelect, TrackGoAway, TrackBox, DragWindow, GrowWindow 같은 대기 호출은 GUI 스레드에서 트래커를 시작하고, 마우스 이벤트로 트래커가 완료시키는 Future를 기다린다. 앱이 해당 호출에 도달하기 전에 버튼을 놓았다면 트래커는 StillDown과 마찬가지로 이를 확인한다. ui/chrome.py는 창 장식의 WDEF에 해당하는 기하 구조, wHit 코드와 그리기를 순수 함수로 처리한다. 크기 조절·확대 기능이 있거나 없는 문서 창과 dBoxProc, plainDBox, altDBoxProc, movableDBoxProc, 모서리 형태가 다른 rDocProc를 다룬다. 모든 요소는 픽셀 단위의 fillRect로 그려 1비트 출력으로 만든다.

메모리와 리소스

System(toolbox/system.py)은 8MB의 에뮬레이션 RAM을 관리한다. 저메모리 전역값에는 MemTop, ScrnBase, WindowList, MenuList, CurrentA5 등이 있다. 시스템 힙은 Window Manager 포트, GrayRgn, System 파일의 리소스 맵과 기본 프로시저 대체물을 담는다. 각 앱은 SIZE 리소스에 지정된 크기(없으면 1MB)의 파티션을 받는다. 화면 버퍼는 1비트다.

512×342 화면을 쓰는 경우 화면 버퍼는 Mac 512K에서와 같은 $7A700 위치에 놓이고, 대체 버퍼는 그 아래 $72700에 놓인다. 시스템 힙은 두 버퍼보다 아래에서 끝나며 앱 파티션은 남은 RAM을 사용한다. 1984년 게임은 ScrnBase를 확인하지 않고 해당 위치에 직접 쓰기도 한다.

각 파티션은 클래식 맥 앱 공간처럼 아래쪽에 힙, 그 위에 A5 세계보다 아래쪽인 스택, 맨 위에 A5 세계를 둔다. QuickDraw 전역값(thePort, 패턴, 화살표 커서, screenBits)은 A5 바로 아래에 있고, 0(A5)는 InitGraf가 설정한 thePort를 가리킨다.

Memory Manager(memmgr.py)는 포인터와 핸들을 24비트 블록 헤더로 관리한다. 헤더에는 태그 바이트, 실제 크기, 상대 핸들 또는 존 포인터가 있으며 마스터 포인터에는 잠금·폐기 가능·리소스 플래그가 있다. 헤더를 직접 읽는 코드는 예상한 형식을 찾을 수 있다. 힙을 압축하려고 블록을 옮기지는 않는다. SetHandleSize가 제자리에서 핸들을 키울 수 없을 때만 핸들을 옮기며, 잠긴 핸들은 옮기지 않는다.

할당 공간이 부족하면 앱 힙은 ApplLimit 쪽으로 커진다. 그래도 부족하면 가능한 한 힙 전체를 늘린다. MaxApplZone이 생기기 전의 프로그램은 전체 힙을 요청하며 MacWrite는 블록 헤더를 순회해 남은 공간을 계산한다. 그래도 할당할 수 없으면 주소가 낮은 순서로 잠기지 않은 폐기 가능 블록을 비운다. EmptyHandle처럼 비워진 핸들은 NIL 마스터 포인터를 가진 채 유효하게 남는다.

MultiFinder에서는 WindowList, MenuList, CurrentA5, ApplZone 등 일부 저메모리 전역값이 실행 중인 앱에 속한다. 각 앱은 World에 전역값을 보관하고 CPU를 사용할 때 이를 교체한다. Toolbox는 어느 경우든 World를 통해 올바른 사본을 참조한다. GrafPort(108바이트), WindowRecord(156바이트), MenuInfo 핸들과 MenuList 메뉴 막대 핸들은 Inside Macintosh와 같은 형태로 놓인다. WindowRecord는 앱의 WindowList 전역값에서 nextWindow로 연결된다. 파이썬 클래스는 메모리 바이트를 보는 뷰이며, 포트 주소마다 표준 뷰 하나를 둔다. 따라서 게스트 코드에서 반환된 WindowPtr는 UI가 아는 객체와 같다.

QuickDraw(qdraw.py)는 창마다 QImage(Backing)에 그려 창에 표시한다. 다른 포트에서는 게스트 메모리의 1비트 비트맵인 portBits에 그리므로 오프스크린 그리기와 CopyBits가 맥처럼 동작한다. 영역은 실제 QuickDraw 영역 형식으로 게스트 메모리에서 읽고 다시 쓴다. 텍스트는 맥과 마찬가지로 글자마다 펜을 정수 픽셀만큼 이동하므로 워드 프로세서가 한 글자씩 측정한 위치에 그려진다.

화면 버퍼(screen.py)는 창과 동기화된다. QuickDraw가 창에 그린 내용은 해당 창이 화면에서 보이는 버퍼 위치에 기록된다. 프로그램이 버퍼에 직접 쓴 내용도 다음에 macrt가 확인할 때 해당 창에 그려진다. 확인은 트랩 호출 때나 프로그램이 아무 작업도 하지 않을 때 CPU 시계의 매 틱에 이뤄진다.

Resource Manager는 toolbox/resources.py에 구현돼 있다. 맥과 마찬가지로 리소스 맵은 디스크상의 형식 그대로 힙에 놓여 있어 68k 코드가 맵을 순회할 수 있다. 리소스는 _ResourceStub을 통해 읽는다. 맵 핸들은 A4, 항목은 A2로 전달되므로 앱이 _ResourceStub을 패치해 자체 방식으로 리소스를 읽을 수 있다.

macfile.load_file은 호스트에 저장된 Mac 파일을 여러 형식으로 읽는다. MacBinary I/II/III, BinHex 4.0, AppleSingle, AppleDouble(._app, .AppleDouble/app, The Unarchiver가 만드는 app.rsrc), Basilisk II의 .rsrc/와 .finf/, Executor의 %app, 일반 app.rsrc, macOS의 ..namedfork/rsrc를 지원한다. 명령행에서 사용하는 open_application은 디스크 이미지도 열며 StuffIt과 zip 아카이브는 풀라는 안내와 함께 거부한다.

load_volume은 machfs를 통해 일반 HFS 디스크 이미지, 원시 이미지, DiskCopy 4.2 이미지와 Apple 파티션 맵 안의 이미지를 연다. 400K MFS 디스크는 직접 읽어 같은 형식으로 만든다. MFS의 평면 디렉터리를 읽고 각 포크를 블록 맵을 따라 찾는다. 플로피 트랙을 담은 MOOF 이미지는 먼저 섹터로 디코딩한다(macrt.moof). 각 트랙의 비트에 담긴 GCR 바이트와 섹터의 3바이트를 4개로 바꾸는 형식을 디코딩하고, 체크섬이 섞인 데이터를 분리한다. 볼륨은 디스크 섹터와 태그를 .disk로 보관해 플로피 드라이버(glue_sony)가 사용할 수 있게 하며, MOOF 이미지의 트랙도 함께 보관한다. Folder는 대소문자를 구분하지 않고 Mac 이름으로 파일을 찾고, OpenResFile은 앱이 있던 폴더에서 이름을 찾는다.

파일을 열면 리소스 맵을 핸들로 복사하고 nextMap과 파일 참조 번호를 채운 뒤 앱의 리소스 맵 체인 맨 위에 둔다(TopMapHndl, CurMap). 체인의 끝은 시스템 힙에 있는 System 파일 맵(SysMapHndl, SysMap = 2)이다. System 파일을 지정하지 않으면 빈 파일을 만든다. 어느 경우든 WDEF, MDEF, CDEF, LDEF, MBDF 대체물은 이 파일의 리소스이므로 GetResource('WDEF', 0)이 작동하고 창의 windowDefProc도 실제 리소스 핸들이다. 파일에 없는 기본 리소스도 sysres.py에서 제공한다. 여기에는 패키지 트랩을 자동으로 스택에서 제거해 패키지 호출이 트랩 호출과 같게 하는 PACK 0~15, 경고 아이콘 ICON 0~2, 커서 CURS 1~4가 포함된다.

리소스 맵 항목은 리소스 핸들을 보관하므로 같은 리소스를 두 번 요청하면 같은 핸들을 반환한다. 속성은 마스터 포인터의 잠금, 폐기 가능, resSysHeap(시스템 힙에 적재), resPreload(열 때 적재) 플래그로 반영된다. 폐기된 리소스는 같은 핸들로 다시 읽는다. System 7 압축 리소스는 rsrcfork로 압축을 푼다. 압축을 풀 수 없으면 CantDecompress를 반환한다. SetResLoad(false), Get1…, UseResFile은 문서에 설명된 대로 동작한다.

AddResource, ChangedResource, RemoveResource, SetResInfo, SetResAttrs는 메모리 안의 맵을 바꾼다. UpdateResFile과 CloseResFile은 포크를 MacFile에 다시 쓴다. 다만 이 내용은 아직 호스트 파일에 저장되지 않는다. 앱을 시작하면 앱의 리소스 포크를 현재 파일(CurApRefNum)로 열고 SIZE 리소스에 따라 파티션 크기를 정한다. 종료할 때 앱이 연 모든 파일을 닫는다.

toolbox/templates.py는 WIND, DLOG, ALRT(네 단계), DITL, CNTL, MBAR, STR#을 파싱하고 centerMainScreen, alertPositionMainScreen 같은 System 7 위치 지정 값을 적용한다. 이를 바탕으로 GetNewWindow, GetMenu, GetNewMBar, SetMenuBar, GetMenuBar, GetString, GetIndString을 제공한다. GetMenu에서는 MENU 리소스가 MenuInfo이며 MDEF ID가 MDEF 핸들이 된다. Dialog와 Control Manager는 DLOG, ALRT, DITL, CNTL로 대화상자·경고창·컨트롤을 만든다.

파이썬에서 리소스 파일을 읽고 앱을 실행하는 예시는 다음과 같다.

python
from macrt.macfile import load_file

app = load_file("Hello.bin") # or ._Hello, Hello.rsrc, …
display.launch(app.name, guest, app=app)
# guest(tb): tb.SetMenuBar(tb.GetNewMBar(128)); w = tb.GetNewWindow(128) …

글꼴과 테스트

창 장식은 시스템 글꼴 Chicago와 앱 글꼴 Geneva를 사용하면 클래식 맥처럼 보인다. 두 글꼴은 프로젝트에 포함되지 않는다. .ttf 또는 .otf 파일을 macrt/ui/fonts/에 넣으면 시작할 때 불러온다. ChicagoFLF는 자유 라이선스로 제공되는 Chicago 복제 글꼴이다. Mac 글꼴이 있으면 선명한 1비트 모양을 위해 텍스트 안티앨리어싱을 끈다. 글꼴이 없으면 윤곽선 글꼴이 깨져 보이므로 안티앨리어싱을 켠 Helvetica 13px로 대체한다. theme.TEXT_ANTIALIAS = True/False로 설정을 강제할 수 있다.

호스트 글꼴은 Chicago, Geneva, New York, Monaco 등 표준 글꼴 계열을 대신한다. 앱이 FONT나 FOND에 포함된 NFNT로 자체 글꼴을 제공하면 해당 글꼴의 비트맵으로 그린다. 이런 글꼴에는 글자 대신 그림이 들어가기도 한다. MacPaint 도구 팔레트와 게임의 스프라이트·점수 숫자가 그 예다. 앱에 없는 크기는 가장 가까운 크기에서 확대·축소한다. 앱이 표준 글꼴 계열의 특정 크기 비트맵을 포함했다면 그 비트맵도 사용한다. 맥처럼 앱 파일을 System 파일보다 먼저 확인하기 때문이다. Balance of Power는 New York 36 제목을 글자 단위로 그려 해당 비트맵이 필요하다.

글자 폭도 글꼴 자체의 폭을 사용한다. StringWidth, MeasureText, TextEdit, TETextBox가 모두 이 폭을 기준으로 줄을 나눠 앱이 예상한 위치에 표시한다. 굵게, 기울임, 밑줄, 외곽선, 그림자도 QuickDraw 방식으로 그린다. Chicago, Geneva, Monaco에서 NUL과 리턴처럼 폭이 정의되지 않은 문자는 호스트 글꼴처럼 상자로 표시하지 않고 아무것도 그리지 않는다.

테스트는 오프스크린에서 약 30초 동안 실행한다.

text
pytest # runs offscreen, in about half a minute

tests/test_memory.py와 tests/test_records.py는 RAM, 힙, 전환된 전역값과 각 레코드 바이트를 Inside Macintosh와 대조한다. tests/test_qdraw.py는 1비트 비트맵에서 QuickDraw 그림을 비트 단위로 확인하고 영역을 QuickDraw 자체 영역 형식과 비교한다. tests/test_pict.py, tests/test_icons.py, tests/test_color.py는 그림, 아이콘, 1비트 화면에서의 색상 호출을 확인한다.

tests/test_m68k_apps.py, tests/test_controls.py, tests/test_dialogs.py, tests/test_lists.py, tests/test_process_mgr.py는 tests/apps의 68k 앱을 에뮬레이션 CPU에서 실행하고 가상 클릭과 키 입력을 보낸다. 각 Toolbox 기능을 다루는 작은 어셈블리 프로그램이 포함돼 있다. Hello는 창·메뉴·그리기를, Controls는 버튼·체크박스·라디오 버튼·스크롤 막대를, Dialogs는 편집 필드가 있는 모달 대화상자와 경고창을 시험한다. Edit는 Scrap을 통한 잘라내기·복사·붙여넣기가 있는 TextEdit 창을, Lists는 스크롤 막대가 있는 List Manager 목록의 클릭·범위 선택·스크롤을 다룬다. Launcher는 _Launch, Menus는 앱 자체 메뉴 막대 정의 프로시저를 시험한다. VBL은 Delay 중에도 두 틱마다 실행되고 제거될 때까지 계속되는 VBL 작업을 확인한다. Sounds는 Sound Driver의 구형파·4음·자유 형식 사운드와 사운드 버퍼에 직접 쓴 샘플을 시험한다. Vectors는 TRAP #1, 0 나누기, CHK, VIA 인터럽트에 자체 핸들러를 설치하고 ROM 핸들러로 이어지는지 확인한다. AEvents는 Open Application 이벤트와 목록을 담아 앱 자신에게 보내는 이벤트를 포함해 Apple Event Manager와 PPC 포트를 시험한다.

python tests/apps/build.py로 m68k용 binutils를 사용해 테스트 앱을 다시 빌드할 수 있다. 빌드된 .bin 파일도 저장소에 포함돼 있어 테스트에 어셈블러가 필요하지 않다.

tests/test_files.py는 포크, MacBinary, BinHex(손상된 파일 포함), AppleSingle/Double, 에뮬레이터 부가 파일과 The Unarchiver 파일을 읽는지 확인한다. machfs로 만든 HFS 이미지와 포크를 디스크 곳곳에 흩어 놓고 바이트 단위로 만든 MFS 이미지, tests/builders.py에서 트랙별 GCR 인코딩을 거친 MOOF 이미지(읽을 수 없는 섹터 포함)도 시험한다. 디스크 이미지에서 명령행이 앱을 선택하는 방식과 아카이브 거부도 확인한다.

tests/test_filemgr.py는 부팅 볼륨, 호스트 폴더, 디스크 이미지에서 파이썬과 68k 코드로 File Manager를 시험한다. 플로피 드라이버, 프로그램 자체의 RdData와 ROM의 RdData, Open·Save 대화상자도 다룬다. tests/test_textedit.py는 TextEdit 레코드와 그리기를, tests/test_scrap.py는 Scrap과 호스트 클립보드를 확인한다. tests/test_viewer.py는 리소스 목록, 명령행 오류, 실행 중인 화면의 ResourceViewer를 시험한다. tests/test_resources.py는 리소스 폐기, 게스트 메모리의 리소스 체인, 적재·폐기·재적재, 파일 열기·닫기와 포크 기록, 템플릿, 리소스에서 메뉴와 창을 만드는 앱을 실행 화면에서 확인한다.

tests/test_sane.py는 Apple Numerics Manual의 표와 IEEE 특수값(NaN, 무한대, 비정규 수, 부호 있는 0)을 모든 반올림 방향·정밀도에서 대조하고, 배정밀도 연산을 호스트 연산과 교차 확인한다. 패키지를 68k 코드에서 호출해 조건 코드와 중단 동작도 확인한다. tests/test_intl.py는 미국 국제 리소스, 날짜, 시간, 문자열 정렬을 확인한다. tests/test_script.py는 Script Manager의 문자 종류·대소문자·악센트·단어·줄바꿈·잘림·양쪽 정렬 측정과 디스패처 선택자를 검사한다. tests/test_fonts.py는 표준 글꼴 계열과 글꼴 메뉴를, tests/test_bitfonts.py는 앱 자체 비트맵 글꼴과 System의 패턴 목록을 확인한다. tests/test_aevents.py는 Apple 이벤트 데이터와 Apple Event Manager 자체의 형식 변환을 검사한다.

tests/test_screen.py는 QuickDraw 그리기가 화면 버퍼에 반영되는지, 프로그램이 버퍼에 직접 쓴 내용이 창에 표시되는지, 데스크톱이 어떻게 보이는지 확인한다. tests/test_hardware.py는 VIA와 IWM, Mac 512K에 해당하는 512×342 화면, ROM과 예외 벡터, 마우스·키보드의 Event Manager 저메모리 기록을 시험한다. tests/test_cpu.py는 프로세서 실행 구간, 인터럽트와 속도 제한, 나누기·CHK·TRAPV 예외 뒤 돌아가는 위치, 추적 모드를 확인한다.

tests/test_display.py는 두 메뉴 막대 방식에서 가상 Qt 마우스·키 이벤트를 실제 입력 경로로 보낸다. 닫기 상자(창 밖에서 놓는 경우 포함), 창 이동·크기 조절·확대와 복원, 뒤쪽 창 선택, 누르고 끌어 놓는 메뉴와 고정 메뉴, 비활성 메뉴 항목과 ⌘ 키를 다룬다. 앱 전환, Application 메뉴의 전환과 Hide Others, 창별 메뉴 막대와 메뉴 전용 창, 종료 동작, 레코드 값을 직접 바꾼 창도 확인한다.

다음 개발 항목은 Color QuickDraw, 호스트 창이 포커스를 얻거나 잃을 때 추가 클릭 없이 클립보드가 따라오도록 suspend·resume 처리, 그리고 더 많은 실제 앱 실행이다. 새 앱은 실행 과정에서 먼저 마주치는 문제를 해결해 나갈 예정이다. 관련 앱 목록은 실행해 본 앱에서 볼 수 있다.