ZX Desk: Giao diện đồ họa desktop trên chiếc ZX Spectrum 48K

Công nghệ19 tháng 9, 2026·9 phút đọc

Một lập trình viên đã hoàn thành giấc mơ thời thơ ấu bằng cách viết hệ điều hành đồ họa với cửa sổ chồng nhau, menu, heap và trình quản lý tệp cho ZX Spectrum 48K chỉ với 48KB RAM. Dự án được viết hoàn toàn bằng hợp ngữ Z80 và chạy được trên máy thật, kèm theo những số đo hiệu năng cực kỳ chi tiết.

ZX Desk: Giao diện đồ họa desktop trên chiếc ZX Spectrum 48K

Năm 1982, chiếc ZX Spectrum 48K ra đời với bộ vi xử lý Z80 chạy ở tốc độ 3,5 MHz và vỏn vẹn 48KB RAM. Vậy mà mới đây, một lập trình viên đã chứng minh rằng cỗ máy tưởng như chỉ để chơi game và gõ BASIC ấy hoàn toàn có thể chạy một giao diện desktop đồ họa đúng nghĩa.

Dự án có tên ZX Desk — một môi trường desktop trọn vẹn với cửa sổ chồng lên nhau, thanh menu cố định, heap bộ nhớ thực thụ, hàng đợi sự kiện, trình quản lý tệp hai khung và cả ứng dụng ghi chú, đồng hồ, lịch. Tất cả được viết bằng hợp ngữ Z80 và chạy trên máy thật, không chỉ trên trình giả lập.

Vì sao lại là ZX Spectrum?

Tác giả kể lại rằng hồi thập niên 80, ông khao khát một chiếc Atari ST để được dùng GEM — hệ điều hành đồ họa với desktop, cửa sổ và thanh menu luôn hiện diện, mang lại cảm giác "cỗ máy là một nơi chốn" chứ không chỉ là dấu nhắc lệnh. Nhưng ông chỉ có ZX Spectrum, và đã dành rất nhiều thời gian tự hỏi liệu có thể làm được bao nhiêu phần của GEM trên cỗ máy khiêm tốn ấy.

Dự án được viết đi viết lại nhiều lần rồi bỏ dở, và lần này là phiên bản hoàn chỉnh. Đây không phải một bản port của GEM, mà là kết quả khi ta đẩy ý tưởng ấy lên đối đầu với Z80 3,5 MHz, 48KB RAM, màn hình một bit với hiện tượng attribute clash (xung đột thuộc tính màu), và một chip video liên tục "ăn cắp" chu kỳ CPU trong lúc vẽ hình.

ZX Desk làm được những gì?

ZX Desk hiện tại cung cấp khá đầy đủ các thành phần của một desktop hiện đại:

  • Cửa sổ: chồng lên nhau, có thứ tự z, di chuyển được, thay đổi kích thước được, kèm thanh tiêu đề, nút đóng và tay nắm góc.
  • Menu: thanh menu thường trực với menu thả xuống, hỗ trợ lưu trữ và kiểm tra vùng nhấn.
  • Nhập liệu: hỗ trợ chuột Kempston, cần điều khiển Kempston và toàn bộ ma trận bàn phím, tất cả đều đến dưới dạng sự kiện.
  • Sự kiện: vòng đệm 16 ô, vòng lặp chính không chứa mã lệnh riêng cho từng cửa sổ.
  • Lưu trữ: hệ thống backend có thể hoán đổi — RAM, băng từ (qua ROM loader thật) và RAM disk tận dụng các bank dự phòng của máy 128K.
  • Bộ nhớ: heap thực thụ với byte chủ sở hữu, dung lượng 8.112 byte.
  • Ứng dụng: mỗi ứng dụng có descriptor với các hàm khởi tạo, xử lý sự kiện và vẽ, kèm trạng thái riêng theo từng phiên bản.
  • Ghi nhớ cài đặt: cấu hình được ghi vào bộ lưu trữ với magic byte và số phiên bản, đọc lại khi khởi động.

Hai cửa sổ xếp lớp theo thứ tự zHai cửa sổ xếp lớp theo thứ tự z

Trình quản lý tệp hai khung hoạt động trên các thiết bịTrình quản lý tệp hai khung hoạt động trên các thiết bị

Những con số đáng giá hơn cả dự án

Điểm khiến bài viết này trở nên đặc biệt không nằm ở việc ZX Desk tồn tại, mà ở chỗ tác giả đã đo đạc tỉ mỉ mọi thứ. Ông thẳng thắn cho rằng: "Nếu bạn đang xây dựng thứ gì đó trên phần cứng này, những con số dưới đây đã tiêu tốn của tôi rất nhiều buổi tối. Chúng là của bạn."

Chu kỳ ngắt không phải 50 Hz

Chu kỳ ngắt của máy 48K là chính xác 69.888 T state ở tốc độ 3,5 MHz, tức tần số 50,0801 Hz chứ không phải đúng 50 Hz. Nếu cứ đếm 50 ngắt là một giây thì mỗi ngày đồng hồ sẽ chạy sai khoảng hai phút mười tám giây. Giải pháp là thỉnh thoảng đếm 51 ngắt, dựa trên việc tích lũy phần trăm giây dư. Sau khi chạy thử một giờ, sai số chỉ còn nửa giây mỗi ngày.

Chi phí contention chỉ 14,7%, không phải 50%

Mọi ngân sách thời gian trong dự án ban đầu đều dựa trên định kiến rằng ghi vào vùng màn hình bị phạt 50% do ULA tranh chấp chu kỳ. Nhưng khi đo thực tế bằng cách quét một vùng đệm 1.024 byte qua khung hình, con số thực chỉ là 14,7%, tương đương khoảng 1,7 T state cho mỗi byte bị tranh chấp. Điều này có nghĩa là các ước tính cũ đã bi quan hơn thực tế khoảng một phần ba.

Một nửa chi phí vẽ cửa sổ nằm ở văn bản

Tác giả chia hàm vẽ cửa sổ thành bốn giai đoạn và đo riêng từng phần. Kết quả gây bất ngờ: văn bản chiếm 50% tổng thời gian, trong khi phần tô nền — thứ mà ông tưởng là nguyên nhân chính — chỉ chiếm 21%. Việc tối ưu phần tô nền sẽ chỉ tiết kiệm được vài phần trăm mỗi khung hình kéo cửa sổ.

Ngắt bị mất, không phải bị hoãn

Đây là phát hiện quan trọng nhất mà tác giả muốn chia sẻ. Trong các thao tác tô nền, thanh ghi SP bị "mượn" để duyệt qua bộ nhớ màn hình, buộc phải tắt ngắt bằng lệnh DI. Quan niệm phổ biến cho rằng điều này chỉ hoãn ngắt lại. Nhưng thực tế, ZX Spectrum chỉ giữ tín hiệu INT trong 32 T state rồi rút lại, nên một cửa sổ DI đủ dài sẽ hủy luôn ngắt thay vì hoãn nó.

Cách khắc phục không phải là rút ngắn vùng DI, mà là chuyển sang kỹ thuật "nợ lần push cuối": chuỗi push được làm ngắn hơn một bước so với hàng, hai byte ngoài cùng bên trái được trả lại ở lần lặp sau, khi SP đã di chuyển sang hàng kế tiếp và không thể chạm tới chúng nữa. Kết quả: sau khi sửa, 0 trong 6070 trong 904 ranh giới lệnh phá hủy ngắt.

Đưa thao tác kéo cửa sổ vào trong một khung hình

Điểm xuất phát khá bi đát: xóa và vẽ lại toàn bộ một cửa sổ tốn 96.010 T state, trong khi một khung hình chỉ có 69.888 T state — tức 137% một khung, khiến thao tác kéo chỉ chạy được ở 25 Hz. Bốn thay đổi theo thứ tự đã giải quyết bài toán:

  • Bộ đệm ngoài màn hình: vẽ một lần vào đệm rồi blit mỗi khung — giảm từ 71.274 T xuống 26.048 T.
  • Kết xuất văn bản nhanh hơn: viết lại bộ kết xuất pixel — từ 1.307 T xuống 622 T mỗi ký tự, nhanh gấp 2,10 lần.
  • Hình chữ nhật thiệt hại: chỉ xóa dải mà cửa sổ đã rời đi — giảm từ 19.664 T xuống 1.456 T.
  • Tô nền desktop hẹp: hai byte đi thẳng qua HL với mẫu giữ trong thanh ghi — giảm từ 14.256 T xuống 5.712 T.

Kết quả cuối cùng: một khung kéo cửa sổ kết thúc ở 59.858 T state, nằm gọn trong khung hình, và thao tác kéo chạy mượt ở 50 Hz.

Những bài học lập trình đáng nhớ

Tác giả dành hẳn một phần để liệt kê các lỗi đáng ghi nhớ, với tinh thần "mọi phát hiện thực sự trong dự án này đều đến từ việc chạy mã, không phải đọc mã". Một số bài học nổi bật:

  • Thanh ghi mang kết quả không được là thanh ghi mang tham số. Trên Z80, cờ hiệu cũng là một thanh ghi. Nếu một giá trị cần sống sót qua lời gọi hàm, nó phải nằm trong bộ nhớ hoặc trên stack.
  • Lỗi tự sửa chữa trước mắt. Hàm giải phóng heap có một lệnh DEC HL thừa, nhưng lỗi không lộ ra vì bộ gom cụm vô tình đọc phải vùng dữ liệu toàn số không và "chữa lành" luôn. Nó chỉ xuất hiện khi một khối đủ lớn chứa pixel cửa sổ được giải phóng lần đầu.
  • Một con số đọc ngược. Vì thời gian được đo bằng cách đếm vòng lặp 16 T cho đến ngắt kế tiếp, một thói quen càng chậm thì để lại càng ít vòng. Điều này khiến tác giả đọc sai hai lần trong cùng một phiên, có lần báo cáo một khoản tiết kiệm 16 T thành mất 1 T, và một chi phí 1.216 T thành tiết kiệm 76 T.

Giới hạn hiện tại

ZX Desk vẫn còn vài khoảng trống đã biết: đồng hồ nằm sau một cửa sổ khác sẽ giữ nguyên thời gian cũ cho đến khi được đưa lên trên, vì bộ đệm cửa sổ được lấy trực tiếp từ màn hình thay vì được tổng hợp. Ứng dụng chưa thể từ chối việc đóng, trình ghi chú chưa có chọn vùng, clipboard, hoàn tác hay tự xuống dòng. Tuy nhiên, tác giả khẳng định không còn lỗi đang hoạt động nào — lỗi cuối cùng đã được đóng lại cùng với kỹ thuật tô nền an toàn với ngắt.

Tương lai: ZX Spectrum Next

Hệ thống 48K được xây dựng với ý định port sang ZX Spectrum Next ngay từ commit đầu tiên. Ranh giới tầng thiết bị đã trụ vững cho phần lưu trữ, esxDOS, mô hình ứng dụng và bản ghi cài đặt. Nhưng nó không trụ được cho mô hình cửa sổ, bởi Next dùng desktop dạng xếp ô (tiling) chứ không chồng lớp — nghĩa là compositor, arena lưu dưới và phần lớn cơ chế thiệt hại trở nên vô dụng. Vì vậy ZX Desk Next là một hệ thống thứ hai, sẽ được công bố riêng khi tác giả có được một chiếc Next thật trong tay.

Lời kết

"Gửi đến một cỗ máy chưa bao giờ được thiết kế để làm bất kỳ điều gì trong số này, và nó vẫn làm được."

ZX Desk là minh chứng cho thấy giới hạn phần cứng không phải lúc nào cũng là rào cản tuyệt đối — đôi khi nó chỉ là một bài toán chưa được đo đạc đúng cách. Với những ai quan tâm đến lập trình nhúng, hợp ngữ Z80 hay đơn giản là tình yêu với phần cứng cổ điển, dự án này là một kho tài liệu quý giá. Mã nguồn được phát hành theo giấy phép MIT trên GitHub.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