Sommario:

Rivisitare il computer Z80: 6 passaggi
Rivisitare il computer Z80: 6 passaggi

Video: Rivisitare il computer Z80: 6 passaggi

Video: Rivisitare il computer Z80: 6 passaggi
Video: LM80C: come opera la CPU Z80 2024, Dicembre
Anonim
Rivisitare il computer Z80
Rivisitare il computer Z80
Rivisitare il computer Z80
Rivisitare il computer Z80

In passato, ho scritto una guida su come costruire un computer basato su Z80 e ho progettato il circuito in modo che fosse il più semplice possibile in modo che potesse essere costruito il più facilmente possibile. Anch'io ho scritto un piccolo programma usando la stessa idea di semplicità. Questo design ha funzionato piuttosto bene, ma non ero totalmente soddisfatto. Ho iniziato riscrivendo un programma che permettesse di programmarlo durante il runtime. Questo era per permettermi di testare pezzi di codice senza doverlo dedicare alla EEPROM, il che a sua volta mi avrebbe richiesto di riprogrammare la EEPROM. Questa non mi sembrava un'idea divertente. Poi ho iniziato a pensare agli spazi di memoria. Se volessi interfacciare un pezzo di hardware (principalmente IO), un pezzo di codice potrebbe potenzialmente superare la quantità di spazio di memoria disponibile per il sistema. Ricorda, il progetto utilizzava solo il byte inferiore del bus degli indirizzi e quindi il bit inferiore del byte alto veniva utilizzato per selezionare tra gli spazi ROM e RAM. Ciò significava che avevo solo 253 byte di spazio da usare. Potresti chiederti perché 253 invece di 256. Questo perché il mio nuovo codice inietta tre byte di dati alla fine di un programma scritto (questo sarà trattato in seguito, poiché l'ho modificato per lavorare sul nuovo design).

n

Sono tornato sui miei vecchi schemi per vedere cos'altro stava succedendo. Ho trovato un piccolo difetto nel circuito di selezione della memoria, che tratterò quando sarò lì. La versione semplificata: tutte le richieste di scrittura sarebbero effettivamente andate a buon fine, anche se venivano sempre inserite nella RAM. Probabilmente non c'era nulla di cui preoccuparsi, ma questa volta volevo farlo correttamente. E con questo, ho iniziato a disegnare un nuovo schema. Le due foto allegate a questa pagina sono prima e dopo del circuito vero e proprio. Ho ripulito così tanto del cablaggio degli spaghetti, non è divertente.

n

Se hai seguito la mia presentazione originale e hai intenzione di seguire questa, mi odierai. Se stai iniziando da zero, allora sei fortunato. Basta prendere le parti nell'elenco (o il loro equivalente) e seguire.

Forniture:

LM7805 - Regolatore 5 Volt Z80 - la CPU; il cervello del sistemaAT28C64B - EEPROM. Memoria dati “permanente” utilizzata per il firmware del computerIDT6116SA - SRAM; utilizzato per la memorizzazione del codice utente e/o per la memorizzazione dei dati generaliNE555 - Orologio di sistema74HC374 - Octal D-Latch con /OE; utilizzato come chip di input74LS273 - Octal D-Latch con /MR; chip di uscitaTLC59211 - Chip driver LED (utilizzato in modo che il 74LS273 possa pilotare LED, poiché da solo non è in grado di fornire l'uscita in corrente) MC14572 - Questo è un chip "Line Driver", ma l'ho trovato perfetto per la logica di controllo della memoria. Dispone di 4 inverter e di un gate NAND e NOR integrato 74LS32 - Quad OR gateCD4001 - Quad NOR gateCD4040 - 12 Stage Ripple Counter; Divisore di clock disegnato, ma non implementato (per far funzionare il sistema a velocità di clock più basse) 2 resistori da 10 K Ohm - Uno è usato nel circuito del timer 555, quindi usa il valore che desideri per esso 4 resistori da 1 K Ohm - Uno è usato per il Circuito timer 555, quindi usa quello che desideri. Un altro è utilizzato per pilotare i LED, quindi varialo anche se desideri 8x330 Ohm Resistor Bus8x10K Ohm Resistor Bus11 LED - Tre sono usati per lo stato del sistema e gli altri otto sono uscite. Per l'8, ho usato un display grafico a barre (HDSP-4836)4 Condensatori - Due sono usati nell'LM7805; 0.22uF e 0.1uF. Uno è per il timer 555, quindi usa quello che ritieni giusto. L'ultimo è per il ripristino all'accensione; 100uF2 NO Pulsanti - Uno è utilizzato per l'ingresso, l'altro per il reset8 DIP Switch SPST - Ingresso dati; Ho usato lo stile Wire di Piano Key. Tanto, tanto filo

n

NOTA: la versione MC14572 con foro passante è obsoleta, ma è ancora attiva la versione SMD (nemmeno lo stato “non per nuovo design”), quindi potrebbe essere necessario acquistare una scheda elettronica per consentirne l'utilizzo. Un secondo 74LS32 può essere utilizzato al posto dell'MC14572 (fare riferimento allo schema del "circuito di selezione della memoria" del precedente ible)

Passaggio 1: panoramica rapida delle modifiche e degli schemi

Panoramica rapida delle modifiche e degli schemi
Panoramica rapida delle modifiche e degli schemi

Come leggere gli schemi:Una freccia puntata in un chip è un input:Input >-Una freccia puntata lontano da un chip è un output:Output <-I bus usano una linea invece di una freccia:Bus |-

n

La maggior parte dei chip sono stati estratti con i loro esatti pinout. Il piccolo tuffo è stato disegnato su questi chip. La maggior parte dei chip ha anche numeri di pin ed etichette. Potrebbero essere un po' difficili da leggere. La mia matita stava diventando opaca.

n

In termini di connessioni circuitali, il layout del nuovo design è per lo più invariato rispetto all'originale. Ho collegato il nibble inferiore dell'indirizzo high byte alle memorie e poi ho utilizzato il bit basso del nibble superiore (A12) per la selezione RAM/ROM. Ciò significava che lo spazio ROM è passato da 0000-00FF fino a 0000-0FFF. Lo spazio RAM è passato da 0100-01FF a 1000-1FFF. Ho anche scambiato la logica di controllo della memoria per un design migliore e ho aggiunto due nuovi LED di stato (e un po' di logica della colla). Ho anche disegnato (ma non ho cablato) un circuito divisore di clock. Doveva svolgere due funzioni. La funzione ovvia è quella di dividere la frequenza di clock verso il basso. L'altra funzione è per scopi PWM (Pulse Width Modulation), poiché il 555 non genera onde con cicli di lavoro del 50%. Questo non ha molta importanza in questo circuito, ma se volessi usare l'orologio per pilotare alcuni LED, noterai sicuramente gli effetti (un (set di) LED sarà più debole dell'altro). L'intero resto della circuiteria è sostanzialmente invariato.

Passaggio 2: CPU, memoria e controllo della memoria

CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria
CPU, memoria e controllo della memoria

Questa è la parte in cui i lettori della mia versione precedente mi odiano. Nella build originale, ho semplicemente lanciato parti sulla scheda in un punto in cui sembrava che avrebbero imposto pochi problemi con il cablaggio. Il risultato sembrava che qualcuno ci avesse rovesciato sopra un piatto di spaghetti ed era come "fili!" Volevo ripulirlo un po', quindi ho iniziato a strappare tutto tranne CPU, RAM e ROM. Ho tirato su quasi l'intero circuito di ingresso, circuito di uscita e la logica della colla. Mi faceva quasi male farlo, ma era necessario. Ho lasciato intatte tutte le connessioni dati e il byte inferiore del bus degli indirizzi. Ho quindi collegato i successivi quattro bit del bus degli indirizzi (A8-A11) al chip ROM. Questa volta ho avuto cura di aggirare il chip per renderlo più facile da recuperare per la riprogrammazione. Ho anche spostato le connessioni degli indirizzi fino al chip RAM.

n

Detto questo, ora dovevo collegare la logica di controllo della memoria. Nello schema originale, avevo collegato la linea /MREQ del processore direttamente a /CE a entrambi i chip di memoria, quindi ho collegato /WR a /WE della RAM. Quindi ho messo insieme /RD e /MREQ della CPU in modo logico OR insieme e A9. In sostanza, è stato impostato in modo che tutte le richieste di memoria attivassero sia la RAM che la ROM, ma A9 è stato utilizzato per selezionare quale dei chip / OE è stato selezionato. Questo andava bene e tutto perché i chip sarebbero rimasti inattivi fino a quando non fosse stata effettuata una richiesta di memoria e quindi solo un /OE sarebbe stato attivo durante una richiesta di lettura. Ciò ha impedito la diafonia, ma ha introdotto una sfumatura imbarazzante. Poiché A9 è stato utilizzato solo per determinare quale chip stava emettendo dati e poiché la CPU aveva accesso diretto al pin /WE della RAM, tutte le richieste di scrittura sarebbero andate a buon fine. Questo andava bene per la ROM perché la sua modalità di scrittura è inibita collegando /WE direttamente all'alimentazione 5V. La RAM, tuttavia, verrebbe scritta indipendentemente da A9. Ciò significava che un tentativo di scrittura in una posizione dello spazio ROM avrebbe scritto nella stessa posizione nello spazio della RAM.

n

Una soluzione per questo sarebbe quella di ricablare la logica di controllo in modo che la CPU abbia accesso diretto ai pin /OE e /WE dei chip e quindi utilizzare MREQ e A12 per selezionare quali chip /CE sono stati pilotati. Sono andato con questa idea, ma invece di usare quattro porte NOR e un inverter come il design originale, ho trovato un piccolo chip imbarazzante che era perfetto per il compito. Ho dovuto creare un circuito che utilizzasse solo le porte logiche disponibili nel chip, ma era abbastanza facile. A12 alimenta direttamente una porta NAND e una porta NOR. /MREQ viene immesso nella porta NOR e il suo complemento viene immesso nella porta NAND. La porta NAND viene utilizzata per pilotare /CE per la RAM e l'uscita NOR viene invertita e utilizzata per pilotare la ROM /CE. Questo fa in modo che /MREQ debba essere basso prima che uno dei due chip venga selezionato e quindi A12 sceglie quale viene selezionato. Con questa configurazione, ora qualsiasi richiesta di scrittura sulla ROM non farà nulla. Risparmia anche energia perché è attivo un solo chip invece di entrambi. Per quanto riguarda il chip logico stesso, abbiamo ancora due inverter inutilizzati all'interno. Uno si abituerà più tardi, ma ci arriveremo quando ci arriveremo.

Passaggio 3: LED di stato del sistema

LED di stato del sistema
LED di stato del sistema
LED di stato del sistema
LED di stato del sistema

Prima di iniziare questo progetto, stavo cercando di interfacciarmi con un certo IC, ma avevo problemi con esso. Incerto su cosa stesse succedendo, ho usato un LED per montaggio a pannello per sondare intorno (uno di quegli assemblaggi che ha un resistore integrato). Fare questo mi ha dato un'idea nostalgica che è ancora usata oggi: i LED di stato utilizzati per indicare se la memoria veniva letta o scritta. Doveva essere utilizzato in combinazione con il LED di ingresso che avevo già. Il LED di ingresso è stato collegato al generatore di segnale /WAIT per indicarci che il sistema è, beh, in attesa di ingresso (ci arrivo, non preoccuparti). Ho preso in considerazione l'aggiunta di un LED per indicare una scrittura IO, ma ho pensato che la modifica dei LED di uscita sarebbe già stata un ottimo indicatore di ciò. A pensarci bene, potrei ancora aggiungerlo. Tuttavia, trovo utile sapere se la memoria viene letta o scritta. Bene, è comunque utile per il debug del programma. In realtà ne ho fatto un uso pesante in quanto tale quando ho cercato di far funzionare il mio programma: perché sta scrivendo nella memoria? Non dovrebbe ancora farlo!”

n

Per controllare questi LED, ho usato il gate NOR quad. Ho usato tutte le porte. Solo due sono stati utilizzati per generare i segnali di stato, ma il chip non ha le capacità di alimentazione per pilotare effettivamente i LED. Sono in grado di assorbire così tanta potenza, quindi ho usato le altre due porte NOR come inverter e ho collegato i LED come tali. Poiché un LED viene utilizzato per indicare le letture e l'altro per le scritture e non si verificherà una richiesta di lettura e scrittura contemporaneamente, sono riuscito a farla franca utilizzando solo un resistore per entrambi i LED. Per quanto riguarda i segnali che dovevo decodificare, era anche abbastanza facile. Volevo che tutte le richieste di lettura della memoria venissero indicate, quindi la prima porta NOR aveva / MREQ e / RD sui suoi ingressi. Lo stato di scrittura era un po' più complicato, ma altrettanto facile. Ho ancora usato /MREQ come input, ma usare /WR come l'altro avrebbe causato una piccola sfumatura che volevo evitare. Avrebbe indicato TUTTE le richieste di scrittura. Volevo solo quelli che sono passati davvero. Quindi come lo farei? Bene, ricordi come ho impostato il sistema in modo che solo la RAM possa essere scritta? Ho usato le RAM /CE come altro input al gate NOR. Ciò significa che il LED si accenderà solo quando è selezionata la RAM e viene effettuata una richiesta di scrittura. In termini di colore del LED, ho scelto l'arancione come indicatore di lettura (ma ho trovato solo quelli gialli) e il rosso come indicatore di scrittura.

Passaggio 4: ingresso e uscita

Ingresso e uscita
Ingresso e uscita
Ingresso e uscita
Ingresso e uscita
Ingresso e uscita
Ingresso e uscita

Nel passaggio precedente, potresti aver notato che ho già aggiunto alcuni degli altri componenti alla scheda. Stavo riservando lo spazio in modo da non posizionare accidentalmente i cavi dove volevo un componente (quindi avrei dovuto trovare una nuova posizione per detto componente). Potresti anche aver notato che ho lasciato gli interruttori di ingresso al loro posto e li ho collegati alla barra di alimentazione. Ho deciso che la posizione originale era il punto perfetto e ho deciso di posizionare i LED di uscita nelle vicinanze (sopra). A destra del display a barre c'è il latch di input. Sopra c'è il latch di uscita e alla sua sinistra c'è il driver LED. Ho iniziato collegando il display al driver poiché era il più facile da fare. Quindi ho collegato gli interruttori al lato di ingresso del latch di ingresso. Successivamente ho collegato il lato di uscita del latch di uscita al driver LED. Questo può sembrare un ordine imbarazzante per farli cablare, ma c'era un motivo. L'ingresso dell'uscita latch doveva essere collegato al bus dati così come l'uscita dell'ingresso latch. L'idea era di collegare le uscite dell'input latch agli ingressi dell'output latch, cosa che ho fatto. Quindi tutto quello che dovevo fare era collegare quel pasticcio al bus dati. Non importava dove andassero fisicamente queste connessioni perché sarebbero state tutte collegate elettricamente. Ora il computer è quasi pronto.

Passaggio 5: ripristino e completamento di input e output

Spiacenti, nessuna foto per questo passaggio. Fare riferimento al passaggio precedente per le foto.

n

Potresti aver notato nell'ultima foto del passaggio precedente, ho installato un pulsante verde e un altro chip logico. Il chip è la porta OR. Vengono utilizzate due porte per generare il segnale /WAIT. Bene, si genera il segnale tramite OR /IORQ e /RD dal processore. L'uscita viene immessa nella seconda porta, dove viene nuovamente inviata in OR a un pulsante. Il pulsante porta alto l'ingresso del gate, portando così alto l'uscita. Questa uscita viene inviata al pin /WAIT del processore. Quando non è premuto, un resistore mantiene basso l'ingresso. Inizialmente ho usato un resistore da 10K, ma l'LS32 stava effettivamente emettendo tensione sull'ingresso. La resistenza non l'ha abbassata abbastanza e ho dovuto sostituirla con una da 1K. Ad ogni modo, l'idea è che quando viene effettuata una richiesta di lettura IO, la prima e la seconda porta OR dicono al processore di attendere. Una volta impostati gli interruttori di input su quello che vuoi, premi il pulsante e porta la CPU fuori dalla condizione di attesa. Il LED verde "input", come l'ho chiamato in un passaggio precedente, è cablato in modo che quando il pin /WAIT diventa basso, si accenda.

n

Ma non abbiamo ancora finito. Il flip flop di input ha bisogno di un segnale per sapere quando l'input dei dati è valido e deve essere inviato alla CPU. Questo pin di clock è attivo alto. Prima, lo collegavamo semplicemente al pulsante. Questa è ancora un'opzione valida, ma questa volta ho scelto di metterla sullo stesso output della seconda porta OR. Questo circuito integrato ha anche un pin /OE che deve essere guidato. Se fosse tenuto alto, non inserirebbe mai dati nel bus. Se tenuto basso, sarebbe sempre alla guida dell'autobus. Per risolvere questo problema, ho semplicemente usato una terza porta OR. Gli ingressi sono /IORQ e /RD e l'uscita va direttamente a /OE del latch.

n

Anche il latch di uscita necessita del pin di clock per essere pilotato. Di nuovo, è attivo alto. Nel mio schema, ho disegnato la quarta porta OR che pilota direttamente il pin usando /IORQ e /WR. Ciò significava che il pin dell'orologio sarebbe stato tenuto alto fino a quando non fosse stata effettuata una richiesta di scrittura, quindi sarebbe andato basso e poi di nuovo alto. Questo probabilmente sarebbe andato bene perché il bus dati avrebbe ancora avuto dati validi su di esso immediatamente dopo il tentativo di scrittura, ma da un punto di vista ingegneristico, era un design spazzatura. Non ho notato questo errore fino a quando non ho scattato le foto finali, ma ho strappato quella connessione e poi ho alimentato l'uscita del gate OR in uno degli inverter inutilizzati dalla logica di controllo della memoria, quindi ho collegato la sua uscita al pin dell'orologio. Ho anche corretto lo schema e ho trovato un altro errore che avevo commesso. L'ho corretto anch'io.

n

Fatto tutto ciò, avevo una piccola quantità di lavoro da fare: il circuito di ripristino. Ho aggiunto un pulsante alla scheda e ho usato un resistore da 10K per tenere alto un lato. L'altro lato va direttamente a terra. Il lato tenuto alto è l'uscita /RESET, che è andata a ogni chip con un pin /RESET (la CPU e il fermo di uscita). Per eseguire il ripristino all'accensione, ho aggiunto un condensatore all'uscita /RESET. L'idea è che il resistore di grande valore farebbe sì che il condensatore relativamente grande si carichi lentamente e mantenga bassi i pin /RESET per un certo numero di cicli di clock (la CPU ha bisogno di quattro cicli di clock). Probabilmente puoi già indovinare qual è il lato negativo di questo circuito. È lo stesso negativo della versione precedente perché è lo stesso circuito. Quando si preme il pulsante, il condensatore è sostanzialmente in cortocircuito attraverso il pulsante. Questo è un male sia per il cappuccio che per il pulsante, quindi se vuoi rendere la tua build un po' più permanente, potresti voler ridisegnarla. Stavo pensando ad un altro timer 555 impostato in modalità monostabile. Ma con ciò, il circuito del computer è ora finito. Sìì. Ora ha bisogno di essere programmato.

Passaggio 6: programmazione

Programmare questa cosa è stato un incubo. Ho costruito un programmatore EEPROM Arduino. Non ha funzionato. Ne ho costruito un altro basato sul design e sulla codifica di qualcun altro. Ancora non ha funzionato. Sono tornato al metodo collaudato di impostare manualmente gli indirizzi e i byte di dati. In qualche modo, l'ho incasinato. Ho riprovato e ancora ho sbagliato. Sono tornato ancora una volta e ho scoperto che era sbagliato di un singolo byte, quindi l'ho corretto e alla fine ha funzionato, grazie a Dio.

n

Per quanto riguarda il programma vero e proprio, sembra che sia super complesso e difficile da seguire, ma non lo è. È abbastanza semplice, in realtà. La metà sta copiando numeri in giro. L'altra metà è condivisa tra matematica a 16 bit, salti condizionali e ancora più numeri di copia in giro. Quindi lascia che lo esamini e ti spieghi come funziona.

n

L'inizializzazione imposta solo alcuni valori di registro per l'utilizzo da parte del programma. Il ciclo del programma è un po' più complesso, ma non molto. Innanzitutto, accetta l'input al registro A sulla porta 00. Quindi il registro E viene scritto in memoria. Nei primi due loop, il registro E contiene dati spazzatura, quindi proviamo a scriverli negli ultimi due byte di spazio ROM perché in realtà non verranno scritti; il puntatore dell'indirizzo (IY) viene quindi incrementato. Il valore memorizzato in D viene quindi spostato in E per essere successivamente scritto. A viene quindi caricato in D e L ed E viene copiato in H. HL è dove avviene il confronto dei valori tramite sottrazione e verifica ZF (zero flag). Il primo valore confrontato viene memorizzato nei registri B e C. B e C sono trattati come un unico registro a 16 bit, BC. Se i valori sono gli stessi, il programma salta direttamente nello spazio RAM, dove si presume che risieda il codice utente. Se il codice in BC non corrisponde, HL viene ricaricato con i valori iniziali di D ed E e viene confrontato nuovamente con il valore in SP nello stesso modo in cui è stato confrontato con BC. Se è una corrispondenza, ha lo stesso risultato, ma in memoria vengono scritti tre byte in più. I byte sono un codice che fa tornare la CPU all'inizio del suo programma (un reset del software). Se il secondo confronto non corrisponde, tuttavia, il programma esegue un ciclo nel punto in cui acquisisce un valore dall'utente.

n

LDSP, EDBFH; codice exe (aggiunge jump)

n

LD IY, FFEH; puntatore di memoria iniziale per la memorizzazione del codice

n

LD BC, EDC3H; codice exe (nessun ciclo)

n

ciclo continuo; direttiva assembler quindi non dobbiamo sapere dove risiede questa parte in memoria

n

IN A, (00H); ottenere i dati del programma

n

LD (IY+00H), E; E contiene il codice da memorizzare

n

INC IY; passa alla posizione di memoria successiva

n

LD E, D; ld D in E

n

LD D, A; ld A in D

n

LDH, E; ld E in H

n

LDL, D; ld D in L

n

O UN; ripristina porta bandiera

n

SBC HL, BC; restituisce 0 se è stato inserito il codice exe 2

n

JPZ, 1000H; se è così, salta ed esegui il programma

n

LDH, E; in caso contrario, aggiornali ai valori corretti

n

LD L, D

n

O UN; la prima sottrazione potrebbe aver impostato carry flag. Cancellalo

n

SBC HL, SP; restituisce 0 se è stato inserito il codice exe 1

n

JP NZ, ciclo; in caso contrario, ripetere il processo (iniziando con l'ottenimento di un valore)

n

LD (IY+00H), C3H; altrimenti, iniettare un codice di salto alla fine del programma utente

n

LD (IY+01H), 00H; jump agisce fondamentalmente come un reset del software

n

LD (IY+02H), 00H; è un reset completo nel caso in cui i registri siano stati modificati

n

JP 1000H; saltare ed eseguire il programma utente

Consigliato: