Nuovo design per le cartucce: ROMBA π

Alcuni potrebbero ricordare ROMBA, una scheda che ho rilasciato quasi 10 anni fa: era una Rombox con alcune funzionalità DMA limitate. Con il passare del tempo è arrivato il COVID, il CPLD utilizzato da quella scheda è passato da meno di 2 € al pezzo a più di 10 € al pezzo, e le pochissime persone interessate al progetto hanno perso interesse.

Dato che a quanto pare i prezzi dei CPLD con un gran numero di I/O non torneranno mai a quelli di una volta, qualche tempo fa ho iniziato a pensare a un nuovo design incentrato, ancora una volta, sull’economicità di realizzazione, aggiungendo al contempo qualche caratteristica in più. Vi presento ROMBA π (o ROMBA 3.14):

Il design è basato su RP2350B, un microcontrollore «pi-pico2» al posto di un CPLD. Questi microcontrollori sono economici, hanno un sacco di pin con tolleranza a 5V e supportano fino a 16 MiB di economica memoria flash QSPI. La scheda si collega alla porta di espansione e, come accadeva con la mia vecchia ROMBA, può essere collegata direttamente tramite un connettore edge, oppure attraverso una scheda MX4 tramite un connettore DIL.

L’uso di una MCU al posto di un CPLD/FPGA per l’interfaccia con il bus parallelo, combinato con l’uso di memoria flash QSPI anziché NOR parallela, ha reso il funzionamento come Rombox una bella sfida a causa delle latenze introdotte dal polling del bus e dalla lettura dal chip QSPI, ma eccola qui!

Le funzionalità attualmente implementate sono:

  • Fino a 16 MiB (128 megabit) di capacità Flash ROM. La maggior parte è utilizzabile come memoria ROM per il CPC, a eccezione della quantità riservata al firmware (che attualmente è di circa 48 KiB, ma dovrebbe crescere man mano che verranno aggiunte nuove funzionalità).
  • Porta USB host. Al momento è implementato il supporto per il mouse, ma si potrebbero aggiungere altre periferiche USB.
  • Il supporto per il mouse include lo scaling frazionario configurabile (per regolare la sensibilità dal lato CPC), con impostazioni indipendenti per gli assi X, Y e la rotella.
  • Compatibile con le Rombox per Amstrad CPC (fino a 256 slot).
  • DMA hardware utilizzabile per copiare dati dalla ROM alla RAM interna (1 byte per microsecondo).
  • Il motore DMA può copiare in modalità lineare e in modalità 2D, permettendo di copiare tile nella memoria video senza doversi preoccupare delle stranezze del CRTC e del GA.
  • Il motore DMA in modalità 2D supporta anche il mirroring dei tile al volo per le 3 modalità grafiche.

Il supporto per le funzionalità sopra descritte è stabile, ma non considererei il firmware completo dal punto di vista delle funzionalità allo stato attuale (ci tornerò sopra tra poco). L’hardware permette di aggiungere il supporto anche per:

  • Pixel shifting per i tile.
  • Mascheramento dei pixel per i tile (attualmente implementato ma solo a livello di byte).
  • Supporto per Lower ROM.
  • Memory mapping più complesso.
  • Espansione di RAM (l’MCU dispone di 512 KiB di RAM, e la quantità non utilizzata dal firmware potrebbe essere usata come RAM per il CPC).
  • Funzionalità audio (capacità FM, riproduzione di campioni PCM).
  • Salvataggio dati su Flash.
  • Generatore programmabile di NMI.
  • Supporto DMA da RAM interna del CPC a RAM interna del CPC (attualmente è supportato solo da ROM a RAM).

Qui potete vedere il supporto per il mouse in azione:

SyX ha patchato diversi giochi per supportare il mouse e dovrei fare un video anche di alcuni di questi :grinning_face_with_smiling_eyes:. Mi ha anche aiutato moltissimo a comprendere la struttura della memoria video, passandomi snippet di codice e programmi di test.

E qui potete vedere le funzionalità del DMA 2D all’opera:

Quello sprite è di 32x64 pixel (modo 0) e il DMA impiega circa 1 ms a disegnarlo (disegna due pixel del modo 0 per microsecondo). Il motore DMA 2D esegue anche il mirroring hardware al volo e gestisce clipping dello schermo e mascheramento (ma nella versione attuale tutto solo con granularità a livello di byte, quindi due pixel in modo 0).

Sia l’hardware che il firmware sono rilasciati come Open Source. Al momento manca la documentazione su come programmarli, ma ho intenzione di aggiungerla in seguito.

Ora, riguardo all’attuale stato «incompleto» del firmware: volevo aggiungere la maggior parte (se non tutte) le funzionalità elencate sopra che non sono ancora state implementate. Sembra però che una buona fetta (forse la maggioranza?) della community CPC si interessi solo ai traguardi software e consideri queste espansioni hardware come «anti-feature», del tipo «questo non è più un CPC» e cose simili. Quindi, invece di investire ulteriori sforzi in questo progetto, sto mettendo in pausa lo sviluppo finché non vedrò un reale interesse per la scheda. Intendo qualcuno realmente interessato a sviluppare per questa piattaforma sfruttando queste caratteristiche. Se deve essere il mio secondo progetto per CPC destinato a finire inutilizzato, preferisco dedicare il mio tempo ad altri progetti hardware che possano tornare utili ad altri sviluppatori.

Quindi, voi cosa ne pensate? Trovate interessante un progetto simile? C’è qualcuno interessato a sviluppare per queste schede così economiche e capaci? Oppure aggiungere qualsiasi cosa che non sia solo memoria è barare e l’idea andrebbe sepolta?

11 Mi Piace

Come appassionato dell’hardware CPC quale sono, mi piacerebbe che il progetto venisse portato a termine, anche se non sono uno sviluppatore e non posso creare nulla.

Se è economico e utile (aggiunta di ROM / RAM / Mouse / una sorta di gestione rapida degli sprite), non vedo perché non possa essere interessante…
I giochi alla Populous / god game potrebbero usare il mouse come metodo di input più comodo. Persino io potrei modificare il mio gioco del campo minato per supportarlo.
Visto che Vespertino richiede una scheda da 4MB per poterci giocare, c’è sicuramente spazio per ottimi titoli con un piccolo aiuto a livello di hardware :slight_smile:. E usano un sacco di ROM, se non sbaglio.

Per il supporto futuro, direi in ordine di priorità:

  • Espansione RAM (fa sempre comodo averne un po’ su del nuovo hardware)
  • DMA da RAM CPC a RAM CPC per trasferimenti di memoria veloci
  • Pixel shifting / mascheramento per le tile
  • Funzionalità audio (i sample con un basso impatto su velocità/memoria fanno sempre comodo)
  • Salvataggio dati su Flash
  • Supporto Lower ROM

Per gli altri punti non ho elementi sufficienti per dare un feedback (memory mapping più complesso / NMI programmabile). Non saprei proprio dove potrei usarli.

Mi incuriosisce!

Trovo la tua presentazione (doragasu) estremamente interessante :slight_smile:

…da qualche tempo sto pasticciando con un «GOD GAME» (simile a Populous). Qualsiasi cosa provi, sono continuamente afflitto da colli di bottiglia delle prestazioni davvero pesanti e altrettanto ostinati. Mi piacerebbe provare il nuovo hardware (ROMBA π per gli sprite e il mouse) non appena sarà disponibile. Gli sprite potrebbero dare una mano con gli «omini brulicanti» (= software sprite) sull’area di gioco.

La mia sfida personale: non ho più un CPC. Ho dovuto venderlo a suo tempo. Ormai lavoro solo con gli emulatori.

Un’idea: se tu (doragasu) mi fornissi le specifiche, darei un’occhiata più approfondita all’emulatore CPC Caprise32 per vedere se sia possibile estenderlo via software per supportare il tuo hardware. In alternativa, creare una scheda adattatore per utilizzare le porte GPIO di un Raspberry Pi come porta di espansione del CPC. (È una cosa un po’ folle, ma anche parecchio interessante). Finora non ho ancora guardato il codice di Caprise32, ma forse tutti noi (la community) insieme possiamo trovare una buona idea…

Sembra interessante. Sinceramente mi ha sorpreso che ci sia voluto così tanto prima che l’RP2350B venisse usato per un’espansione multifunzione del genere.

Una cosa da considerare: se viene collegata contemporaneamente un’altra espansione dotata di RAM, molto probabilmente si verificherà un conflitto poiché agiranno sempre in parallelo. Ad esempio, un DDI-5 ha ancora senso in parallelo alla tua espansione. Forse si potrebbe aggiungere un interruttore per disattivare la RAM in questi scenari.

Grazie per l’interesse.

@SoftDevil Scriverò le specifiche più avanti, appena avrò un po’ di tempo. L’interfaccia del mouse dovrebbe essere definitiva così com’è. Come anticipazione, ecco come funziona. Utilizza le porte di I/O da $F990 a $F993:

Porta Lettura Scrittura Predefinito
$F990 STAT N/D N/D
$F991 WHEEL_BTN WHEEL_SCALING 64 (1.0)
$F992 X_AXIS X_SCALING 32 (0.5)
$F993 Y_AXIS Y_SCALING 32 (0.5)
  • STAT: il bit 0 è 1 se il mouse è collegato, 0 se non è collegato.
  • WHEEL_BTN: i 5 bit superiori rappresentano il delta di lettura della rotella (dall’ultima lettura) in complemento a due, positivo per il movimento della rotella verso l’alto, negativo per il movimento verso il basso. I 3 bit inferiori indicano lo stato attuale dei pulsanti: M, R, L.
  • X_AXIS: delta dell’asse X (dall’ultima lettura) in complemento a due a 8 bit. Positivo per il movimento verso destra, negativo per il movimento verso sinistra.
  • Y_AXIS: delta dell’asse Y (dall’ultima lettura) in complemento a due a 8 bit. Positivo per il movimento verso l’alto, negativo per il movimento verso il basso.
  • WHEEL_SCALING: fattore di scala frazionario della rotella.
  • X_SCALING: fattore di scala frazionario dell’asse X.
  • X_SCALING: fattore di scala frazionario dell’asse Y.

Leggendo la rotella e gli assi si ottiene il delta dall’ultima lettura, e il valore viene azzerato a 0. I valori saturano al loro massimo/minimo (da 15 a -16 per la rotella, da 127 a -128 per gli assi) se si muove il mouse velocemente o se passa troppo tempo tra una lettura e l’altra.

I fattori di scala consentono di regolare la sensibilità del mouse. Sono in formato a virgola fissa Q1.6 (1 bit di segno, 1 bit per la parte intera, 6 bit per la parte frazionaria). Per ottenere il valore di scala, bisogna dividere per 2^6 = 64. Quindi un fattore di scala di 64 corrisponde a 1.0, un fattore di 32 a 0.5, un fattore di 20 a 20/64 = 0.3125 e così via.

L’uso di valori di scala negativi provoca l’inversione dell’asse (ad esempio, se si imposta l’asse Y a 0xE0, che corrisponde a -0.5, oltre al fattore di scala di 0.5, muovendo verso l’alto si otterranno valori negativi e muovendo verso il basso valori positivi). L’uso di 0 come valore di scala disabilita a tutti gli effetti quell’asse.

Ciascun asse può essere configurato individualmente. Questo è utile ad esempio per invertire assi specifici come spiegato sopra, ma anche perché a volte è necessario avere valori diversi per ciascun asse. Per esempio, la patch per il mouse di Operation Wolf si gioca molto meglio con il fattore di scala X impostato a 16 (0.25) e il fattore di scala Y impostato a 32 (0.5), piuttosto che con lo stesso fattore per entrambi.

Per quanto riguarda il DMA al momento non ho tempo di spiegare tutti i registri, ma lo farò più avanti. Inoltre, se lo sviluppo del firmware continuerà, è probabile che cambi. E di molto. Si trovano da $FC00 a $FC0F:

enum rdma_reg {
	RDMA_REG_DMASRCL = 0,
	RDMA_REG_DMASRCH,
	RDMA_REG_DMASRCU,
	RDMA_REG_2D_MASK,      // Per la modalità 2D, non copia se la maschera corrisponde
	RDMA_REG_DMADSTL,
	RDMA_REG_DMADSTH,
	RDMA_REG_DMALENL,
	RDMA_REG_DMALENH,
	RDMA_REG_SCR_WIDTH,    // In caratteri, es.: 40
	RDMA_REG_SCR_HEIGHT,   // Altezza riga - 1 e altezza in caratteri: lllhhhhh (es. 0xF9)
	RDMA_REG_2D_W,         // In byte. NOTA: l'altezza viene dedotta da LEN
	RDMA_REG_2D_X,         // In byte, con segno
	RDMA_REG_2D_YL,        // In righe, con segno
	RDMA_REG_2D_YH,
	RDMA_REG_DMACTRL2,
	RDMA_REG_DMACTRL1,
	__RDMA_REG_MAX
};

// Maschere registro DMACTRL1
enum rdma_reg_dmactrl1 {
	RDMA_DMACTRL1_START_LSB = 0,    // Avvia il DMA se a 1
	RDMA_DMACTRL1_SRCINC_LSB = 1,   // Abilita auto-incremento SRC
	RDMA_DMACTRL1_DSTINC_LSB = 2,   // Abilita auto-incremento DST
	RDMA_DMACTRL1_2D_MASK_LSB = 3,  // Abilita mascheramento 2D se a 1
	RDMA_DMACTRL1_2D_XMIR_LSB = 4,  // Abilita mirroring su X
	RDMA_DMACTRL1_FULLADDR_LSB = 6, // 0: usa banco, 1: indirizzo a 24 bit
	RDMA_DMACTRL1_MODE_LSB = 7,     // 0: standard, 1: 2D
	RDMA_DMACTRL1_START_MASK = BIT(RDMA_DMACTRL1_START_LSB),
	RDMA_DMACTRL1_SRCINC_MASK = BIT(RDMA_DMACTRL1_SRCINC_LSB),
	RDMA_DMACTRL1_DSTINC_MASK = BIT(RDMA_DMACTRL1_DSTINC_LSB),
	RDMA_DMACTRL1_2D_MASK_MASK = BIT(RDMA_DMACTRL1_2D_MASK_LSB),
	RDMA_DMACTRL1_2D_XMIR_MASK = BIT(RDMA_DMACTRL1_2D_XMIR_LSB),
	RDMA_DMACTRL1_FULLADDR_MASK = BIT(RDMA_DMACTRL1_FULLADDR_LSB),
	RDMA_DMACTRL1_MODE_MASK = BIT(RDMA_DMACTRL1_MODE_LSB),
};

// Maschere registro DMACTRL2
// IMPOSTAZIONE MIRRORING:
// 0b00: nessun mirroring
// 0b01: mirroring modalità 0
// 0b10: mirroring modalità 1
// 0b11: mirroring modalità 2
enum rdma_reg_dmactrl2 {
	RDMA_DMACTRL2_XMIRR_LSB = 0, // Mirroring su asse X
	RDMA_DMACTRL2_XMIRR_MASK = 0x3,
};

In pratica, all’inizializzazione bisogna configurare una serie di parametri relativi alla geometria dello schermo, alla posizione della memoria video, all’indirizzo sorgente dello sprite, ecc. Poi si scrivono i flag di X, Y e mirroring secondo necessità, e si avvia la transazione DMA scrivendo su DMACTRL1. Se il progetto andrà avanti, l’idea sarebbe quella di avere un insieme di sprite preconfigurati, ma al momento si ha a disposizione solo questo set di registri, quindi ad esempio per disegnare un secondo sprite bisognerebbe prima disegnare il primo, poi cambiare l’indirizzo sorgente per il nuovo sprite, le dimensioni se variano, ecc., e infine disegnarlo.

Riguardo a chi vorrebbe avere una cartuccia per fare dei test: se c’è abbastanza interesse posso produrne un po’, ma forse mi servirebbe qualche consiglio per questioni come la spedizione, dato che non l’ho mai fatto prima. Vivo in Spagna, quindi le spedizioni all’interno dell’Europa non dovrebbero essere un problema, ma al di fuori potrebbero esserlo.

2 Mi Piace

Ciao, ho integrato il supporto per il mouse sul mio clone di campo minato (al momento senza scaling X/Y, ma ho già preparato il codice).

Tuttavia, sembra che winape restituisca bit0 = 1 sulla porta F990 e quindi è ATTIVO per impostazione predefinita (e i valori di deltaX deltaY sono -1 di default), perciò forse 0xFF quando nulla risponde su una porta è «normale» (non saprei, dato che non uso molto hardware).

Hai un altro modo per verificare se ROMBOX pi è presente, così da controllare la porta solo quando possibile? O magari invertire il bit 0, oppure fornire un valore specifico da verificare (non un solo bit) come 142 o 155.

Se vuoi testarlo con la tua scheda e farmi sapere se va tutto bene, te lo invio tramite messaggio privato.

Esatto. Tanto vale sbarazzarsi del DDI5 fin dall’inizio e mettere l’emulazione disco sulla stessa scheda :wink: L’RP2350 può gestire entrambe le cose https://youtube.com/playlist?list=PLa1DevUxKDmU&si=sxcJhFYr4qiDhYtl Probabilmente aggiungeremo l’espansione RAM insieme alla ROM e all’emulazione disco per la prossima revisione del PCB. A questa mancano i pin necessari. A qualcosa bisogna pur rinunciare. Ma d’altronde, ci sono ottime espansioni RAM disponibili, come quella di Revaldhino. Tuttavia, un dispositivo all-in-one ha sicuramente il suo fascino.

