Tak jste se po 6-ti letech dočkali…
4. 7. 2026

Když jsem začal dělat ZXDebugGUI, nechtěl jsem psát vlastní emulátor ZX Spectra. To by byla práce na roky a hlavně zbytečnost, protože Fuse (který roky používám) už emuluje Spectrum velmi dobře. Potřeboval jsem jen způsob, jak Fuse ovládat zvenku a dostat z něj stav počítače zpět do svého debuggeru.

Jak ZXDebugGUI mluví s Fuse přes STDIO

ZXDebugGUI / Fuse

Jak ZXDebugGUI mluví s Fuse přes STDIO

Když jsem začal dělat ZXDebugGUI, nechtěl jsem psát vlastní emulátor ZX Spectra. To by byla práce na roky a hlavně zbytečnost, protože Fuse (který roky používám) už emuluje Spectrum velmi dobře. Potřeboval jsem jen způsob, jak Fuse ovládat zvenku a dostat z něj stav počítače zpět do svého debuggeru.

Fuse je normálně samostatná aplikace. Spustíš ji, otevře se okno, v něm běží Spectrum a ovládáš to přes menu a klávesnici. Jenže já jsem potřeboval něco jiného. Chtěl jsem mít vlastní okno s registry, pamětí, disassembly, breakpointy a obrazovkou, ale samotné emulování nechat na Fuse. Takže vznikla jednoduchá dohoda: ZXDebugGUI bude ovládací panel a Fuse bude motor pod kapotou. GUI mu pošle příkaz, Fuse ho provede a pošle zpět, co se ve Spectru změnilo.

ZXDebugGUI
krátký textový příkaz
Fuse

Příklad: kliknu v GUI na Step. GUI pošle Fuse „udělej jednu instrukci“. Fuse ji provede a odpoví „procesor teď stojí na téhle adrese, registry mají tyhle hodnoty, obrazovka vypadá takhle“.

Co je STDIO

STDIO je obyčejný standardní vstup a výstup programu. Nic magického. Program něco čte ze vstupu a něco píše na výstup. To je celé. Mohl jsem vše řešit přes sockets, named pipes nebo nějaké větší API, ale toto řešení mi přišlo funkční a popravdě i nejlepší. Spustím proces, zapíšu mu řádek textu, přečtu řádek odpovědi. Když se něco pokazí, dá se to pořád ještě rozumně ladit.

Jak vypadá zpráva

Příkazy jsou JSON. Ne proto, že by to bylo módní, ale protože se to dobře skládá, dobře čte a .NET i C s tím umí rozumně pracovat. Jeden řádek znamená jeden příkaz.

{"id":1,"command":"step"}

Tohle je příkaz číslo 1 a říká Fuse: proveď step. Fuse odpoví taky jedním řádkem. V odpovědi je stejné číslo, aby GUI vědělo, k čemu odpověď patří.

Snapshot je stav Spectra v jednom okamžiku. Registry, kus paměti, disassembly, obrazovka, stav pásky, breakpointy. Prostě všechno, co GUI potřebuje překreslit.

{
  "id": 1,
  "success": true,
  "snapshot": {
    "machine": "ZX Spectrum 128K",
    "backendStatus": "Paging: RAM page 0/7, ROM 0, screen 5, lock off"
  }
}
Vypadá to jednoduše, ale FUSE se muselo "trošku" přiohnout...

Samotný Fuse takhle zvenku ovládat nejde. Je to emulátor s vlastním životem. Má okno, menu, vlastní smyčku a čeká, že s ním bude pracovat uživatel. Já jsem ale potřeboval, aby čekal na příkazy z mého programu. Proto jsem do Fuse přidal nový režim. Zapíná se parametrem --zxdebug-stdio. Když ten parametr nepoužiješ, Fuse se chová normálně (proto je povinný a z nastavení ZXDebugGUI nejde smazat).

fuse.exe --zxdebug-stdio
Co jsem ve Fuse ohnul

Nakonec se ukázalo, že to není tak jednoduché, jak jsem si plánoval. Fuse bylo potřeba na několika místech trochu přesvědčit, že teď nebude hlavní hvězda s vlastním oknem, ale backend pro debugger. Proto jsem na ZXDebugGUI pracoval a pak ho dal do šuplíku - nezdá se to, ale ale práce to byla opravdu úmorná...

Co GUI Fuse posílá

Žádné romány. Většinou krátký příkaz, který odpovídá tomu, co jsem právě kliknul v GUI.

Příkaz Co tím myslím
connect Připoj se a pošli první stav.
step Proveď jednu instrukci procesoru a zastav.
continue Běž dál, ale vrať řízení GUI.
pause Zastav emulaci.
reset Resetuj Spectrum.
triggerNmi Pošli do Spectra NMI.
loadFile Načti snapshot, pásku, disk nebo ROM.
keyDown / keyUp Stiskni nebo pusť emulovanou klávesu.
setRegister Nastav hodnotu v procesoru, třeba SP, HL, nebo PC.
writeMemoryByte Zapiš jeden bajt do paměti.
toggleBreakpoint Přidej nebo zruš breakpoint.
Co je snapshot

Fuse po příkazu neposílá jen „OK“, ve většině případů Fuse pošle snapshot, tedy aktuální stav emulovaného Spectra.

Registry

Hodnoty uvnitř procesoru Z80, například AF, BC, DE, HL, IX, IY a SP.

PC

Ukazatel, kde procesor právě stojí v paměti. Podle něj GUI ví, kterou instrukci má v disassembly zvýraznit.

Paměť

Řádky paměti, které vidím v memory panelu, včetně bank u 128K strojů.

Disassembly

Bajty v paměti přečtené jako instrukce procesoru.

Obrazovka

Obsah obrazovky, ze kterého GUI složí náhled.

Ne každá odpověď posílá všechno. Když jen posunu paměťové okno, nepotřebuju znovu posílat celou obrazovku.

Breakpointy

Breakpoint je ...... (chvilka napětí) ....... zastavení běhu programu. Já vím, že to víte, musel jsem to napsat... :)

Typ Kdy zastaví
Execute Když procesor doběhne na zadanou adresu. Tohle je ten klasický puntík v disassembly.
Run to Dočasný execute breakpoint. Zadám adresu, pustím běh a po zastavení se zase uklidí.
Podmíněný Taky stojí na adrese, ale zastaví jen při splněné podmínce. Třeba až procesor dojde na $8000 a zároveň BC=$1234.
Memory write Zastaví ve chvíli, kdy program zapisuje na určitou adresu v paměti. Dá se ještě zpřesnit na konkrétní zapisovaný byte.
Frame Zastaví na začátku nového video framu. Hodí se, když řeším časování obrazu a nechci lovit místo podle adresy v kódu.
Scanline Zastaví při kreslení další viditelné řádky obrazovky. Tohle je užitečné hlavně u raster efektů, multicoloru a podobných věcí, kde rozhoduje přesné místo na obrazovce.

Execute, podmíněné a memory-write breakpointy mají konkrétní adresu (může být definována i návěstím), takže je umím ukázat přímo v disassembly. Frame a scanline breakpointy jsou jiné. Ty nejsou navázané na instrukci, ale na časování obrazu, takže je GUI ukazuje v seznamu breakpointů a v menu.

Port $7FFD

U 128K Spectra se přes port $7FFD přepínají paměťové banky, ROM a obrazovka. Samotné číslo na portu není moc přátelské, takže GUI ukazuje i rozpad na jednotlivé bity. Hned vidím, která RAM banka je připojená, která ROM je aktivní a jestli je paging zamčený.

Bit Co říká
0-2 Která RAM banka je připojená nahoře v paměti.
3 Která obrazovka se používá: screen 5 nebo screen 7.
4 Která ROM je vybraná.
5 Zámek stránkování. Když se zapne, běžně už nejde stránkování měnit až do resetu.
6-7 Nepoužité bity.
Funguje to?

Ano, podle mne velmi dobře, ale je mi jasné, že vypuštěním do světa se na problémy narazí. Ale např. dnes jsem řešil (po vydání NextPlayeru pro klasické ZX Spectrum 128k s EsxDosem), že se tento program špatně spustí, když ho spouštíte přes .tapein. Vzal jsem ZXDebugGUI, v nastavení zvolil parametry, aby mi interní FUSE startovalo s EsxDosem, dal breakpoint na adresu od které se spouští NextPlayer a snažil se přijít na to, včem byl zakopaný pes (po chvilce jsem zjistil, že je nastránkovaná špatná ROM a tak jakékoliv volání RST $08 končili velmi špatně)... ano, nevyužil jsem veškeré možnosti ZXDebugGUI, ale nahrání LST souboru, použití RUN TO breakpointu a zobrazení portu $7FFD s vysvětlením, že je nastránkovaná druhá ROM jsem problém celkem rychle odhalil... ;) Ještě si budu chvilku se ZXDebugGUI hrát a pak ho vypustím ven....

Snímek obrazovky 2026-07-05 000235

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *