Joyce, l'emulatore Amstrad PCW: installazione e modifiche al codice per il supporto alla tastiera italiana del Macbook Pro
Questo post descrive la procedura per compilare ed installare l'emulatore Joyce di Amstrad PCW su MacOS e una mia patch al codice sorgente per utilizzare la tastiera italiana del Macbook Pro.
La documentazione dell'emulatore spiega come personalizzare il layout della tastiera, tuttavia questo va fatto a CP/M già avviato, tramite il comando PCKEY. Ho trovato questo metodo di difficile applicazione, considerando anche che non riuscivo a digitare caratteri basilari come i due punti!
Ho quindi deciso di procedere con la via "facile", modificando direttamente il codice Joyce che gestisce la corrispondenza tra la tastiera del PC e quella del sistema emulato. Le modifiche, che si applicano alla versione 2.2.12, l'ho scritte senza badare molto alla forma e senza cercare alternative meno invasive ad una mappatura dei tasti hardcoded nel codice stesso, per cui non è certo un bel esempio di programmazione!
Joyce permette l'emulazione dei PCW serie 8000, 9000 e PCW10, mentre Anne (che si compila automaticamente con Joyce) emula il PCW16. La patch è stata testata solo con la serie 8000 (PCW 8256 e 8512).
Joyce permette l'emulazione dei PCW serie 8000, 9000 e PCW10, mentre Anne (che si compila automaticamente con Joyce) emula il PCW16. La patch è stata testata solo con la serie 8000 (PCW 8256 e 8512).
Per compilare Joyce occorre prima di tutto installare alcune librerie. Per fare questo userò brew, un tool che porta moltissime applicazioni e librerie di Linux su MacOS. Incollare questa riga sul terminale e premere Invio per installare brew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Installare ora tramite brew le librerie necessarie (PNG, SDL e XML):
brew install libpng
brew install sdl
brew install libxml2
Il sito originale di Joyce è https://www.seasip.info/Unix/Joyce/, mentre la versione patchata è scaricabile dal mio profilo github, in questo modo:
git clone https://github.com/fdivitto/joyce-custom.git
brew install libpng
brew install sdl
brew install libxml2
Il sito originale di Joyce è https://www.seasip.info/Unix/Joyce/, mentre la versione patchata è scaricabile dal mio profilo github, in questo modo:
git clone https://github.com/fdivitto/joyce-custom.git
Terminato il download procedere alla compilazione e all'installazione:
A questo punto Joyce è installato e funzionante e si può rimuovere la cartella con i sorgenti.
cd joyce-custom/
./configure
make
sudo make install
Per eseguire Joyce basta scrivere:
xjoyce
Se appare una finestra bianca premere Invio. Verrà creata la cartella ~\Joyce\Boot dove andranno inserite le immagini (dsk) di boot.
Al primo avvio Joyce necessita di una piccola configurazione e ci chiede da dove effettuare il boot che può essere effettuato sia dai drive fisici (fd0, fd1...) che da un'immagine di un floppy (Disc file):
La schermata successiva permette la selezione del file immagine. In questo caso sto selezionato il lato B del disco di sistema, dov'è contenuto il CP/M:
Si seleziona OK e quindi CONTINUE:
Ora Joyce ci chiede di dare un nome all'immagine. Io ho scritto 'cpm_plus':
A questo punto appare la schermata principale, che è quella che verrà presentata ogni volta che si avvia Joyce:
E' stata specificata una sola immagine, quella denominata "cpm_plus" ed identificata con "1". L'"1" è il tasto da premere se si vuole avviare dal disco "cpm_plus".
Prima di iniziare il boot occorre specificare il tipo di hardware da emulare. Come detto questa patch è specifica per PCW 8256/8512, quindi si procederà come segue. Premere F9, quindi F6 e selezionare con i tasti freccia la voce "General":
Premere Invio e selezionare 8256/8512:
Selezionare EXIT e quindi "Save Settings":
Uscire da tutte le schermate con Exit o con il tasto ESC. Adesso premendo "1" si effettuerà il boot ed il sistema sarà pronto per l'utilizzo:
Per concludere ecco una breve spiegazione delle modifiche che ho apportato al codice sorgente di Joyce. La classe C++ che gestisce la tastiera è chiamata JoycePcwKeyboard, ed è dichiarata e definita nei file JoycePcwKeyboard.hxx e JoycePcwKeyboard.cxx, entrambi posizionati nella cartella "bin".
Nella dichiarazione della classe ho aggiunto il campo m_fdvAlt che è un flag attivo per tutto il tempo in cui il tasto ALT è premuto. Ho dichiarato anche due metodi, il primo è fdv_handle() che è il gestore della tastiera alternativo a Joyce e fdv_setKeyMap() che ne è di supporto:
void fdv_setKeyMap(Uint8 addr, Uint8 bit, bool down);
bool fdv_handle(SDLKey keysym, Uint8 type);
fdv_handle() intercetta solo le configurazioni di tasti (che chiamerò regole) specificati nell'array fdv_KMAPEX[], definito in JoycePcwKeyboard.cxx. Questo array è composto da una struttura di 11 campi: il primo (keysym) specifica il codice SDL del tasto premuto, mentre i campi shift e alt indicano se SHIFT e/o ALT debbano essere attivi affinché la regola venga applicata.
In alcuni casi è necessario disattivare SHIFT o ALT perché il tasto corrispondente sul PCW non li richiede mentre era necessario sul PC. E' il caso dell'uguale (=) che sul macbook si fa con SHIFT + '0', mentre sul PCW c'è un tasto dedicato. Per questo si utilizzano i campi off_addr_swAlt/off_bit_swAlt (quando l'opzione scambia EXTRA<->ALT è attiva) oppure off_addr_nswAlt/off_bit_nswAlt (quando l'opzione scambia EXTRA<->ALT è disattiva). I campi *_addr_* contengono il primo nibble (primi quattro bit) dell'indirizzo memory mapped della tastiera, mentre i campi *_bit_* il numero del bit relativo al tasto, secondo questa tabella:
Ad esempio, per disattivare lo SHIFT occorre impostare off_addr_swAlt = 2 (indirizzo 3FF2h) e off_bit_swAlt = 5 (bit 5).
I campi on_addr_1/on_bit_1 e on_addr_2/on_bit_2 specificano l'azione da intraprendere, cioè i tasti che il PCW vedrà premuti.
La regola da impostare per generare l'uguale è quindi questa:
{SDLK_0, true, false, // tasto SDLK_0 e SHIFT premuto (ALT no)
0x02, 0x05, // SHIFT su (nel caso ALT-EXTRA scambiati)
0x02, 0x05, // SHIFT su (ALT-EXTRA non scambiati)
0x03, 0x00, // '=' giù
0xFF, 0xFF}, // 0xFF indica "campi non utilizzati"
Il metodo fdv_handle(), oltre ad applicare le regole specificate nell'array fdv_KMAPEX[], si occupa anche di rilevare la pressione del tasto ALT (per impostare il flag m_fdvAlt) e sistemare un problema con il tasto CAPSLOCK.
Questa è la dichiarazione della struttura fdv_KMAPEX_t, utilizzata nell'array fdv_KMAPEX[]:
struct fdv_KMAPEX_t {
// PC side state
SDLKey keysym;
bool shift;
bool alt;
// PCW side: switch off if m_swapAlt == true
Uint8 off_addr_swAlt; // 0xFF nothing to switch off
Uint8 off_bit_swAlt; // 0xFF nothing to switch off
// PCW side: switch off if m_swapAlt == false
Uint8 off_addr_nswAlt; // 0xFF nothing to switch off
Uint8 off_bit_nswAlt; // 0xFF nothing to switch off
// PCW side: switch on
Uint8 on_addr_1; // 0xFF nothing to switch on
Uint8 on_bit_1; // 0xFF nothing to switch on
Uint8 on_addr_2; // 0xFF nothing to switch on
Uint8 on_bit_2; // 0xFF nothing to switch on
};
Queste sono le regole:
static const fdv_KMAPEX_t fdv_KMAPEX[] = {
// ':' MAC-IT: SHIFT + '.' PCW: SHIFT + ';'
{SDLK_PERIOD, true, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x03, 0x05}, // ';' down
// ';' MAC-IT: SHIFT + ',' PCW: ';'
{SDLK_COMMA, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x03, 0x05, // ';' down
0xFF, 0xFF}, //
// '\' MAC-IT: '\' PCW: EXTRA + '1/2'
{SDLK_BACKSLASH, false, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x0A, 0x1, // EXTRA down
0x02, 0x6}, // '1/2' down
// '|' MAC-IT: SHIFT + '\' PCW: EXTRA + '.'
{SDLK_BACKSLASH, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x0A, 0x01, // EXTRA down
0x03, 0x07}, // '.' down
// '&' MAC-IT: SHIFT + '6' PCW: SHIFT + '7'
{SDLK_6, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT down
0x05, 0x01}, // '7' down
// '/' MAC-IT: SHIFT + '7' PCW: '/'
{SDLK_7, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x03, 0x06, // '/' down
0xFF, 0xFF}, //
// '(' MAC-IT: SHIFT + '8' PCW: SHIFT + '9'
{SDLK_8, true, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x04, 0x01}, // '9' down
// ')' MAC-IT: SHIFT + '9' PCW: SHIFT + '0'
{SDLK_9, true, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x04, 0x00}, // '0' down
// '=' MAC-IT: SHIFT + '0' PCW: '='
{SDLK_0, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x03, 0x00, // '=' down
0xFF, 0xFF}, //
// ''' MAC-IT: ''' PCW: SHIFT + '6'
{SDLK_QUOTE, false, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x06, 0x00}, // '6' down
// '?' MAC-IT: SHIFT + ''' PCW: SHIFT + '/'
{SDLK_QUOTE, true, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x03, 0x06}, // '/' down
// '^' MAC-IT: SHIFT + 'ì' PCW: EXTRA + '7'
{(SDLKey)0xA0, true, false, // In my system 'ì' is 0xA0
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x0A, 0x01, // EXTRA down
0x05, 0x01}, // '7' down
// '[' MAC-IT: ALT + 'è' PCW: '['
{(SDLKey)0xA1, false, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x03, 0x02, // '['
0xFF, 0xFF}, //
// ']' MAC-IT: ALT + '+' PCW: ']'
{SDLK_PLUS, false, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x02, 0x01, // ']'
0xFF, 0xFF}, //
// '+' MAC-IT: '+' PCW: SHIFT + '='
{SDLK_PLUS, false, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x03, 0x00}, // '=' down
// '*' MAC-IT: SHIFT + '+' PCW: SHIFT + '8'
{SDLK_PLUS, true, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x05, 0x00}, // '8' down
// '@' MAC-IT: ALT + 'ò' PCW: SHIFT + '1/2'
{(SDLKey)0xA3, false, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x02, 0x05, // SHIFT down
0x02, 0x6}, // '1/2' down
// '#' MAC-IT: ALT + 'à' PCW: '#'
{(SDLKey)0xA2, false, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x02, 0x03, // '#' down
0xFF, 0xFF}, //
// '{' MAC-IT: SHIFT + ALT + 'è' PCW: SHIFT + '['
{(SDLKey)0xA1, true, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x02, 0x05, // SHIFT down
0x03, 0x02}, // '['
// '{' MAC-IT: SHIFT + ALT + '+' PCW: SHIFT + '['
{SDLK_PLUS, true, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x02, 0x05, // SHIFT down
0x02, 0x01}, // ']'
// '°' MAC-IT: SHIFT + 'à' PCW: EXTRA + '5'
{(SDLKey)0xA2, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x0A, 0x01, // EXTRA down
0x06, 0x01}, // '5'
// '~' MAC-IT: ALT + '5' PCW: EXTRA + '-'
{SDLK_5, false, true,
0x0A, 0x01, // EXTRA up (EXTRA<->ALT)
0x0A, 0x07, // ALT up
0x0A, 0x01, // EXTRA down
0x03, 0x01}, // '-'
// '<' MAC-IT: '<' PCW: SHIFT + '§'
{SDLK_LESS, false, false,
0xFF, 0xFF, //
0xFF, 0xFF, //
0x02, 0x05, // SHIFT down
0x03, 0x04}, // '§'
// '>' MAC-IT: SHIFT + '<' PCW: SHIFT + '#'
{SDLK_LESS, true, false,
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT up
0x02, 0x05, // SHIFT down
0x02, 0x03}, // '#'
};
Ecco i due metodi fdv_setKeyMap() (di supporto) e fdv_handle() (il gestore vero e proprio):
void JoycePcwKeyboard::fdv_setKeyMap(Uint8 addr, Uint8 bit, bool down)
{
if (down)
m_pcwKeyMap[addr] |= 1 << bit;
else
m_pcwKeyMap[addr] &= ~(1 << bit);
}
bool JoycePcwKeyboard::fdv_handle(SDLKey keysym, Uint8 type)
{
if (type == SDL_KEYDOWN || type == SDL_KEYUP)
{
// semi-handle ALT
if (keysym == SDLK_LALT || keysym == SDLK_RALT)
{
m_fdvAlt = type == SDL_KEYDOWN;
return false; // not handled
}
// handle CAPSLOCK
if (keysym == (SDLKey)0x12D)
{
m_shiftLock = type == SDL_KEYDOWN;
return true; // handled
}
for (int i = 0; i < sizeof(fdv_KMAPEX) / sizeof(fdv_KMAPEX_t); ++i)
if (fdv_KMAPEX[i].keysym == keysym && fdv_KMAPEX[i].shift == m_trueShift && fdv_KMAPEX[i].alt == m_fdvAlt)
{
// switch off (if m_swapAlt == true)
if (m_swapAlt && fdv_KMAPEX[i].off_addr_swAlt != 0xFF)
fdv_setKeyMap(fdv_KMAPEX[i].off_addr_swAlt, fdv_KMAPEX[i].off_bit_swAlt, type != SDL_KEYDOWN);
// switch off (if m_swapAlt == false)
if (!m_swapAlt && fdv_KMAPEX[i].off_addr_nswAlt != 0xFF)
fdv_setKeyMap(fdv_KMAPEX[i].off_addr_nswAlt, fdv_KMAPEX[i].off_bit_nswAlt, type != SDL_KEYDOWN);
// switch on (down)
if (fdv_KMAPEX[i].on_addr_1 != 0xFF)
fdv_setKeyMap(fdv_KMAPEX[i].on_addr_1, fdv_KMAPEX[i].on_bit_1, type == SDL_KEYDOWN);
if (fdv_KMAPEX[i].on_addr_2 != 0xFF)
fdv_setKeyMap(fdv_KMAPEX[i].on_addr_2, fdv_KMAPEX[i].on_bit_2, type == SDL_KEYDOWN);
m_keyPress = true;
return true;
}
}
return false; // not handled
}
La chiamata a fdv_handle() viene effettuata all'interno di handleEvent(), per cui va modificato così:
int JoycePcwKeyboard::handleEvent(SDL_Event &e)
{
...
if (keysym == SDLK_LSHIFT || keysym == SDLK_RSHIFT)
m_trueShift = b;
if (fdv_handle(keysym, e.type))
return 1;
...
}
Commenti
Posta un commento