Translate

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).

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


Terminato il download procedere alla compilazione e all'installazione:


cd joyce-custom/
./configure
make
sudo make install


A questo punto Joyce è installato e funzionante e si può rimuovere la cartella con i sorgenti.
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:

    bool m_fdvAlt;
    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_1on_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

Post popolari in questo blog

Retro-restoring Amstrad PCW 8512

Amiga 600 con Gayle ver.1, implementazione VHDL del PAL di supporto

Configurare la tastiera con SETKEYS (Amstrad CPC 6128 - CP/M Plus)