diff --git a/.markdownlint.json b/.markdownlint.json index 1cdac62..324630d 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,5 +1,6 @@ { "MD033": false, - "MD013": false + "MD013": false, + "MD029": false } \ No newline at end of file diff --git a/docs/_docs/30-flags.md b/docs/_docs/30-flags.md index 42ab63d..4a34c3d 100644 --- a/docs/_docs/30-flags.md +++ b/docs/_docs/30-flags.md @@ -19,13 +19,13 @@ Il registro dei Flag dell'NQSAP emula i 4 flag **NVZC** del 6502: - **Z**ero (Z) - **C**arry (C) -E' completamente differente dal semplice registro dei Flag del computer SAP di Ben Eater, nel quale un unico registro tipo D 74LS173 memorizzava i soli 2 flag C e Z nello stesso momento: la gestione delle istruzioni necessitava di 4 set di microcode, cioè uno per ogni combinazione dei segnali di flag portati agli ingressi delle EEPROM; ogni set di microcode era infatti sviluppato "su misura" per attivare in output i corretti segnali per la gestione di C e/o Z. Questo è ben spiegato nel video di Ben Eater Conditional jump instructions. +E' completamente differente dal semplice registro dei Flag del computer SAP di Ben Eater, nel quale un unico registro tipo D 74LS173 memorizzava i soli 2 flag C e Z nello stesso momento: la gestione delle istruzioni necessitava di 4 set di microcode, cioè uno per ogni combinazione dei segnali di flag portati agli ingressi delle EEPROM; ogni set di microcode era infatti sviluppato "su misura" per attivare in output i corretti segnali per la gestione di C e/o Z. Questo è ben spiegato nel video di Ben Eater Conditional jump instructions. L'approccio di Tom era invece basato su una verifica logica eseguita in hardware: il microcode non variava a seconda dello stato dei flag, che non erano più direttamente connessi agli indirizzi delle ROM che attivano poi a loro volta diversi segnali di output in base all'indirizzo/flag presentato in ingresso! Analizzando ad esempio un'istruzione di salto condizionale legata al flag Z riscontravo che: -- il microcode dell'istruzione di salto attivava un generico segnale "Jump Enable" connesso al pin 7 del Data Selector/Multiplexer 74LS151 visibile in basso a destra nello schema generale; +- il microcode dell'istruzione di salto attivava un generico segnale "Jump Enable" connesso al pin 7 del Data Selector/Multiplexer 74LS151 visibile in basso a destra nello schema generale; - la selezione del flag da mettere in uscita sul '151 dipendeva dalla codifica dell'istruzione in esecuzione, poiché i 3 bit Select S2, S1 ed S0 erano direttamente collegati all'Instruction Register, cioè *hardwired*, in maniera similare a quanto realizzato anche nel modulo ALU; @@ -57,13 +57,13 @@ I miglioramenti derivanti da questa architettura sono: ## Componenti e funzionamento -Un multiplexer (MUX) 74LS157 prende in input i valori dei flag V, Z e C selezionandone la provenienza: +Un multiplexer (MUX) 74LS157 prende in input i valori dei flag V, Z e C selezionandone la provenienza: 1. **dal bus**; quando il '157 legge dal bus, è possibile caricare i registri dei flag leggendo valori arbitrari dalla memoria del computer (o, più precisamente, dalla zona di memoria adibita allo Stack) similarmente a quanto svolto dall'istruzione Pull Processor Status **PLP** del 6502; -2. **da un computo**: +2. **da un computo o da un altro modulo**: - **V** attraverso un Data Selector/Multiplexer '151 che ricrea la funzione logica dell'Overflow verificando un eventuale cambio di segno nel risultato delle operazioni di somma o sottrazione dei numeri con segno (Signed); - - **Z** come risultato del comparatore 74LS688; + - **Z** come risultato del comparatore 74LS688; - **C** attraverso un altro '151 che seleziona la sorgente del Carry; V, Z e C escono dal MUX '157 e sono presentati a 3 dei 4 Flip-Flop disponibili in una coppia di 74LS74. @@ -251,7 +251,7 @@ La necessità di inviare al modulo ALU non solo il valore reale del flag C, ma a La negazione del segnale inviato in ingresso al Carry Input del '181 deriva dal fatto che la configurazione utilizzata dall'ALU (logica attiva alta, “Active-High data”) richiede un segnale Carry In [invertito](../alu/#funzioni-logiche-e-operazioni-aritmetiche). -Si noti che la Truth Table della tabella richiederebbe i componenti evidenziati nello schema seguente, ma l'applicazione del teorema di De Morgan permette la semplificazione utilizzata poi nello schema adottato nell'NQSAP. +Si noti che la Truth Table della tabella richiederebbe i componenti evidenziati nello schema seguente, ma l'applicazione delle leggi di De Morgan permette la semplificazione utilizzata poi nello schema adottato nell'NQSAP e nel BEAM. ![Schema originale per realizzazione della Truth Table di selezione del Carry](../../assets/flags/30-flag-c-h-alu-de-morgan.png){:width="50%"} @@ -273,7 +273,7 @@ Da notare che il computer NQSAP prevedeva 8 step per le microistruzioni, mentre ## Link utili -- I video di Ben Eater CPU flags register e Conditional jump instructions, che spiegano la costruzione del modulo dei Flag e le modifiche necessarie al microcode per la gestione delle istruzioni di salto condizionale. Si noterà la differenza con l'approccio dell'NQSAP, che non richiede microcode ad-hoc per ogni flag e non abbisogna di linee di indirizzamento EEPROM dedicate. +- I video di Ben Eater CPU flags register e Conditional jump instructions, che spiegano la costruzione del modulo dei Flag e le modifiche necessarie al microcode per la gestione delle istruzioni di salto condizionale. Si noterà la differenza con l'approccio dell'NQSAP, che non richiede microcode ad-hoc per ogni flag e non abbisogna di linee di indirizzamento EEPROM dedicate. - Tom segnala di aver preso ispirazione da un thread su Reddit How to add a decremental and incremental circuit to the ALU ? per l'idea di pilotare il caricamento del [Program Counter](../programcounter/) dal registro dei Flag anziché gestirli con copie multiple del microcode come avveniva come sul SAP di Ben Eater. diff --git a/docs/_docs/40-control.md b/docs/_docs/40-control.md index c1f4efb..44ec28c 100644 --- a/docs/_docs/40-control.md +++ b/docs/_docs/40-control.md @@ -141,7 +141,7 @@ Nel BEAM, ad esempio, l'istruzione LDA #$94 (che nel linguaggio mnemonico del 65 | ---- | -------------------------- | ~~~ -*Scomposizione dell'istruzione LDA nelle sue quattro microistruzioni elementari*. +*Scomposizione dell'istruzione LDA Immediato nelle sue quattro microistruzioni elementari*. 1. Il primo step carica l'indirizzo del Program Counter nel Memory Address Register: - RPC, Read Program Counter - espone sul bus l'indirizzo del Program Counter @@ -155,16 +155,16 @@ Nel BEAM, ad esempio, l'istruzione LDA #$94 (che nel linguaggio mnemonico del 65 - WM, Write Memory Address Register - scrive il contenuto del bus nel MAR 4. Il quarto ed ultimo step carica l'operando nell'accumulatore*, incrementa il PC per farlo puntare alla istruzione successiva e resetta il Ring Counter - RR, Read RAM - espone sul bus il contenuto della locazione di memoria puntata dal MAR - - FNZ, Flag N & Z - abilita la scrittura dei Flag N e Z - - WAH, Write A & H - scrive il contenuto del bus su A e H* + - FNZ, Flag N & Z - aggiorna i Flag N e Z + - WAH, Write A & H - scrive il contenuto del bus in A e H** - PCI, Program Counter Increment - incrementa il Program Counter - - NI, Next Instruction - resetta il Ring Counter** + - NI, Next Instruction - resetta il Ring Counter*** -\* Perché anche H? Si veda la sezione dedicata alla spiegazione del [registro H](../alu/#il-registro-h) nella pagina dell'ALU. +\* Non bisogna trascurare il fatto che i primi due step di *tutte* le istruzioni sono *sempre* identici. Alla fine del secondo step, l'Instruction Register contiene l'opcode dell'istruzione, che, insieme alle microistruzioni, definisce le operazioni che gli step successivi devono eseguire. Questo vale per qualsiasi istruzione, compresa la prima che una CPU esegue all'accensione. Prima di costruire il SAP di Ben Eater, non riuscivo a immaginare quale meccanismo permettesse ad una CPU di sapere cosa dovesse fare una volta accesa; l'averlo compreso è stato piuttosto appagante. -\*\* Approfondimenti in merito nella sezione [Lunghezza delle istruzioni](#lunghezza-delle-istruzioni) in questa stessa pagina. +\*\* Perché anche H? Si veda la sezione dedicata alla spiegazione del [registro H](../alu/#il-registro-h) nella pagina dell'ALU. -Non bisogna trascurare il fatto che i primi due step di *tutte* le istruzioni sono *sempre* identici. Alla fine del secondo step, l'Instruction Register contiene l'opcode dell'istruzione, che, insieme alle microistruzioni, definisce le operazioni che gli step successivi devono eseguire. Questo vale per qualsiasi istruzione, compresa la prima che una CPU esegue all'accensione. Prima di costruire il SAP di Ben Eater, non riuscivo a immaginare quale meccanismo permettesse ad una CPU di sapere cosa dovesse fare una volta accesa; l'averlo compreso è stato piuttosto appagante. +\*\*\* Approfondimenti in merito nella sezione [Lunghezza delle istruzioni](#lunghezza-delle-istruzioni) in questa stessa pagina. Uno schema che mostra chiaramente gli step di alcune istruzioni del SAP è visibile in questa immagine tratta dal video 8-bit CPU control logic: Part 3 di Ben Eater; gli step 000 e 001 sono comuni per tutte le istruzioni e compongono quella che viene chiamata **Fase Fetch**, evidenziata in giallo. diff --git a/docs/_docs/50-alu.md b/docs/_docs/50-alu.md index 020c8e6..7829871 100644 --- a/docs/_docs/50-alu.md +++ b/docs/_docs/50-alu.md @@ -15,7 +15,7 @@ Il '181 è un'ALU a 4 bit sviluppata negli anni '70 che può eseguire 16 operazi **Utilizzo dei termini '181 e ALU come equivalenti**: in questa pagina, troveremo i termini ALU e '181 che vengono spesso utilizzati come equivalenti. Notare che ALU potrebbe indicare sia il modulo Unità Aritmetica e Logica nella sua interezza, sia il solo chip '181. Il contesto aiuterà a comprendere se per ALU si intenda il modulo, oppure il solo chip. -Inoltre, poiché nel modulo si utilizzano due '181 per poter comporre una word di 8 bit, in determinate situazioni si definiscono primo e secondo '181 oppure '181 inferiore e superiore rispettivamente quello che contiene i 4 bit meno significativi (LSB) e quello che contiene i 4 bit più significativi (MSB). +Inoltre, poiché nel modulo si utilizzano due '181 per poter comporre una word di 8 bit, in determinate situazioni si definiscono primo e secondo '181 oppure '181 inferiore e superiore rispettivamente quello che contiene i 4 bit meno significativi (Least Significant Bit, LSB) e quello che contiene i 4 bit più significativi (Most Significant Bit, MSB). ## Il registro A @@ -27,7 +27,7 @@ Peraltro, il registro A dell'NQSAP e del BEAM sono molto simili dal punto di vis ## L'ALU dell'NQSAP -Tra le caratteristiche che spiccavano nello schema dell'ALU dell'NQSAP, notavo soprattutto un numero elevato di chip - tra i quali gli Shift Register 74LS194 - e un modo particolare di indirizzare i '181, che erano "strettamente legati" all'istruzione presente nell'Instruction Register della [Control Logic](../control). Anche il legame con la Control Logic è stato tra i più complessi da analizzare e comprendere, ma quello con il modulo dei Flag non è meno importante e la sua comprensione è stata altrettanto difficile: ad ogni operazione dell'ALU (e non solo) corrisponde infatti un'azione sul registro dei Flag. +Tra le caratteristiche che spiccavano nello schema dell'ALU dell'NQSAP, notavo soprattutto un numero elevato di chip - tra i quali i 4-bit Bidirectional Universal Shift Register 74LS194 - e un modo particolare di indirizzare i '181, che erano "strettamente legati" all'istruzione presente nell'Instruction Register della [Control Logic](../control). Anche il legame con la Control Logic è stato tra i più complessi da analizzare e comprendere, ma quello con il modulo dei Flag non è meno importante e la sua comprensione è stata altrettanto difficile: ad ogni operazione dell'ALU (e non solo) corrisponde infatti un'azione sul registro dei Flag. [![Schema dell'ALU di Tom Nisbet](../../assets/alu/50-alu-nqsap.png "Schema dell'ALU di Tom Nisbet"){:width="100%"}](../../assets/alu/50-alu-nqsap.png) @@ -63,23 +63,60 @@ Gli Shift Register '194 sono utilizzati anche per le operazioni di scorrimento e | LO | LO | Mantiene lo stato precedente | | LO | HI | Scorre a sinistra i bit di output (Q3 ← Q2 \| Q2 ← Q1 \| Q1 ← Q0) e carica l'input Serial Right in Q0 | | HI | LO | Scorre a destra i bit di output (Q3 → Q2 \| Q2 → Q1 \| Q1 → Q0) e carica l'input Serial Left in Q3 | -| HI | HI | Carica gli input P0-P3 in Q0, Q1, Q2 e Q3 | +| HI | HI | Carica gli input P0-P3 in Q0-Q3 | -Lo schema mostra l'esecuzione di un'operazione di scorrimento. L'attivazione del segnale di controllo HL prepara i '194 per lo scorrimento da destra a sinistra. +Lo schema mostra l'esecuzione di un'operazione di scorrimento da destra a sinistra, che richiede l'attivazione del segnale di controllo HL: [![Scorrimento a sinistra nel registro H del BEAM](../../assets/alu/50-alu-beam-h.png "Scorrimento a sinistra nel registro H del BEAM"){:width="100%"}](../../assets/alu/50-alu-beam-h.png) *Scorrimento a sinistra nel registro H del BEAM.* -Al rising Edge del clock i '194 caricano su Q0 gli ingressi Shift Right (Dsr) e gli output Q0-Q3 vengono traslati verso sinistra: +Al rising Edge del clock i '194 traslano verso sinistra gli output Q0-Q3 e caricano in Q0 i valori presenti agli ingressi Serial Right (Dsr), eseguendo tutte le seguenti operazioni nello stesso momento: -- il segnale H-Cin presente all'ingresso del '194 di destra diventa il bit meno significativo (LSB) del byte traslato -- H3 dello stesso '194 diventa il 5° bit del byte traslato -- H7 viene perso, ma nelle operazioni di scorrimento, il bit "uscito" viene sempre salvato sul carry. Questa operazione è effettuata dal microcode, che prima di fare la rotazione salva H7 su C, effettivamente memorizzando nel Carry il valore più significativo del byte +- il '194 di destra carica in Q0/H0 il segnale H-Cin presente al suo ingresso Dsr, che diventa il nuovo LSB del byte traslato; +- il '194 di sinistra carica in Q0/H4 il valore di H3 presente al suo ingresso Dsr; +- gli ouput Q0-Q3 di entrambi i '194 scorrono verso sinistra. -ad esempio, mostrare microcode e immagine ASL +Le istruzioni di scorrimento / rotazione a sinistra del 6502 memorizzano il bit più significativo nel Carry. L'output H-Q7 evidenziato in giallo è connesso al modulo dei Flag e viene salvato dal microcode dell'operazione: - Nota che nello schema il '194 è rappresentato con gli output Q0, Q1, Q2 e Q3 rispettivamente equivalenti a QA, QB, QC e QD indicati nel datasheet del '194. +~~~text +| ---- | -------------------------- | +| Step | Microistruzione | +| ---- | -------------------------- | +| 1* | RPC | WM | +| 2* | RR | WIR | PCI | +| 3 | C1 | FS | FC | +| 4 | HL | CC | +| 5 | FNZ | RH | WA | NI | +| ---- | -------------------------- | +~~~ + +*Scomposizione dell'istruzione ASL Accumulatore nelle sue cinque microistruzioni elementari*. + +3. Il terzo step memorizza il valore di H7 nel Carry: + - C1 - seleziona la [provenienza del Carry](../flags/#carry) dall’MSB (H-Q7) del registro H + - FS, Flag Select - origine del Flag, in questo caso [da un altro modulo](../flags/#componenti-e-funzionamento) + - FC, Flag C - aggiorna il Flag C +4. Trasla il contenuto del registro H verso sinistra e carica uno zero nell'LSB + - HL, H Left - esegue le tre operazioni descritte poco sopra + - CC, Carry Clear - presenta un [valore 0](../flags/#il-carry-e-i-registri-h-e-alu) all'input di H +5. Scrive i Flag N e Z, copia H in A + - RH, Read H - espone il contenuto del Registro H sul bus + - FNZ, Flag N & Z - aggiorna i Flag N e Z + - WA, Write A - scrive il contenuto del bus in A + - NI, Next Instruction - resetta il Ring Counter + +che verso un altro modulo del BEAM a nelle operazioni di scorrimento, il bit "uscito" viene sempre salvato sul carry. Questa operazione è effettuata dal microcode, che prima di fare la rotazione salva H7 su C, effettivamente memorizzando nel Carry il valore più significativo del byte + +L'immagine mostra le istruzioni di scorrimento e rotazione del 6502: il bit in uscita viene sempre salvato sul Carry. + +![Istruzioni di scorrimento e rotazione del 6502](../../assets/alu/50-alu-shift-rotate-6502.png "Istruzioni di scorrimento e rotazione del 6502"){:width="66%"} + +*Istruzioni di scorrimento e rotazione del 6502.* + +\* I primi due step di tutte le istruzioni sono sempre uguali, come spiegato in [Ring Counter e Microistruzioni](../control/#ring-counter-e-microistruzioni). + + Nota che nello schema il '194 è rappresentato con gli output Q0, Q1, Q2 e Q3 rispettivamente equivalenti a QA, QB, QC e QD indicati nel datasheet del '194. Vista la flessibilità e l'utilità del Registro H, questo è stato implementato anche nel BEAM, con una differenza: l'NQSAP implementa scorrimento e rotazione a sinistra sfruttando l'operazione A Plus A dei '181, mentre il BEAM sfrutta i '194 sia verso sinistra sia verso destra. diff --git a/docs/assets/alu/50-alu-beam-h.png b/docs/assets/alu/50-alu-beam-h.png index 7fa1929..3a813cb 100644 Binary files a/docs/assets/alu/50-alu-beam-h.png and b/docs/assets/alu/50-alu-beam-h.png differ diff --git a/docs/assets/alu/50-alu-shift-rotate-6502.png b/docs/assets/alu/50-alu-shift-rotate-6502.png new file mode 100644 index 0000000..ab553f7 Binary files /dev/null and b/docs/assets/alu/50-alu-shift-rotate-6502.png differ