ZX Spectrum용 그래픽 데스크톱
요약
ZX Desk는 Z80 어셈블리로 작성된 ZX Spectrum 48K용 그래픽 데스크톱으로, 실제 하드웨어에서 구동됩니다. 창 관리, 메뉴 표시줄, 메모장, 파일 관리자 등 현대적인 데스크톱 기능을 구현했습니다. 특히 실측 기반 최적화와 이벤트 기반 구조를 통해 성능을 개선하고 장치 계층을 분리하는 설계 원칙을 적용한 것이 특징입니다.
핵심 포인트
- ZX Spectrum 48K 하드웨어에서 동작하는 그래픽 데스크톱 구현.
- 창 관리, 메뉴, 파일 시스템 등 다양한 OS 기능을 Z80 어셈블리로 구현.
- 실측 기반 최적화로 창 드래그 성능을 개선하고 이벤트 구조를 채택함.
- 장치 계층 분리 및 인터럽트 처리 방식을 수정하여 안정성을 높임.
ZX Desk 는 Z80 어셈블리로 작성한 ZX Spectrum 48K용 데스크톱으로, 겹치는 창과 메뉴, 메모장, 시계, 달력, 파일 관리자를 48K 안에 구현하고 실제 하드웨어에서 실행함
실측 기반 최적화 로 창 드래그 처리 시간을 프레임당 69,888 T 상태 이내인 59,858 T로 줄여, 25Hz였던 드래그를 50Hz로 끌어올림
이벤트 기반 구조와 교체 가능한 저장소 백엔드 를 사용하며, 장치 계층을 분리해 화면 메모리 구조를 상위 코드에서 직접 다루지 않도록 설계함
화면 채우기 중 인터럽트를 막으면 인터럽트가 지연되는 것이 아니라 유실됨 을 확인하고, 인터럽트를 계속 허용하면서도 스택을 이용해 화면을 채우는 방식으로 수정함
기본 데스크톱 기능은 동작하지만 가려진 시계의 갱신 , 앱의 닫기 거부, 메모장의 선택/클립보드/실행 취소/자동 줄바꿈은 아직 지원하지 않음
48K에서 구현한 데스크톱
ZX Desk 는 1982년의 ZX Spectrum 48K에서 실행하는 그래픽 데스크톱이며, 에뮬레이터뿐 아니라 실제 기기에서도 동작함
출발점은 1980년대 Atari ST의 GEM )처럼 창과 상시 메뉴 표시줄이 있는 환경을 Spectrum에서 구현하려던 시도였음
GEM의 이식판은 아님
3.5MHz Z80 , 48K RAM, 속성 충돌이 있는 1비트 디스플레이, 화면을 그리는 동안 CPU 사이클을 가져가는 영상 칩이라는 제약에 맞춰 설계함
구현 자체뿐 아니라 실제 기기를 측정해 얻은 수치 를 공유하는 데 비중을 둠
현재 기능과 사용 제약
창과 메뉴 는 일반적인 데스크톱 조작을 지원함
창은 겹침, Z 순서, 이동, 크기 조절, 제목 표시줄, 닫기 버튼, 크기 조절 손잡이를 갖춤
Z 순서의 맨 앞이 포커스이므로 창을 앞으로 가져오는 동작과 포커스 설정이 하나임
상시 메뉴 표시줄과 풀다운 메뉴를 제공하며, 메뉴 아래 화면 저장과 히트 테스트를 지원함
입력 은 Kempston 마우스, Kempston 조이스틱, 전체 키보드 매트릭스를 지원하고 모두 이벤트로 전달함
키보드는 세 개의 테이블로 해석하며 반복 입력을 지원함
저장소 는 RAM, 실제 ROM 로더를 사용하는 테이프, 128K 기기의 여유 메모리 뱅크를 이용한 RAM 디스크를 지원함
백엔드 레지스트리와 여섯 개의 벡터를 통해 접근하며, esxDOS 용 ID도 확보해 둠
후반 구현 현황에서는 esxDOS 코드도 작성과 테스트를 완료한 상태임
앱과 설정 은 메모장, 시계, 달력, 2패널 파일 관리자 Commander, 정보 창을 제공함
Commander는 디렉터리가 아니라 장치 사이에서 파일을 다룸
메모장은 Sinclair 방식으로 Shift 상태를 표시하는 커서를 사용함
설정 패널에서 바꾼 값은 실제 동작에 반영되며, 매직 바이트와 버전을 붙여 저장한 뒤 부팅 시 읽어 들임
macOS용 Fuse 1.9.2 에서는 마우스 버튼을 누르는 동안 Kempston 마우스 이동 전달이 멈추므로, 창 드래그에 마우스 버튼 대신 스페이스바 를 사용해야 함
제목 표시줄에 포인터를 놓고 스페이스바를 누른 채 이동한 뒤 놓는 방식임
다른 마우스 동작과 버튼 감지는 정상이며, 실제 Kempston 마우스에서는 일반적인 드래그가 가능함
마우스 포트를 직접 읽는 MOUSETEST=1
진단에서도 버튼을 누르면 이동 카운터가 멈춰, 데스크톱이 아닌 에뮬레이터 문제임을 확인함
스페이스바 처리는 마우스 없는 기기에서도 데스크톱을 쓰기 위한 기능이기도 함
빌드는 pasmo 0.5.5 를 사용하며, 도구와 에뮬레이터, esxDOS ROM은 저장소에 포함하지 않음
빌드 과정은 코드가 버퍼 영역까지 커지면 중단함
이 검사가 없으면 첫 창 화면 저장이 프로그램을 덮어쓰고, 잠시 뒤 관련 없어 보이는 오류와 함께 BASIC으로 돌아갈 수 있음
합성 입력 을 사용하는 SCRIPT=1
은 메뉴 열기, 항목 선택, 창 겹쳐 이동하기, 필드 입력, 저장 등을 매번 동일하게 실행해 이전 결과와 비교할 수 있게 함
자체 드래그 시연, 빔 스케줄러를 끈 드래그, 원시 마우스 진단용 빌드도 제공함
장치 계층과 이벤트 기반 구조
장치 계층 은 DevFillRect
, DevFillDesk
, DeskFillCol
, AddrAt
, BlitRect
, RectGrab
로 구성함
상위 계층은 바이트 단위 열과 픽셀 단위 행만 다루며, Spectrum 화면 메모리의 3분할과 인터리브 배치를 직접 취급하지 않음
다른 기기로 옮길 때 교체할 경계로 처음부터 분리함
프레임 루프 는 인터럽트에서 깨어난 뒤 상단 테두리 구간에서 포인터를 처리하고, 창이 움직였다면 영상 빔을 기다린 후 다시 그림
입력 읽기와 이벤트 처리는 프레임 끝에 수행함
입력을 뒤로 보내 빔 대기 이전의 처리 비용을 일정하게 유지하므로 스케줄러 타이밍을 정확하게 맞출 수 있음
이벤트 큐 는 4바이트 이벤트 16개를 담는 링 버퍼임
EvPoll
이 원시 입력을 포인터 이동, 버튼 누름, 키 이벤트로 바꾸고 EvDispatch
가 핸들러 테이블을 통해 처리함
메인 루프에는 특정 창 전용 코드가 없음
히트 테스트 는 컨트롤당 5바이트 행을 사용하고 Z 순서의 앞에서 뒤로 검색하며 $FF
로 끝남
검색 전에 모델에서 테이블을 다시 채워 창 위치의 오래된 복사본이 남지 않게 함
닫힌 메뉴는 높이를 0으로 설정하므로 별도 예외 처리 없이 검색에서 제외됨
임시 화면 영역 은 최대 16×96 크기의 화면을 네 단계까지 쌓고 꺼내는 구조임
패널 레코드가 없는 메뉴도 아래쪽 화면을 저장하므로 스택을 둘로 분리함
창 레코드 는 사용할 때 활성 복사본으로 교체함
창 위치를 여섯 파일의 93곳에서 참조하므로 패널 레코드와 같은 방식을 적용함
Z 순서는 그리기 순서의 역순이자 히트 테스트 순서임
저장소 백엔드 는 ID, 기능 비트, 여섯 진입점으로 이루어진 14바이트 행으로 등록함
백엔드 선택 시 여섯 JP
명령의 피연산자를 수정하므로 호출 분배는 10 T 상태 가 들고 레지스터를 훼손하지 않음
컨트롤 은 패널의 행으로 구성하며, 행 인덱스를 검색 대신 세 번의 시프트로 계산함
네 가지 유형 중 순환 선택 컨트롤을 중심으로, 한도가 2이면 체크박스, N이면 라디오 그룹으로 사용함
설정 패널, 파일 목록, 저장 대화상자는 서로 다른 테이블과 같은 코드를 사용함
앱 모델 은 초기화, 이벤트, 그리기 함수가 있는 기술자와 인스턴스별 상태 교체 방식으로 구성함
메모리 배치와 경계 조건
메모리를 저속 영역과 고속 영역 으로 나눠, 프레임 타이밍에 민감한 경로를 ULA 경합 영역에서 제외함
$6000–$727C
: 패널, 달력, 파일 패널, 데스크톱 설정, Commander 등 저속 영역임
$727D–$7FFF
: 경합이 있는 여유 공간 3,460바이트임
$8000–$B1B7
: 나머지 고속 코드 영역임
$B1B8–$BCFF
: 여유 공간 2,889바이트임
런타임 데이터 는 스택과 인터럽트 영역 뒤에 배치함
$BD00
: 스택 최상단, $BDBD
: 인터럽트 핸들러, $BE00–$BEFF
: 인터럽트 벡터 테이블임
$C000–$C63F
: RAM 디스크, 디렉터리, 테이프 버퍼임
$C640–$C74F
: 활성 메모장 상태, $C750–$DF4F
: 임시 화면 네 개의 공간임
$DF50–$FEFF
: 소유자 바이트를 갖춘 8,112바이트 힙 으로, 창 크기에 맞춰 버퍼를 할당함
$FF00–$FFFF
: 의도적으로 사용하지 않음
ULA가 화면을 그리는 동안 $8000
아래에서 사이클을 가져가므로, 그곳의 코드는 약 3분의 1 정도 느려질 수 있음
저속 영역에는 드래그, 포인터, 인터럽트 경로를 두지 않으며, 코드 이동 전후 타이밍은 T 상태 단위까지 같았음
시작점을 $6000
으로 잡아 테이프 로더가 IY로 접근하는 시스템 변수와 그 위의 BASIC 로더를 보존함
힙을 $FFFF
까지 쓰지 않는 이유는 주소 넘침 을 막기 위해서임
다음 블록 주소를 현재 주소, 헤더 크기, 블록 크기의 합으로 구하므로 끝이 $10000
이면 0으로 돌아가 힙 시작보다 낮은 주소가 됨
마지막 256바이트를 포기해 이 경계에서 생기는 실패를 제거함
측정 방법과 실제 병목
69,888 T 상태인 48K 인터럽트 주기 를 기준 시계로 사용함
HALT
로 동기화하고 필요하면 정해진 지연을 넣은 뒤 측정 대상을 호출하며, 다음 인터럽트까지 16 T 루프가 몇 번 실행되는지 셈
빈 보정 실행과 비교해 고정 오버헤드를 제거함
비용 계산식은 cost = (K - K0) * (69888 - 118) - 16 * (C - C0) - delay
임
인터럽트 복귀 경로 118 T 는 명령별로 계산한 정확한 값임
핸들러, 변수, 계수 루프, 스택은 모두 $8000
위에 있어 ULA 경합이 없으며, 측정 대상만 경합 메모리에 접근함
알려진 지연 2,599 T, 51,999 T, 103,999 T를 각각 2,592 T, 52,000 T, 103,994 T로 측정함
최대 오차는 7 T였고, 프레임 경계를 넘는 세 번째 측정으로 118 T 값도 확인함
화면 쓰기 경합 비용은 해당 측정에서 14.7% 였으며, 처음 가정한 50%보다 작았음
1,024바이트 채우기는 상단/하단 테두리에서 11,744/11,745 T, 화면 표시 중 최악에는 13,473 T였음
경합이 있는 바이트 쓰기당 약 1.7 T가 추가됐으며, 50%를 전제로 한 예산은 약 3분의 1 지나치게 보수적이었음
창 그리기의 절반은 짧은 문자열 세 개 에 들었음
프레임 상단에서 텍스트 35,872 T, 테두리 19,552 T, 채우기 14,688 T, 닫기 버튼 832 T였음
전체 WinDraw
는 상단에서 71,274 T, 화면 표시 중간에서 73,099 T였음
제목과 본문 27글자는 글자당 약 1,330 T가 들었고, 병목으로 의심했던 채우기는 약 5분의 1에 그침
네 단계의 합과 전체 측정 차이는 330 T 이내로, 호출/복귀 비용과 16 T 측정 단위에 해당함
기법만 측정한 벤치마크 는 실제 루틴 비용을 과소평가했음
셀 경계를 고려한 스택 푸시 채우기를 처음에는 바이트당 7.4 T로 기록했지만, 행별 주소 계산을 포함한 실제 비용은 11.5 T로 54% 더 컸음
행당 183 T 중 픽셀 쓰기는 88 T, 레지스터 교환과 주소 이동, 셀 경계 검사, 반복 처리는 95 T였음
이 프로세서에서는 바이트당 전송량뿐 아니라 행마다 반복되는 오버헤드를 줄이는 것이 중요함
인터럽트 유실과 안전한 스택 기반 채우기
Spectrum은 INT를 32 T 동안만 유지 하므로, DI
구간이 이를 덮으면 인터럽트는 지연되지 않고 사라짐
기존 채우기는 SP를 화면 주소로 사용해 일반 스택으로 쓸 수 없었으므로 실행 내내 인터럽트를 막았음
프레임 시작 후 62,399 T에 시작한 채우기가 분명히 프레임 경계를 넘는데도 넘지 않은 것으로 측정됐음
인터럽트 하나가 유실된 것으로 다시 계산하자 11,745 T가 나와 상단 테두리의 11,744 T와 1 T 차이로 일치함
명령 경계별 인터럽트 주입 으로 문제 범위를 검증함
8×24 채우기의 두 루틴에서 각각 500개 경계 중 458개, 752개 중 710개가 인터럽트를 받아들이지 못했음
행 사이에서 잠깐 EI
를 실행해도 이미 사라진 인터럽트는 돌아오지 않음
236 T마다 몇 T씩 허용하는 방식은 약 30행에 한 번만 포착하므로, 금지 구간을 짧게 만드는 것만으로는 해결되지 않음
수정은 마지막 푸시를 다음 반복으로 미루는 방식 으로, 인터럽트를 계속 허용하면서 복귀 주소가 곧 덮어쓸 곳에만 기록되도록 함
행의 푸시 체인을 한 번 짧게 만들고 왼쪽 두 바이트의 쓰기를 보류함
SP가 다음 행으로 옮겨 이전 위치에 접근할 수 없게 된 뒤 보류한 두 바이트를 기록함
수정 후에는 각각 607개와 904개 명령 경계에서 유실이 0건 이었고, 사각형 결과와 화면 체크섬이 동일하며 바깥 영역도 바뀌지 않았음
안전성 확보에는 비용이 들었음
16×96 사각형 채우기는 17,414 T에서 22,068 T로 늘어 행당 48 T가 추가됨
같은 크기의 데스크톱 채우기는 25,351 T에서 30,125 T로 늘어 행당 50 T가 추가됨
드래그 경로의 대부분은 HL로 쓰는 열 채우기이므로 SP를 사용하지 않았고 이 위험에 노출되지 않았음
프레임 감시기가 드러낸 레지스터 문제
프레임 감시기 는 핸들러에서 인터럽트를, 메인 루프에서 프레임을 세어 그 차이로 누락 프레임을 확인함
첫 핸들러는 IX를 빌려 INC (IX+0)
로 계수했지만, 이 명령이 플래그를 변경 해 인터럽트 주입 테스트에서 실패함
채우기는 행 주소 계산과 셀 경계 분기 사이에 캐리 플래그를 유지해야 함
그 사이 인터럽트가 들어오면 캐리가 바뀌어 다음 행 주소가 잘못됨
플래그를 바꾸지 않는 16비트 INC IX
로 전환하고 카운터도 워드로 바꿈
최종 핸들러는 플래그와 레지스터를 바꾸지 않으며 회당 104 T 가 듦
감시기가 인터럽트 버그를 먼저 잡은 것이 아니라, 인터럽트 테스트가 감시기의 버그를 잡았음
핸들러에서 AF를 푸시하는 대안은 SP 아래 사용 공간을 2바이트에서 4바이트로 늘려, 보류 영역을 두 배로 만들고 행마다 22 T를 더 요구함
창 드래그를 한 프레임 안에 넣기
초기의 전체 지우기와 다시 그리기 는 96,010 T로 프레임의 137%를 사용해 드래그가 25Hz였음
다음 최적화를 순차적으로 측정하고 적용함
오프스크린 버퍼 에 한 번 합성한 뒤 프레임마다 복사해 다시 그리기를 71,274 T에서 26,048 T로 줄임
텍스트 렌더러를 재작성해 글자당 1,307 T를 622 T로 줄여 2.10배 빨라짐
손상 사각형(damage rectangle) 으로 창이 떠난 띠 부분만 지워 삭제 비용을 19,664 T에서 1,456 T로 줄임
좁은 데스크톱 채우기는 패턴을 레지스터에 보관하고 HL로 두 바이트를 직접 쓰며, 네 가지로 펼친 코드로 행별 패턴 위치 계산을 없앰
그 결과 단일 열 띠 채우기가 14,256 T에서 5,712 T로 줄어듦
빔이 창 전체를 지나가길 기다리는 대신 빔을 뒤쫓아 그려 16,128 T를 더 줄임
최종 드래그 프레임은 59,858 T에 끝나 50Hz 로 동작함
빔 스케줄러는 한 주사선이 224 T이고 상단 테두리가 64줄인 점을 이용해, 나눗셈 대신 8비트 덧셈으로 목표 주사선을 계산함
163줄 대기는 실측 36,576 T였으며 수작업 계산은 36,353 T였음
텍스트 렌더러 에서는 처음 의심한 원인이 실제 병목이 아니었음
주소 루틴 호출은 픽셀 행마다가 아니라 글자마다 한 번이었고, 셀 경계 통과 자체에는 비용이 없었음
실제 비용은 매 행 반전 플래그 검사 232 T, 행 이동 루틴 여덟 번 호출 344 T, 바로 오른쪽 바이트 주소 재계산 126 T였음
새 구현은 문자열마다 자체 수정 XOR
피연산자를 한 번 설정하고, 문자열 전체를 하나의 주소로 순회하며, 넘을 수 있는 단일 셀 경계 양쪽으로 여덟 픽셀 행을 나눠 처리함
손상 영역만 복사하는 방식 은 그대로 적용할 수 없음
창이 수직으로 움직이면 아래에 같은 창이 잘못된 위치로 남아 있으므로 모든 행을 다시 써야 함
손상 영역은 지우기만 줄임
화면 저장 시 행별 서명을 만들면 균일한 내부 영역을 생략해 해당 창에서 약 9,700 T를 아낄 가능성이 있지만, 넓고 평평한 영역을 가진 창에만 이득이 있어 보류함
스택 기반 화면 복사 도 채택하지 않음
여덟 레지스터 쌍의 팝/푸시는 행당 약 212 T로 LDI
의 256 T보다 작지만, SP 저장과 재설정을 더하면 약 304 T로 현재 327 T와 차이가 작음
DI
를 쓰면 인터럽트 유실이 돌아오고, 보류 푸시로 안전하게 만들면 행당 48 T가 더 들어 오히려 불리함
시계 보정과 나눗셈 없는 달력
48K 인터럽트는 정확히 50Hz가 아니라 50.0801Hz 임
3.5MHz에서 69,888 T인 프레임마다 발생하므로, 50회마다 1초로 세면 0.16% 빨라져 하루 2분 18초가 어긋남
시계는 보통 50회, 때때로 51회를 1초로 사용하도록 백분율 단위 보정값을 누적함
한 시간 구동에서 실제 기준 180,288회에 대해 180,287회를 사용해 하루 약 0.5초 오차 수준이었음
128K 는 3.5469MHz에서 70,908 T인 프레임을 사용해 50.0211Hz이며 보정값도 다름
시계 초기화 전에 기기 감지를 완료함
메인 루프 실행 횟수가 아니라 감시기의 인터럽트 카운터 차이를 읽으므로, 메인 루프가 놓친 프레임도 시간에 반영함
달력 은 1980년 1월 1일 화요일부터 요일을 더해 계산함
Zeller 나 Sakamoto 방식에 필요한 4, 100, 400 나눗셈 대신 평년은 1, 윤년은 2를 더함
다시 그릴 때 최대 100번의 덧셈만 필요함
지원 범위인 1980~2079년 에서 유일한 세기 연도인 2000년은 윤년이므로, 이 범위에서는 4로 나누어지는지만 보면 됨
표시 가능한 1,200개월 모두에 대해 첫 요일과 월 길이를 Python datetime
과 대조함
실행으로 찾아낸 버그와 테스트 교훈
스스로 복구되는 것처럼 보인 힙 버그 는 잘못된 해제를 숨겼음
DEC HL
이 한 번 더 실행돼 블록 크기의 상위 바이트를 0으로 만들었지만, 병합기가 0으로 채워진 데이터 영역을 빈 블록으로 오인해 4바이트씩 흡수하며 원래 총량을 복원함
창 픽셀처럼 0이 아닌 데이터가 들어간 블록을 해제하자 힙이 2,619바이트 부족해짐
이제 해제 전에 데이터 영역을 $A5
로 채우고, 복구될 수 있는 총량 대신 헤더를 직접 검사함
인수와 반환값의 레지스터 충돌 은 다섯 개의 별도 버그를 만들었음
검색 루틴의 POP AF
가 비교 전 플래그를 되살려 검색이 일치하지 않았음
십의 자리 계산에 B를 쓰는 숫자 출력기를 B에 다른 숫자를 둔 채 호출해 달력 연도가 2050으로 출력됨
LDIR
로 끝나는 두 루틴이 A를 훼손하고 BC를 0으로 만들어, 두 번째 창이 첫 번째 창의 복제본이 되거나 패널 행 수가 0이 됨
B를 행 카운터로 쓰는 루틴 호출 전에 DJNZ
카운터를 준비하면 반복이 256회가 되는 문제도 두 번 발생함
Z80에서는 플래그도 레지스터 이며, 호출을 넘어 보존할 값은 메모리나 스택에 둬야 함
행 이동의 빌림 처리 누락 은 창 위치에 따라 드러났음
LDI
가 DE 전체를 증가시킨 뒤 E에서만 폭을 빼면, 행이 256바이트 경계를 넘을 때 이미 증가한 D를 다시 증가시켜 다음 행이 한 페이지 위로 이동함
열 8의 기존 창에서는 나타나지 않다가 두 번째 창을 열 16으로 옮기자 발생함
남은 루프 횟수를 성능값으로 잘못 읽는 문제 도 있었음
루틴이 느릴수록 다음 인터럽트까지 남은 반복 횟수는 줄어듦
16 T 절약을 1 T 손실로, 1,216 T 추가 비용을 76 T 절약으로 잘못 읽어 실제 회귀를 놓쳤음
계산으로 얻는 수치는 사람이 판단할 최종 단위로 출력해야 함
손상된 블록 크기 는 힙 탐색을 오류 종료가 아닌 무한 반복으로 만들 수 있음
다음 주소가 $FFFF
를 넘어 힙 시작 아래로 돌아오면 여전히 힙 내부로 비교됨
현재는 잘못된 크기를 만드는 경로가 없다는 판단으로 방어 코드를 추가하지 않았지만, 이 실패는 멈춤으로 나타남
이전 실험에서도 경계와 실제 하드웨어 에서만 드러나는 문제가 있었음
잘못된 방향으로 시프트한 LFSR의 주기가 65,535 대신 71이 됨
시험용 카드 게임에서 레지스터 훼손으로 잘못된 카드가 출력됨
래스터 타이밍 버그로 실제 기기의 화면 상단 절반에서 포인터가 사라졌지만 에뮬레이터에서는 보였음
부호 비트 언더플로로 Y가 127을 넘는 순간 창이 화면 위쪽으로 이동함
연구, 구현, 비판의 작업 방식
모든 작업은 한 세션에서 연구 → 구현 → 비판 의 세 단계를 거침
연구에서는 Spectrum 데모씬, ULA와 플로팅 버스 문서, esxDOS API, Next 레지스터 목록 등 기존 해법을 먼저 찾음
구현에서는 측정 가능한 최소 기능과 측정 방법을 함께 커밋함
비판에서는 레지스터 훼손, 화면 끝 경계 조건, 더 이상 유효하지 않은 이전 수치를 확인함
성능은 타이밍 , 정확성은 화면 체크섬 없이 받아들이지 않는 방식을 사용함
화면에서 올바르게 보인다고 작업이 끝난 것은 아님
직접 관측하지 못한 값은 실측이 아니라 계산으로 도출한 값으로 구분하며, 특히 래스터 동작에서 이 구분을 유지함
검증한 작업 하나마다 커밋 하나를 만들고, 커밋 메시지에 통과 기준 수치를 남김
미완성 기능과 ZX Spectrum Next
esxDOS 구현은 작성과 테스트를 마침
실제 DivMMC가 없어 실행하지 못한 코드를 커밋하지 않다가, 세 번의 실패 뒤 2026년 9월 esxDOS가 상주한 DivMMC 에뮬레이션으로 검증할 수 있게 됨
알려진 기능 공백 은 남아 있음
창 버퍼를 버퍼 안에서 직접 합성하지 않고 화면에서 캡처하므로, 다른 창 뒤의 시계는 앞으로 가져올 때까지 마지막 표시 시간을 유지함
닫기를 거부할 수 있는 종료 처리 벡터가 없어 앱은 닫기를 거부할 수 없음
메모장 창 제목은 문서가 아니라 앱 이름을 따르며, 선택, 클립보드, 실행 취소, 자동 줄바꿈을 지원하지 않음
마우스 존재 감지는 휴리스틱이며, 마우스 없는 기기가 없어 검증하지 못함
현재 알려진 미해결 버그는 없음
마지막 버그는 인터럽트 안전 채우기로 수정했고 이후 새 버그가 확인되지 않음
ZX Spectrum Next 용 ZX Desk Next 는 이식판이 아니라 별도 시스템이 됨
저장소, esxDOS, 앱 모델, 설정 레코드 등 창 모델 아래쪽 구조는 재사용함
28MHz Z80, Layer 2, 타일맵, 하드웨어 스프라이트, DMA를 갖춘 환경에서는 타일형 데스크톱을 선택함
겹치는 창을 처리하는 합성기, 아래 화면 저장 영역, 손상 영역 처리 대부분은 타일형 창 모델에 필요하지 않음
별도 저장소에 있으며 아직 공개하지 않았고, 실제 Next 기기가 도착할 때까지 기다리는 상태임
코드는 MIT 라이선스 이며 다른 사람의 코드에서 파생하지 않았음
사용하는 도구 체인, 에뮬레이터, ROM은 함께 배포하지 않음
댓글과 토론
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기