Una volta scelta la CPU ho iniziato a studiarla. Nonostante abbia usato computer ad 8 bit, all’epoca ero troppo giovane per cui non ho mai a disegnare o progettare uno di quei sistemi. Fortunatamente, la scelta di una CPU molto comune è stata ricompensata perché internet è pieno di risorse per lo Z80, perciò non è stato difficile trovare quello che stavo cercando. Inoltre, Zilog continua a produrre e vendere il processore Z80 per cui trovare informazioni aggiornate è stato un compito facile.
Nella figura qui sotto potete vedere la piedinatura del processore Z80:
Ecco una breve descrizione dei pin che serviranno ai miei scopi:
- pin di indirizzamento A0..A15: come potete ben capire, questi piedini sono utilizzati per “indirizzare” la memoria. Questo significa che sono usati per specificare l’indirizzo fisico della locazione di memoria da cui la CPU vuole leggere o scrivere un dato.
- pin dati D0..D7: questi piedini sono utilizzati per trasportare il dato dalla/alla CPU.
- M1: questo piedino è particolare. Per capire la sua funzione bisogna capire come lavora lo Z80. Non opera per singoli segnali di clock ma per cicli macchina (M), 3 cicli-M per ogni istruzione. Il primo ciclo macchina è chiamato “M1” ed è utilizzato per recuperare dalla memoria l’istruzione da eseguire. Il pin M1, insieme al pin MREQ, indica che la CPU sta recuperando l’istruzione. È usato anche insieme al pin IORQ per segnalare che è stato recepito un segnale di interrupt.
- MREQ: questo pin è usato per segnalare che il bus indirizzi contiene un valore valido per un ciclo di lettura/scrittura da/per la memoria.
- IORQ: questo pin segnala che i primi 8 piedini del bus indirizzi (A0..A7) contengono un indirizzo di periferica valido.
- WR: esso indica che la CPU vuole scrivere (WR sta per Write) ad un indirizzo di memoria o su una periferica di I/O.
- RD: opera in maniera simile a WR ma serve per le operazioni di lettura.
- REFSH: quasi inutile. Era utilizzato per “rinfrescare” le Dynamic RAM (DRAM) ma oggi le Static RAM (SRAM) sono più convenienti e non necessitano più dei cicli di rinfresco.
- HALT: la CPU Z80 può essere completamente fermata durante il suo funzionamento. Mentre è ferma, essa continua ad eseguire l’istruzione NOP per rinfrescare la memoria, in attesa di un segnale esterno di interrupt che le faccia riprendere la sua attività.
- WAIT: questo pin era utile quando le memorie erano più lente della CPU, venendo usato per far sì che la CPU attendesse che la memoria avesse terminato l’operazione di lettura/scrittura.
- INT e NMI: questi sono i pin di interrupt. Questi piedini indicano che la CPU deve eseguire specifiche routine di interrupt. La differenza fra i 2 è semplice: mentre INT è mascherabile, vale a dire che il programmatore può impostare un flag per far sì che la CPU ignori il segnale INT, l’interrupt NMI non lo è ((NMI significa proprio Non Maskable Interrupt) e sarà perciò sempre eseguito.
- RESET: questo pin è usato per resetter la CPU.
- BUSREQ e BUSACK sono usati per condividere i bus dati e indirizzi con periferiche esterne. Il primo indica alla CPU che una periferica esterna vuole il controllo dei bus mentre il secondo indica che la CPU ha lasciato il controllo di tali bus.
- CLK: questo è il pin di ingresso del clock. Il clock è un segnale speciale: esso va alternativamente alto e basso. Il segnale di clock è usato dalla CPU come un temporizzatore per sincronizzare tutte le sue operazioni interne. È come un direttore d’orchestra che da il tempo a tutti i componenti della banda. La frequenza massima del clock dipende dal tipo di CPU: lo Z80 originale può operare a frequenze massime di 2,5 MHz, lo Z80A fino a 4 MHz, lo Z80B fino a 6 MHz e lo Z80H fino ad 8 MHz. Versioni più recenti sono capaci di lavorare a 10 ed anche a 20 MHz. Il mio Z80 è un modello “B”.
Per i miei scopi ignorerò diversi pin. Userò per ora i pin dati ed indirizzi (ovviamente…), quelli IORQ e MREQ con la coppia WR/RD, ed il pin di clock, eventualmente i pin INT e NMI.
Prima di procedere ho dovuto testare la mia CPU. Dato che il mio Z80 è un processore di seconda mano ho dovuto verificare che fosse un esemplare funzionante. Per fare questo ho trovato su internet uno schema molto semplice. Lasciate che vi spieghi come funziona. Quando la CPU riceve l’alimentazione essa carica nel suo Program Counter (registro PC) l’indirizzo $0000, poi salta a tale locazione, legge l’istruzione lì memorizzata e la esegue. L’istruzione più semplice è NOP, che sta per “No Operation”. Quest’istruzione è farlocca: la CPU non esegue niente mentre la esegue a parte incrementare il registro PC per caricare l’istruzione successiva. L’op-code, il valore numerico che rappresenta questa istruzione, è $00: se noi impostiamo il bus dati in modo che la CPU legga $00, stiamo dicendo alla CPU che l’istruzione da eseguire è proprio NOP. Per far ciò dobbiamo solo connettere i pin dati a massa così che la CPU legga 8 valori bassi. Per verificare che la CPU stia lavorando possiamo collegare alcuni LED ai primi pin indirizzi: dato che NOP è una specie di segnaposto, la CPU incrementa il PC per recuperare l’istruzione successiva dalla memoria di sistema. Questo dovrebbe portare all’indirizzo di memoria che viene incrementato indefinitamente. Se usiamo un clock molto basso possiamo osservare questa cosa come un contatore binario con i LED che si accendono quando i pin indirizzi sono impostati ad un livello alto (“1”) e spengono quando impostati ad un livello basso (“0”). Grazie alla capacità dello Z80 di lavorare a frequenze di clock molto, molto basse, possiamo costruire un semplice circuito oscillatore RC con una logica NOT data da un 7414 Schmitt-trigger per generare l’onda quadra che la CPU si aspetta sul pin CLK. Lo schema è il seguente:
I pin indirizzi della CPU non sono molto robusti per cui possono emettere solo una corrente limitata: per questo motivo ho collegato i LED usando resistori con un alto valore per essere sicuri che la corrente sarà limitata ad un valore molto basso. Ho usato dei resistori da 680 ohm che, per i LED rossi, corrispondono a circa 2,5 mA per pin. Lo stesso per il bus dati: non ho connesso direttamente a massa ma ho usato dei resistori di pull-down da 1 Kohm ciascuno. Ho usato un 74HCT14 Schmitt-Trigger hex inverter (una logica NOT) per generare il clock ad onda quadra: esso scatta solo quando la tensione sul pin di ingresso va sotto oppure sopra certi valori, evitando di scattare per una minima fluttuazione del segnale. Con il circuito su mostrato la frequenza ottenuta è di 12 Hz, ma voi potete variare I valori di R1 e C1 per ottenere tempi di oscillazione differenti, vale a dire diverse velocità di clock. Ho aggiunto un pulsante di reset perché la CPU necessita di un clock stabile prima di lavorare correttamente: quando diamo tensione al circuito le piccole oscillazioni della tensione potrebbero portare a comportamenti imprevisti, per cui è raccomandabile che una volta data alimentazione al circuito venga applicato un segnale di reset di diversi cicli di clock. Questo non è necessario se si utilizza un chip di reset dedicato oppure un differente circuito basato su un NE555 in modalità monostabile, come vedremo in seguito.
Ecco cosa si vede dal test: