Personalizzare il firmware della scheda terminale ANSI / VT-100

Scritto il 12 maggio 2018 da

La scheda terminale ANSI / VT-100 per il computer RC2014 utilizza il firmware open source disponibile su GitHub ed è pertanto liberamente modificabile da chiunque. Tuttavia, essendo scritto nel linguaggio Spin proprio dei microcontrollori Parallax Propeller, potrebbe essere difficile, anche per chi è esperto di programmazione di microcontrollori, capirne il funzionamento, cercherò quindi di spiegare il più semplicemente possibile come effettuare alcune piccole modifiche per rendere il firmware un po’ più personale.

Propeller ANSI / VT-100 Terminal

Propeller ANSI / VT-100 Terminal

Le indicazioni date di seguito presuppongono una certa familiarità nell’uso degli strumenti a linea di comando tramite il Terminale di Linux o il Prompt dei Comandi di Windows e che siate in posseso di un programmatore in grado di scrivere le EEPROM seriali, oppure dell’adattatore PropPlug per la programmazione in-circuit.

Compilare il firmware

Per prima cosa è necessario scaricare il sorgente da GitHub sul proprio computer per poterlo poi modificare.

Se si dispone del programma git già installato, il metodo più semplice è quello di “clonare” il repository, cioè crearne una copia sul proprio computer. Aprite una finestra Terminale (o Prompt dei Comandi se utilizzate Windows) e digitate il comando

git clone https://github.com/maccasoft/propeller-vt100-terminal.git

Dopo alcuni istanti, in cui il programma scaricherà tutto il contenuto del repository, troverete nella cartella propeller-vt100-terminal il sorgente completo.

Esempio:

marco@bridge:~$ git clone https://github.com/maccasoft/propeller-vt100-terminal.git
Cloning into 'propeller-vt100-terminal'...
remote: Counting objects: 130, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 130 (delta 7), reused 7 (delta 2), pack-reused 115
Ricezione degli oggetti: 100% (130/130), 641.75 KiB | 378.00 KiB/s, done.
Risoluzione dei delta: 100% (72/72), done.
Checking connectivity... fatto.

Se non disponete di git e/o non volete usarlo, è comunque possibile scaricare una copia del repository da GitHub utilizzando questo collegamento https://github.com/maccasoft/propeller-vt100-terminal/archive/master.zip. Una volta scompattato l’archivio troverete una cartella con il nome propeller-vt100-terminal-master contenente il sorgente completo.

A questo punto è necessario scaricare e installare il compilatore OpenSpin necessario per compilare il codice sorgente in un formato leggibile dal microcontrollore. Il programma può essere scaricato dalla pagina di download. Nota: la versione presente su quella pagina è stata compilata direttamente dai sorgenti e contiene alcune correzioni necessarie per compilare il nostro codice, se avete una versione precedente di OpenSpin consiglio di aggiornarla.

Il programma non necessita di alcuna installazione, scompattate l’archivio e troverete il programma esegubile chiamato openspin(.exe). Consiglio di spostare il file in una cartella raggiungibile dalla variable di ambiente PATH per non dover scrivere il percorso completo ogni volta.

Adesso siamo pronti a compilare il sorgente, quindi andate nella cartella in cui è stato scaricato e digitate il comando

openspin -b -u -DKEYMAP_IT vt100.spin

Senza dilungarsi troppo sul significato dei parametri, -b e -u servono per generare il codice binario, il parametro -DKEYMAP_IT definisce la mappatura della tastiera, in questo caso italiano, e vt100.spin è il nome del file principale da compilare. Tutti gli altri file saranno inclusi automaticamente e non è necessario specificarli nel comando.

Esempio di compilazione:

propeller-vt100-terminal$ ./openspin -b -u -DKEYMAP_IT vt100.spin 
Propeller Spin/PASM Compiler 'OpenSpin' (c)2012-2018 Parallax Inc. DBA Parallax Semiconductor.
Version 1.00.81 Compiled on Apr 27 2018 08:13:09
Compiling...
vt100.spin
|-usb-fs-host.spin
|-com.serial.spin
|-com.serial.terminal.spin
  |-com.serial.spin
  |-string.integer.spin
|-waitvid.80x25.driver.spin
|-generic8x16-2font.spin
|-keymap_it.spin
Done.
Unused Method Elimination:
   55 methods removed
    1 objects removed
 1092 bytes saved
--------------------------
Program size is 14460 bytes

Se tutto è andato bene e non vengono visualizzati errori, trovete nella stessa cartella il file vt100.binary risultato dalla compilazione del sorgente. Questo file può essere scritto sulla EEPROM usando il programma Propeller Loader, se avete un adattatore PropPlug, oppure tramite un programmatore di EEPROM seriali.

Se la vostra tastiera non è italiana o volete utilizzare una mappatura diversa potete cambiare le ultime due lettere del parametro -DKEYMAP_IT con la sigla del paese che volete utilizzare tra quelli disponibili. Ad esempio openspin -b -u -DKEYMAP_US vt100.spin per impostare la mappaura per le tastiere degli Stati Uniti, oppure openspin -b -u -DKEYMAP_UK vt100.spin per le tastiere del Regno Unito. I paesi attualmente disponibili sono DE (Germania), FR (Francia), IT (Italia), UK (Regno Unito) e US (Stati Uniti).

Adesso che abbiamo verificato che la compilazione funziona correttamente possiamo procedere con le personalizzazioni. Ricordate che ogni volta che modificate il codice sorgente è necessario ripetere la procedura di compilazione e di scrittura sulla EEPROM come descritto sopra altrimenti le vostre modifiche non avranno alcun effetto.

Cursore

Con le impostazioni predefinite viene visualizzato un cursore grande e lampeggiante in pieno stile terminali anni ’70. La variable cursor.byte alla linea 103 del sorgente vt100.spin permette di modificare questa impostazione.

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_BLOCK | CURSOR_FLASH)

Il parametro CURSOR_ON indica che il cursore è visible, CURSOR_BLOCK indica che deve essere visualizzato come un blocco intero e CURSOR_FLASH indica che deve lampeggiare. Cambiando questi parametri è possibile cambiare il cursore predefinito, ad esempio:

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_ULINE | CURSOR_FLASH)

Visualizza il cursore come un piccolo rettangolo alla base del carattere in stile MS-DOS.

Sostituendo CURSOR_FLASH con CURSOR_SOLID si otterrà un cursore fisso e non più lampeggiante.

cursor.byte{CM} := (cursor.byte{CM} & constant(!CURSOR_MASK)) | constant(CURSOR_ON | CURSOR_BLOCK | CURSOR_SOLID)

I possibili parametri sono elencati più in alto nel sorgente.

    CURSOR_ON    = vga#CURSOR_ON
    CURSOR_OFF   = vga#CURSOR_OFF
    CURSOR_ULINE = vga#CURSOR_ULINE
    CURSOR_BLOCK = vga#CURSOR_BLOCK
    CURSOR_FLASH = vga#CURSOR_FLASH
    CURSOR_SOLID = vga#CURSOR_SOLID

Nota: alcune sequenze VT-100 permettono di modificare lo stile del cursore, l’impostazione predefinita rimane in uso finchè non vengono utilizzate tali sequenze.

Tasti Speciali

Una normale tastiera non dispone solo dei tasti alfabetici e numerici ma anche di numerosi tasti speciali, come i tasti funzione, le frecce direzionali, ecc. che in ambito PC permettono di eseguire operazioni speciali. In un terminale tipo VT-100 alcuni di questi tasti hanno assegnata una sequenza di codici, altri non hanno alcuna funzione, semplicemente perchè non erano originariamente previsti nelle tastiere dell’epoca. Il firmware permette di associare una sequenza di codici ad ogni tasto, vediamo come è possibile cambiarla.

In fondo al sorgente vt100.spin troviamo una lunga lista di etichette a cui è stata associata una sequenza di byte.

strKeyInsert        byte    0
strKeyHome          byte    $1B, "[H", 0
strKeyPageUp        byte    0
strKeyDelete        byte    $7F, 0
strKeyEnd           byte    $1B, "[K", 0
strKeyPageDown      byte    0
strKeyUp            byte    $1B, "OA", 0
strKeyDown          byte    $1B, "OB", 0
strKeyLeft          byte    $1B, "OD", 0
strKeyRight         byte    $1B, "OC", 0
strKeyShiftLeft     byte    0
strKeyShiftRight byte 0

Le etichette dovrebbero essere abbastanza esplicative riguardo a quale tasto si riferiscono. In questo esempio possiamo vedere che per il tasto Insert non è associato alcun codice (0), mentre al tasto Home (Inizio) è associata la sequenza $1B, “[H”. Pertanto ogniqualvolta premiamo il tasto Insert nulla viene trasmesso al computer, mentre quando premiamo il tasto Home la sequenza indicata accanto all’etichetta viene trasmessa al computer e, se il programma in funzione è in grado di riconoscerla e gestirla, otterremo un effetto. Lo stesso discorso si applica ovviamente a tutte le altre etichette e i corrispondenti tasti.

Se vogliamo cambiare i codici che vengono inviati premendo un particolare tasto basta trovare la corrispondente etichetta e modificare la sequenza dopo l’istruzione byte secondo le nostre esigenze. Ricordatevi di terminare le sequenze con il codice 0 che, come nel linguaggio C, indica la fine della “stringa” di codici.

Supponiamo per esempio di voler inviare la parola “INS” quando si preme appunto il tasto Insert. Troviamo l’etichetta strKeyInsert e cambiamo la sequenza dopo l’istruzione byte come segue.

strKeyInsert        byte    "INS", 0

Ora, dopo aver compilato il sorgente e scritto il codice binario sulla EEPROM, ogni volta che premiamo il tasto Insert vedremo comparire sullo schermo la parola INS.

Possiamo anche scrivere le sequenze utilizzando il codice decimale o esadecimale di ogni singolo byte da inviare separandoli con una virgola.

strKeyInsert        byte    "INS", 13, 0          ' aggiunge il codice 13 del ritorno a capo
strKeyInsert        byte    $49, $4E, $53, $0D, 0 ' equivalente al precedente ma in esadecimale

Ricordate che le sequenze di codici speciali devono essere interpretate dal programma che sta “girando” sul computer, se queste non sono riconosciute non otterranno il risultato desiderato e in alcuni casi potrebbero causare effetti indesiderati.

Tavolozza dei Colori

Il driver VGA è predisposto per visualizzare i caratteri e lo sfondo con un massimo di 16 colori, da una tavolozza di 64, utilizzando una mappatura simile a quella delle prime schede VGA, con un massimo di 16 colori per il primo piano e 8 colori per lo sfondo. La tavolozza è definita nel file waitvid.80×25.driver.spin tramite il blocco di righe dalla 70 alla 85.

                long               $82020282, $22020222, $A20202A2, $0A02020A, $8A02028A, $2A02022A, $AA0202AA
                long    $02828202, $82828282, $22828222, $A28282A2, $0A82820A, $8A82828A, $2A82822A, $AA8282AA
                long    $02222202, $82222282, $22222222, $A22222A2, $0A22220A, $8A22228A, $2A22222A, $AA2222AA
                long    $02A2A202, $82A2A282, $22A2A222, $A2A2A2A2, $0AA2A20A, $8AA2A28A, $2AA2A22A, $AAA2A2AA
                long    $020A0A02, $820A0A82, $220A0A22, $A20A0AA2, $0A0A0A0A, $8A0A0A8A, $2A0A0A2A, $AA0A0AAA
                long    $028A8A02, $828A8A82, $228A8A22, $A28A8AA2, $0A8A8A0A, $8A8A8A8A, $2A8A8A2A, $AA8A8AAA
                long    $022A2A02, $822A2A82, $222A2A22, $A22A2AA2, $0A2A2A0A, $8A2A2A8A, $2A2A2A2A, $AA2A2AAA
                long    $02AAAA02, $82AAAA82, $22AAAA22, $A2AAAAA2, $0AAAAA0A, $8AAAAA8A, $2AAAAA2A, $AAAAAAAA
                long    $02565602, $82565682, $22565622, $A25656A2, $0A56560A, $8A56568A, $2A56562A, $AA5656AA
                long    $02C2C202, $82C2C282, $22C2C222, $A2C2C2A2, $0AC2C20A, $8AC2C28A, $2AC2C22A, $AAC2C2AA
                long    $02323202, $82323282, $22323222, $A23232A2, $0A32320A, $8A32328A, $2A32322A, $AA3232AA
                long    $02F6F602, $82F6F682, $22F6F622, $A2F6F6A2, $0AF6F60A, $8AF6F68A, $2AF6F62A, $AAF6F6AA
                long    $020E0E02, $820E0E82, $220E0E22, $A20E0EA2, $0A0E0E0A, $8A0E0E8A, $2A0E0E2A, $AA0E0EAA
                long    $02CECE02, $82CECE82, $22CECE22, $A2CECEA2, $0ACECE0A, $8ACECE8A, $2ACECE2A, $AACECEAA
                long    $023E3E02, $823E3E82, $223E3E22, $A23E3EA2, $0A3E3E0A, $8A3E3E8A, $2A3E3E2A, $AA3E3EAA
                long    $02FEFE02, $82FEFE82, $22FEFE22, $A2FEFEA2, $0AFEFE0A, $8AFEFE8A, $2AFEFE2A, $AAFEFEAA

A prima vista può sembrare un ammasso di dati senza senso ma in realtà è molto semplice decifrarne il significato. Ogni riga rappresenta un colore di primo piano mentre ogni colonna rappresenta un colore di sfondo, abbiamo quindi una griglia di 16 righe e 8 colonne. Ogni cella è un valore esadecimale che rappresenta il colore RGB da utilizzare per lo sfondo e il primo piano in modalità normale o quando il carattere deve lampeggiare.

Prendiamo per esempio il colore bianco su sfondo blu e vediamo come viene definito nella tavolozza. Il colore bianco di primo piano si trova alla riga numero 16 mentre il colore blu di sfondo si trova alla colonna 5, quindi come faremmo con la “battaglia navale” scorriamo la lista dall’alto in basso fino alla riga 16, quindi da sinistra verso destra fino alla colonna 5 e otteniamo il codice esadecimale $0AFEFE0A. Ogni gruppo di 2 cifre esadecimali rappresenta un byte e un colore RGB secondo il seguente schema.

| 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0 0 | 0 0 | 1 0 | 1 0 | 1 1 | 1 1 | 1 1 | 1 0 | 1 1 | 1 1 | 1 1 | 1 0 | 0 0 | 0 0 | 1 0 | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|       0A (blu)        |      FE (bianco)      |      FE (bianco)      |       0A (blu)        |

Da sinistra a destra, il primo byte indica il colore di primo piano da utilizzare per il lampeggio, il secondo byte il colore di sfondo sempre durante il lampeggio, il terzo byte il colore di primo piano in modalità normale e il quarto byte il colore di sfondo sempre in modalità normale.

I bit 7-6 rappresentano la componente di colore rosso, i bit 5-4 la componente verde e i bit 3-2 la componente blu. I bit 1-0 devono essere mantenuti ai valori indicati 1 e 0 rispettivamente. Poichè ogni componente è rappresentata da 2 bit abbiamo un massimo di 4 livelli di intensità (00, 01, 10 e 11) quindi 4 * 4 * 4 = 64 combinazioni possibili che corrispondono ad altrettante tonalità di colore sullo schermo. Ad esempio $02 corrisponde al nero, $C2 corrisponde al rosso, $32 corrisponde al verde, $0E corrisponde al blu, $FE corrisponde al bianco, e così via.

Se volessimo cambiare il colore di primo piano e farlo diventare un giallo dobbiamo impostare le componenti rosso e verde all’intensità massima e la componente blu a zero, nei due byte centrali.

| 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 | 7 6   5 4   3 2   1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 | r r | g g | b b | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 0 0 | 0 0 | 1 0 | 1 0 | 1 1 | 1 1 | 0 0 | 1 0 | 1 1 | 1 1 | 0 0 | 1 0 | 0 0 | 0 0 | 1 0 | 1 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|       0A (blu)        |      F2 (giallo)      |      F2 (giallo)      |       0A (blu)        |

Quindi sostituendo il codice $0AFEFE0A con $0AF2F20A alla riga 16 e colonna 5 otteniamo il colore giallo su sfondo blu al posto del bianco.

Prova ad eseguire questa riga con l’interprete BASIC prima e dopo la modifica.

PRINT CHR$(27);"[1;37;44m";"WHITE ON BLUE;CHR$(27);"[0m"

Ora potete divertirvi a creare la tavolozza di colori che più vi soddisfa.