Commodore blog

Nel gruppo Facebook Commodore Plus/4 Ungheria ho già accennato brevemente di aver iniziato a lavorare al port di The Pit per Plus/4.

A mo' di introduzione, cito anche quanto scritto lì:

A cosa servono i suoni che ho menzionato nel post precedente? Per uno dei miei grandi preferiti, che fu il mio primo incontro con i videogiochi, se non contiamo il Videoton TV Tennis.

Nella metà degli anni '80, nel circolo ufficiali del complesso residenziale militare (!!!) c'era, per qualche motivo, un arcade a gettoni che eseguiva The Pit. Innumerevoli monete da 5 fiorini Kossuth venivano gettate dentro, finché non scoprimmo che la chiave della "sala", che doveva essere richiesta separatamente se si voleva giocare, apriva anche la cassa della macchina.

Ovviamente era interessante soprattutto finché non ebbi un computer a casa, poiché il gameplay continuamente ripetitivo, sebbene accelerasse col tempo, non era neanche lontanamente eccitante come aspettare che il gioco del momento si caricasse davanti al registratore a nastro.

Comunque, in me lasciò un'impronta profonda, e mi tornò in mente quando, di recente, scoprii che Doug Turner, il creatore di Icicle Works (e altri capolavori minerari), circa cinque anni fa pubblicò il nuovo rilascio: The Pit.


Che, tra l'altro, è un gioco completamente diverso, con competenze tecniche e standard completamente differenti, ma c'è comunque un parallelo diretto. L'ispirazione di Doug, il suo stesso Icicle Works, è un clone a interpretazione libera di Boulder Dash, a tema natalizio-invernale. L'ispirazione per Boulder Dash, secondo il creatore Peter Liepa, fu un programma BASIC di Chris Gray, non sopravvissuto ai giorni nostri. Tuttavia, neanche il programma di Chris era un'idea originale: nacque in una sala giochi, giocando su un arcade. Quella macchina era The Pit.

Vedete l'analogia? Vediamo l'avventura sotterranea in sezione, come in una formicaio, le pietre ci cadono in testa, eccetera, eccetera...

Che bel cerchio, no?

Così, The Pit mi ha spinto a creare il port di The Pit per Plus/4. Non è un compito che mette davvero alla prova l'uomo e la tecnica, ma considerando il tempo che ho per queste cose, e quanto devo riabituarmi alla codifica, per me è il compito perfetto.

Ho anche allegato tre video, per chi non conoscesse il gioco. Il primo è la versione Arcade originale, il secondo il port per C64, il terzo il futuro port per Plus/4. Quest'ultimo è solo un assaggio, perché il seguito è ancora molto grezzo.

 

 

 

 

Comunque, illustra bene che inserire suoni digitali sarebbe una forte esagerazione (a meno che non digitalizziamo un'onda quadra, ma a che pro, giusto?), e non servono nemmeno troppi effetti sonori, ecco perché sto ancora scervellandomi per risolvere questo aspetto (nel breve estratto si sente TEDZakker).

Un altro motivo per cui cerco una soluzione efficiente: voglio restare entro i 16K. La versione originale per C64 era su cartuccia, anche la versione craccata gira al posto della cartuccia, sta comodamente in 16K, con tanto spazio vuoto.

Ah, e quale sarà il titolo? Ovviamente The Pit, poiché è anche l'originale, ma inventerò qualcosa per distinguerlo. Diciamo The Pit Arcade, o simile.


Anche se ho relativamente poco tempo da dedicarci (come di solito accade da adulti), il progetto procede bene, probabilmente sarà pronto in un futuro prevedibile.

Il motivo per cui è diventato un post del blog è che, a causa della sua relativa semplicità, sono emersi molti problemi speciali da risolvere, anche se in parte me li sono causati da solo.

Prima di tutto, il programma originale per C64 (almeno quello che mi è capitato) è stato craccato da una cartuccia, e sta in 16K con ancora molto spazio avanzato. Quindi uno degli obiettivi è stato far sì che anche la versione per Plus/4 stia in 16K, anzi, che giri su C16 (i due non sono la stessa cosa!).

Secondo: nell'originale c'è un sacco di sprite (elementi che girano via hardware, indipendenti dallo sfondo, come il giocatore, i nemici, ecc...), questi sul Plus/4 possono essere gestiti solo via software, il che è molto dispendioso in termini di risorse. Tuttavia, la caratteristica del gioco è che la maggior parte di questi sono piccoli (1x1 carattere), e incontrano relativamente raramente lo sfondo o l'un l'altro, quindi il mascheramento degli sprite (la "pettinatura" dello sprite e dello sfondo l'uno sull'altro) è quasi completamente eliminabile.

Per questo, fin dall'inizio, è stato stabilito che gli sprite funzioneranno come segue:

1. Giocatore: figura di 1x1 carattere, che teoricamente potrebbe apparire su una maschera sprite di 2x2 caratteri. Come potremmo semplificarlo?
- rimane il movimento pixel per pixel, ma può fermarsi solo sui bordi dei caratteri. Questo facilita un po' il gioco, poiché nell'originale era particolarmente frustrante centrare i bordi dei pixel.
- Il giocatore, se si muove nella sabbia, scava sempre un'area di un carattere davanti a sé - per questo, però, si muove sempre su un luogo già scavato e vuoto. Quindi non incontra lo sfondo, non c'è bisogno di mascheramento.
- Se raccoglie un tesoro, di nuovo non c'è bisogno di mascheramento - poiché lo raccoglie, lasciando anche lì solo spazio vuoto.
- Se collide con un nemico, non c'è bisogno di mascheramento, poiché MORTE! MORTE! MORTE!, che nell'originale ha anche una strana animazione di aggrovigliamento. Questo può essere prodotto da fasi fisse, inoltre entro i bordi dei caratteri.
- Non deve mascherare il proiettile, poiché lo spara davanti a sé, e questo si allontana da lui.
- Se la roccia gli cade addosso, la situazione è la stessa che con il nemico, niente di extra da fare.
- Inoltre, il nostro giocatore non si muove in diagonale, avanza solo in direzione verticale O orizzontale, non è possibile entrambe contemporaneamente.

Per i motivi di cui sopra, il movimento del giocatore e la sua visualizzazione si semplificano significativamente. Praticamente deve muoversi davanti a spazio vuoto, potrebbe anche apparire per caratteri, ma così il suo movimento sarebbe molto a scatti. Per il movimento pixel per pixel è nata la seguente soluzione:
- Se il giocatore è entro i bordi di un carattere, semplicemente lo mettiamo come un carattere, in cui copiamo la fase di animazione attuale.
- Se si muove lateralmente, dalla posizione attuale SINISTRA no, DESTRA ottiene un carattere aggiuntivo, in questi due lo spostiamo lateralmente di 1-7 pixel, l'8° avviene già un carattere più in là.
- Nel movimento su-giù accade lo stesso, ma dalla posizione attuale una riga più in basso esce il secondo carattere.
- Con questo non sovrascriviamo lo sfondo, perché prima del movimento controlliamo cosa c'è nelle quattro direzioni possibili, e permettiamo di proseguire solo se c'è spazio vuoto. Se in una direzione c'è sabbia come ostacolo, ma il giocatore si muoverebbe, scaviamo un po' - dopo di che anche lì ci sarà spazio vuoto, può proseguire.
- Poiché l'area di gioco, tranne l'ingresso, è circondata da ostacoli non percorribili, per i motivi precedenti non è necessario controllare la posizione minima e massima percorribile, poiché gli ostacoli fermano il giocatore prima che superi l'area percorribile. L'ingresso (e l'uscita vicino al carro armato) sono circondati da caratteri $00, che sono esattamente vuoti come i caratteri percorribili $20, ma bloccano l'avanzamento del giocatore.

Un bonus speciale è la configurazione dell'area di gioco, per cui il lato destro dello schermo non è percorribile dal giocatore. Una piccola spiegazione su questo:

Lo schermo del Commodore Plus/4 è di 40 caratteri per 25 righe, in pixel 320x200. La posizione verticale (0-199, o esadecimale $00-$C7) può essere descritta in un byte, quella orizzontale invece no, il suo valore (se l'intero schermo è percorribile) può essere $0000-$013F, che può essere memorizzato solo in due byte. Anche se la posizione dello sprite non può essere $013F / $C7, poiché allora sarebbe visibile solo un pixel nell'angolo in basso a sinistra.

Ma nel caso di The Pit non c'è questo problema, usando la mappa originale il giocatore (e quindi anche i nemici), incluso il suo size, può andare al massimo fino alla posizione $D0 / $B8. Questo può essere memorizzato anche in un byte. Questa differenza non sembra significativa, ma così risparmiamo memoria con il programma più corto, e tempo di processore con calcoli inutili del +1 byte.

2. Nemici: quasi tutto ciò che vale per il giocatore vale anche per loro. Bonus aggiuntivo, poiché si incontrano molto raramente tra loro, non è necessario occuparsene approfonditamente. Collidendo tra loro semplicemente si sovrascriveranno, cosa che nel fervore del gioco non sarà evidente, quindi nemmeno fastidiosa.

3. Proiettili: ci sono due tipi di proiettili, quello del giocatore e quello del carro armato. Quest'ultimo sarà una visualizzazione a caratteri semplice, non richiede di più. Nemmeno quello del giocatore sarà più complicato, poiché si muove davanti a uno sfondo vuoto, collidendo con un ostacolo viene distrutto (= semplicemente scompare), collidendo con un nemico scompare insieme a esso.

4. Bombe nella caverna inferiore: La loro visualizzazione è simile a quella del proiettile, inoltre si muovono in una sola direzione, quindi sono gestibili ancora più semplicemente.

5. Mostro nella vasca di acido, carro armato, UFO: non hanno un vero ruolo nel gioco. Tutti e tre si muovono in un'area minima visualizzabile con una semplice storia a caratteri. Solo il carro armato riceve un'animazione a caratteri con fase spostata di mezzo, a causa del percorso relativamente lungo e rettilineo.

Per rientrare nei 16K, non ho dovuto fare grandi sforzi, poiché il gioco stesso non è troppo complicato. 

  • Invece del set completo di caratteri disponibile, ne uso solo metà, è più che sufficiente per la visualizzazione dello schermo.

 

Il set di caratteri di The Pit
Il set di caratteri di The Pit allo stato attuale. Non è quello finale, le fasi di animazione ne verranno ancora rimosse.

(Per la grafica, fondamentalmente ho preso come base la versione C-64, ma alcuni dettagli li ho presi dalla versione originale da sala giochi)

  • Anche i suoni non sono troppo complicati nella versione originale da sala giochi, quindi possono essere prodotti abbastanza facilmente e in modo economico. In questo, dopo qualche ricerca, Epy mi è stato d'aiuto. Il suo player di effetti a singola voce è di circa 256 byte, a cui si aggiungono le definizioni degli effetti, che richiederanno uno spazio di dimensioni simili, quindi il suono occupa solo circa mezzo kilobyte di memoria. Il player di Epy, tra l'altro, l'ho riutilizzato quasi invariato, ho solo modificato che un dato effetto possa essere loopato (ripetuto). Così il suono del carro armato e dell'UFO è diventato descrivibile con relativamente pochi dati ripetuti.

 

Il suono dell'UFO
Il suono dell'UFO, 45 byte

 

  • Lo schermo di gioco, cioè i dati di colore e carattere, occupano in totale 2000 byte di memoria. In questi ci sono relativamente molti dati ripetuti, quindi in teoria la dimensione potrebbe essere ridotta con una semplice compressione byte. Tuttavia, la routine di decompressione necessaria occuperebbe spazio aggiuntivo in memoria, quindi la riduzione non sarebbe così significativa. Per ora c'è ancora molto spazio, se dovesse scarseggiare, ci tornerò sopra.
  • La schermata del titolo, invece, che teoricamente richiederebbe anche 2000 byte di spazio, non è memorizzata. Poiché ha un contenuto relativamente scarso, utilizzando le routine KERNAL di fabbrica viene visualizzata come contenuto testuale. Così occupa solo $015C, cioè 348 byte in decimale.
  • Le aree di memoria che contengono come variabili i vari stati, indirizzi, timer, sono state in gran parte spostate in parte sulla pagina zero ($00-$FF), in parte sull'area sotto la memoria video (sotto $0800). Queste non portano molto, ma poiché ad ogni avvio del gioco devono essere ricaricate con dati, sarebbe insensato consumare la memoria "normale" con esse, tanto meno avrebbe senso memorizzarle nel file del programma finito.
  • L'interrogazione della tastiera e del joystick avviene da una tabella. 37 byte di programma eseguono l'interrogazione, e per tasto 4-5 byte è la dimensione della tabella, in base alla quale sappiamo quale stato del tasto interrogare, e se premuto, cosa farne. Con pochi tasti questo porta addirittura a un aumento, ma se il gioco è controllabile da joystick e tastiera, più c'è pause, uscita e simili, si può già guadagnare qualche byte.
  • Per quanto possibile, uso le routine KERNAL di fabbrica. Cancellazione schermo, interrogazione matrice tastiera, scrittura su schermo - singolarmente non rappresentano sempre un serio vantaggio, ma con un uso metodico si può risparmiare abbastanza bene.
  • Da ciò consegue anche che non disattivo le ROM, nemmeno temporaneamente. Non ho bisogno della RAM sottostante, e così risparmio anche le istruzioni a tre byte STA $FF3F / STA $FF3E. Come conseguenza, nemmeno l'interrupt lo reindirizzo all'indirizzo $FFFE/FF, ma su $0314/15, risparmiando così anche il push e il pop dei registri sullo stack.
  • Anche se memorizziamo la posizione del giocatore in pixel, c'è spesso bisogno anche della sua posizione in caratteri e in pixel dentro il carattere. Dopo il movimento, queste ultime vengono calcolate e memorizzate una volta per ciclo schermo da una routine separata. Così piuttosto occupo altri quattro byte sulla pagina zero, ma non devo ricalcolarle continuamente con AND e shift di bit durante l'esecuzione.
  • Quando si scrivono gli elementi mobili sullo schermo, carico la posizione dell'elemento in una coppia di byte sulla pagina zero, e se è già lì, nello stesso ciclo uso questa coppia di byte anche per esaminare l'ambiente (ad es. se c'è un ostacolo davanti al giocatore, o se il nemico ha raggiunto il giocatore). Con questo si può evitare di ricalcolare continuamente le posizioni sullo schermo. Combinato con l'idea precedente si possono risparmiare molte risorse.
  • Ci sono innumerevoli piccole soluzioni per risparmiare byte ancora più piccoli - ad esempio, se da qualche parte devo inserire un dato, e per questo l'ho caricato in un registro, cerco già di usarlo anche per altro. Un tipico esempio è che se disattivo lo schermo, poi imposto il bordo, lo sfondo su nero, poi lo riempio di contenuto, allora basta un singolo LDY #$00, poiché posso inserirlo in sequenza in $FF06, $FF19, $FF15, e con lo stesso posso avviare anche il ciclo indicizzato con Y con cui riempio il contenuto. Questa soluzione tipicamente funziona bene dopo, poiché nel programma (quasi) finito è più facile notare cose del genere - ma pensando in anticipo, la maggior parte si può già fare nelle fasi iniziali.
  • ...e beh, uno dei modi migliori per risparmiare spazio è che invece del Monitor integrato, lavoro in un crossplatform-assembler. Così non devo impacchettare tutto su indirizzi facili da ricordare, che terminano con zero, come ai tempi classici :)

La maggior parte di quanto sopra, ovviamente, risparmia solo spazio minimo, ma per capire perché anche queste briciole sono importanti, torniamo indietro al testo sopra, apparentemente senza motivo scritto in verde - la sua dimensione è di circa 4500 caratteri, cioè circa 4.4kB.

Questa piccola parte di testo verde, ripetuta quattro volte di seguito, non ci sta già più in 16K!

Un'ultima piccolezza: durante lo sviluppo uso spesso metodi di debug tipo fai-da-te, che poi dalla versione finale possono essere facilmente e rapidamente eliminati. In questi casi, sullo schermo, nelle aree passive, irrilevanti durante l'esecuzione del programma, si possono visualizzare benissimo informazioni che aiutano il debug, o addirittura il processo di sviluppo stesso. Questi sono visibili anche in questa schermata:

The Pit Arcade Commodore Plus/4
"Modalità debug" sotto The Pit

Si vede bene da un lato la classica "misurazione del tempo di raster", il colore del bordo impostato all'inizio dell'interrupt e ripristinato alla fine, dall'altro in fondo allo schermo in riga le coordinate X e Y in pixel del giocatore, le coordinate in pixel dentro X e Y dei caratteri, le coordinate in caratteri di X e Y, il contatore degli scavi (0-7, e corrispondentemente @-G), e la direzione di movimento attuale (orizzontale, verticale o - in questo caso - nessuna delle due).

Nella lista degli High Score, al posto dei nomi, in alto ci sono gli ostacoli che circondano il giocatore nelle quattro direzioni, sotto la direzione di movimento attuale.

Oltre a ciò, qui in modalità debug il carattere $00, cioè @, menzionato sopra, vuoto ma diverso dall'area libera, non è vuoto, contiene un singolo pixel, per distinguerlo dal carattere veramente vuoto e percorribile $20 (spazio). Ecco perché si vede, ad esempio, che dall'uscita vicino all'UFO il giocatore può uscire solo verso il basso.

Inoltre, in modalità debug è disattivabile (e qui è disattivato) il movimento dei nemici, la caduta delle rocce, lo sparo del carro armato, ecc... Questo può aiutare molto quando bisogna rintracciare qualche errore, la cui fonte è ancora completamente oscura.

Tutta questa modalità debug, prima del salvataggio della versione finita, può essere commentata con alcuni ;, e durante il salvataggio può essere saltata. Nel nostro caso - proprio per l'obiettivo dei 16K si trova sopra $4000, quindi nemmeno disturba nella memoria disponibile.

 

Bene, per ora è tutto, la prossima volta - spero - mi farò vivo con The Pit finito.