プログラミング
ZX Spectrumのためのグラフィカルデスクトップ
A graphical desktop for the ZX Spectrum (github.com)
要約
Z80アセンブリで書かれたZX Spectrum 48K用のグラフィカルデスクトップ環境「ZX Desk」が開発されました。このプロジェクトは、1980年代に高価で手に入れられなかったAtari STのGEMデスクトップに触発されており、オーバーラッピングウィンドウ、プルダウンメニュー、イベントキュー、ストレージレイヤーなどを48KBのメモリ内に収めることに成功しています。
全文翻訳
ZX Spectrumのためのグラフィカルデスクトップ
ZX Desk
ZX Spectrum 48Kのためのグラフィカルデスクトップ。Z80アセンブリで書かれています。Zオーダーとフォーカスを持つオーバーラッピングウィンドウ、プルダウンメニュー、ヒープ、イベントキュー、スワップ可能なバックエンドを持つストレージレイヤー、ダイアログ、コントロール、メモ帳、時計、カレンダー、2ペインのファイルマネージャー、そして実際に設定を変更できる設定パネルを備えています。これらすべてが1982年のマシンで48KBに収まり、単一の69,888 Tステートフレーム内でウィンドウをドラッグできます。エミュレータだけでなく、実機でも動作します。
なぜ?
1980年代、私はAtari STを欲しかったのですが、買えませんでした。私が本当に欲しかったのはGEMでした。デスクトップ、ウィンドウ、常にそこにあるメニューバー、プロンプトではなくマシンが場所であるという感覚です。代わりにSpectrumを持っていましたが、それがどれだけできるのか長い間疑問に思っていました。その一部を書き始めましたが、完成しませんでした。だから、これは完成したものです。GEMのポートではなく、そうだと主張するものでもありません。3.5MHzのZ80、48KBのRAM、アトリビュートクラッシュのある1ビットディスプレイ、そしてCPUからサイクルを奪って描画するビデオチップに押し付けたときに、そのアイデアがどうなるかということです。多くの答えは質問よりも興味深いものになり、ほとんどすべてが推論からではなく、マシンを測定することから得られました。これは楽しいプロジェクトであり、愛情のこもった労働であり、これだけの長さで書かれている理由は、測定値が役立つ部分だからです。このハードウェアで何かを構築しているなら、以下の数値は私に多くの夜を費やさせました。それらはあなたのものです。
今日の機能
ウィンドウ:オーバーラッピング、Zオーダー、移動可能、リサイズ可能、タイトルバー、クローズボックス、グリップ付き。フォーカスはZオーダーの前面であり、レイズとフォーカスは1つのアクションです。
メニュー:プルダウン、セーブアンダー、ヒットテストを備えたパーマネントメニューバー。
入力:Kempstonマウス、Kempstonジョイスティック、および3つのテーブル全体でデコードされたフルキーボードマトリックス(リピート付き)。すべてイベントとして到着します。
イベント:16スロットのリング。メインループにはウィンドウ固有のコードは含まれていません。
ストレージ:6つのベクターの後ろにあるバックエンドのレジストリ。RAM、テープ(実際のROMローダー経由)、および128KのスペアバンクをRAMディスクとして使用。esxDOSには予約済みIDがあります。
メモリ:オーナーバイト付きのリアルヒープ、8,112バイト、ウィンドウバッファをウィンドウサイズに合わせて割り当てます。
アプリケーション:init、event、paintを備えたディスクリプタ、およびインスタンスごとの状態をスワップイン・アウトします。メモ帳、時計、カレンダー、コマンダー、アバウト。
永続性:マジックバイトとバージョンをストレージに書き込み、起動時に読み戻します。
スクリーンショット:
2つのウィンドウ、Zオーダー
メモ帳、シンクレアスタイルのシフトレポートカーソル付き
2ペインのコマンダー、ディレクトリではなくデバイスの上に表示
時計とカレンダー
実行方法
ツールチェーンはローカルで小さく、pasmo 0.5.5で、ソースからtools/にビルドされます。
./build.sh assemble src/zxdesk.asm をビルド/zxdesk.tap にビルドします。
./run.sh build を実行し、マシンにロードします。
MACHINE=128 ./run.sh 128Kで同じことを行い、設定を元に戻します。
esxDOSもここで実行されます。エミュレートされたDivMMCと64MBカードイメージを使用します。tools/はリポジトリに含まれていません。pasmo、エミュレータ、esxDOS ROMは私が再配布できるものではないため、esxDOSリリースとDivMMCカードイメージから自分でイメージをビルドする必要があります。それが存在したら、tools/esxdos/esxdos.szxを起動すると、esxDOSは既に常駐しています。Machine > NMIでファイルブラウザが表示されます。
ビルドフラグはすべて渡されます --equ:
DEMO=1 ./build.sh 再現可能なキャプチャのためのセルフドラッグビルド
NOWAIT=1 DEMOと組み合わせて、ビームスケジューラなしの同じドラッグ
SCRIPT=1 ./build.sh 合成入力からデスクトップを駆動します
MOUSETEST=1 ./build.sh 生のKempstonマウス診断
build.shは、pasmoがテープヘッダー名をパスからそのまま取得し、そうしないとヘッダーにbuild/zxdeと入力してしまうため、--nameを明示的に渡す必要があります。また、コードがバッファ領域にまで成長した場合、テープのビルドを拒否します。なぜなら、そのオーバーランはサイレントになるからです。最初のウィンドウグラブはプログラムを上書きし、数秒後にマシンは無関係なエラーでBASICにドロップします。
Fuseでウィンドウをドラッグするには、マウスボタンではなくスペースバーが必要です。macOS用のFuse、1.9.2は、ボタンが押されている間Kempstonマウスの移動を停止するため、ドラッグが開始された瞬間にポインタがフリーズし、ウィンドウは決して追従しません。タイトルバーを指し、SPACEを押し、移動し、離します。マウスは他のすべてのものには問題なく、ボタン自体も正しく登録されます。移動だけが停止します。これはデスクトップではなくエミュレータであり、MOUSETEST=1はその証明方法です。ポートから画面まで何も介さずにポートを1回ずつ読み取り、ボタンが押されている間カウンターは静止したままです。ReadInputのRiBtnはSPACEを機能させるものであり、マウスを持たないマシンでもデスクトップが使用できるようにするためです。実際のマウスは通常通りドラッグします。
SCRIPT=1は知っておくべきものです。マウスではなく、合成入力イベントのリストからデスクトップを駆動するため、インタラクション(メニューを開く、項目を選択する、ウィンドウを別のウィンドウの上にドラッグする、フィールドに入力する、保存する)は毎回同じように実行され、最後の実行と比較できるため、監視するのではなく比較できます。
どのようにビルドされたか
下から上へ。
デバイスレイヤー。DevFillRect、DevFillDesk、DeskFillCol、AddrAt、BlitRect、RectGrab。すべての上位はバイト列とピクセル行で機能し、画面の3番目のインターリーブレイアウトに直接触れることはありません。これはポートがスワップアウトする境界であり、その理由で初日に描画しました。
フレーム規律。メインループは割り込みで停止し、トップボーダーでポインタ作業をすべて行い、ウィンドウが移動した場合はビームを待ち、再描画してから、フレームの最後にイベントを読み取ってディスパッチします。入力は最後に行われるため、ビーム待機前のコストは一定であり、それがスケジューラを正確にします。
イベントキュー。4バイトイベントの16スロットリング。EvPollは生の入力をポインタ移動、ボタンプレス、キーに変換します。EvDispatchはハンドラテーブルを通してそれをドレインします。
ヒットテスト。Zオーダーで前面から背面へ、$FFで終端された、コントロールごとに5バイトの行。検索前にモデルから再充填されるため、ウィンドウ位置の2番目のコピーが古くなることはありません。閉じられたメニューは高さゼロになり、決して一致しないため、HitTestには特別なケースはありません。
一時的なサーフェス。4つの深いアリーナ、各サーフェスは最大16x96で、プッシュおよびポップされます。2つのスタックではなく1つなのは、メニューの下のピクセルがパネルレコードを全く持たないものによってプッシュされるためです。
ウィンドウ。レコードはライブコピーにスワップされ、パネルレコードと同じトリックです。ウィンドウ位置は6つのファイルにわたって93回参照されるためです。Zオーダーはペイントオーダーの逆であり、ヒットテストオーダーでもあります。
ストレージ。バックエンドのレジストリ、各バックエンドはID、機能ビット、および6つのエントリポイントを持つ14バイトの行です。6つの操作は手作業で配置されたJP命令であり、オペランドは選択時にパッチされるため、ディスパッチコストは10 Tステートでレジスタを破壊しません。
コントロール。パネルは行のスタックであり、各コントロールは行です。したがって、行のインデックスは検索ではなく3つのシフトです。4つのタイプがあり、有用なのはサイクルです。チェックボックスは制限が2のサイクルであり、ラジオグループは制限がNのサイクルです。設定パネル、ファイルリスト、セーブボックスはすべて同じコードで、異なるテーブルを使用します。
メモリマップ
$6000-$727C スロー領域:パネル、カレンダー、ファイルパネル、デスクトップセットアップ、コマンダー
$727D-$7FFF 空き、3,460バイト、競合
$8000-$B1B7 ファスト領域:その他すべて
$B1B8-$BCFF 空き、2,889バイト
$BD00 スタックトップ
$BDBD 割り込みハンドラ
$BE00-$BEFF 割り込みベクタテーブル
$C000-$C63F RAMディスク、そのディレクトリ、およびテープバッファ
$C640-$C74F ライブメモ帳状態
$C750-$DF4F 4つの一時サーフェス
$DF50-$FEFF ヒープ、8,112バイト
$FF00-$FFFF 意図的に未使用
このマップで説明に値するものは2つあります。スロー領域は、ULAがディスプレイ描画中に$8000未満のサイクルを盗むため、そこにあるコードは約3分の1遅く実行されます。そのためのルールは1行です。その中の何もフレーム内で実行されてはなりません。