In ogni caso, l’RP2350 È INCREDIBILE!!

WinApe non emula questa cartuccia, e molto probabilmente leggere da quelle porte restituirà sempre 0xFF. SyX ha applicato delle patch ad alcuni giochi e ha anche modificato MAME per aggiungere il supporto al mouse ROMBA; potrei chiedergli di rilasciare le sue modifiche. Inoltre, @SoftDevil potrebbe provare ad aggiungere il supporto a Caprice.

Finché non ci sarà una release pubblica di un emulatore con supporto a ROMBA, se vuoi che provi il tuo gioco nessun problema: puoi mandarmi un messaggio privato e lo testerò.

Riguardo al fractional scaling, usarlo è del tutto opzionale. La configurazione predefinita dovrebbe funzionare bene per la maggior parte dei giochi e, se necessario, potrebbe essere aggiunto persino su un loader in BASIC.

1 Mi Piace

Il problema con l’espansione della RAM, almeno con la mia cartuccia, è che non ho RAM extra saldata sulla scheda (e non ho intenzione di aggiungerla). Quindi l’unica RAM che posso usare per l’espansione è quella che il firmware non utilizza dei 520 KiB integrati nell’RP2350. A seconda di cosa finisce sulla cartuccia, un’espansione di RAM da 256 KiB potrebbe essere possibile, ma arrivare a 512 KiB no (beh, tecnicamente forse sarebbe possibile rimuovendo tutte le funzionalità tranne il supporto per ROM e RAM, che dovrebbero entrare in 8 KiB).

Ad ogni modo, i conflitti non dovrebbero verificarsi finché un’applicazione non tenta di utilizzare la RAM, giusto? Quindi forse, invece di uno switch, potrei scrivere in una configurazione non volatile (modificabile tramite scrittura su una porta di configurazione).

La domanda è: cosa fa un vero CPC senza la tua estensione quando leggiamo questa porta? Se restituisce -1 come winape, allora devi inviare qualcosa di diverso da un semplice 1 nel bit 0 per indicare se c’è o meno. Dal mio punto di vista è sufficiente un byte di firma.
Preferirei non dover creare una versione del mio gioco con e una senza ROMBOX π. Dovrebbe semplicemente sfruttarla o meno.

Ti invio una versione via messaggio privato giusto per verificare se si gioca meglio con il mouse.

OK, potresti considerare il funzionamento del registro in questo modo (lo aggiungerò alla documentazione):

B7 B6 B5 B4 B3 B2 B1 B0
PRESENT R R R R R R STAT
  • Bit 7, PRESENT: 0 se ROMBA è collegato al CPC, 1 se non lo è
  • Bit da 6 a 1: riservati
  • Bit 0, STAT: 0 se il mouse non viene rilevato, 1 se il mouse viene rilevato

Naturalmente il bit 0 può essere interpretato solo se il bit 7 è 0. I bit riservati servono nel caso in cui aggiungessi altre funzionalità. Ad esempio, ho pensato di supportare due mouse per i giochi a due giocatori. Se dovessi implementarlo, il bit 1 sarebbe il bit di stato per il secondo mouse.

Il test dimostra che il supporto per il mouse è facile da aggiungere a un progetto esistente con solo qualche in/out su 4 porte :slight_smile:
Se tutto è così, non vedo perché non possa essere utile agli sviluppatori.

Grazie per il test.

2 Mi Piace

Ora la cosa si fa imbarazzante per me. :frowning:

Non capisco come ampliare Caprise32 (emulatore CPC) affinché supporti la tua scheda. E c’è di peggio: non capisco Caprise32 nel suo insieme. Mi manca il tempo per addentrarmi nel codice esistente con la profondità necessaria.

Così ho dato tutto in pasto a Claude Code. Per Claude Code è tutto chiarissimo e logico. Ed ecco qua: ecco il file diff.

Tuttavia, anche con il supporto dell’IA non sono riuscito a capire l’integrazione. Trovo sbagliato inserire semplicemente del codice non compreso in un prodotto, anche se alla fine dovesse funzionare tutto. In caso di errori sarei completamente perso e dovrei sperare di nuovo in un’IA. La cosa non mi va proprio a genio.

Nata da un pizzico di presunzione, devo purtroppo ritirare la mia offerta di integrare Caprise32. Ti chiedo sinceramente scusa.

Nessun problema, grazie per averci provato. Non ho guardato i sorgenti di Caprice32, ma suppongo che tu debba vedere dove gestisce gli OUT.