/*
 * ESERCIZIO: LED SHIFT CON CALCOLO PRECISO DEI RITARDI
 * =====================================================
 * 
 * Hardware Setup per Wokwi:
 * - LED1 collegato a Pin 8  (PB0) con resistenza 220Ω
 * - LED2 collegato a Pin 9  (PB1) con resistenza 220Ω
 * - LED3 collegato a Pin 10 (PB2) con resistenza 220Ω
 * - LED4 collegato a Pin 11 (PB3) con resistenza 220Ω
 * 
 * Pattern: I LED si accendono in sequenza con shift a sinistra
 * 
 * CALCOLO DEI RITARDI
 * ===================
 * 
 * Clock Arduino Uno: 16 MHz
 * Periodo di clock: T = 1 / 16.000.000 = 62.5 ns (nanosecondi)
 * 
 * Ogni istruzione assembly impiega un certo numero di cicli di clock.
 * Tempo esecuzione = Numero_Cicli × 62.5 ns
 * 
 * TABELLA TEMPI ISTRUZIONI AVR PRINCIPALI:
 * ┌─────────────────┬────────────┬──────────────┐
 * │   Istruzione    │   Cicli    │  Tempo @16MHz│
 * ├─────────────────┼────────────┼──────────────┤
 * │ NOP             │     1      │   62.5 ns    │
 * │ MOV, LDI        │     1      │   62.5 ns    │
 * │ ADD, SUB, AND   │     1      │   62.5 ns    │
 * │ OR, EOR, COM    │     1      │   62.5 ns    │
 * │ LSL, LSR, ROL   │     1      │   62.5 ns    │
 * │ OUT, IN         │     1      │   62.5 ns    │
 * │ LDS, STS        │     2      │  125.0 ns    │
 * │ LD, ST          │     2      │  125.0 ns    │
 * │ SUBI, SBCI      │     1      │   62.5 ns    │
 * │ CPI             │     1      │   62.5 ns    │
 * │ BRNE, BREQ      │   1/2      │  62.5/125 ns │
 * │ RJMP, JMP       │     2      │  125.0 ns    │
 * │ CALL            │   3/4      │ 187.5/250 ns │
 * │ RET             │     4      │  250.0 ns    │
 * │ PUSH            │     2      │  125.0 ns    │
 * │ POP             │     2      │  125.0 ns    │
 * └─────────────────┴────────────┴──────────────┘
 * 
 * Nota: BRNE impiega 1 ciclo se non salta, 2 cicli se salta
 */
// Definizione maschere per i LED sui pin PB0-PB3
#define LED_MASK 0x0F  // 0b00001111 - maschera per 4 LED
// Variabile globale per il pattern corrente
volatile uint8_t ledPattern = 0b00000001;
void setup() {
  Serial.begin(9600);
  Serial.println("=== LED SHIFT CON ANALISI TIMING ===\n");
  
  // Configurazione PORTB: Pin 8-11 (PB0-PB3) come output
  // Manipolazione diretta del registro DDR
  DDRB |= LED_MASK;  // Imposta i primi 4 bit di DDRB a 1 (output)
  
  // Inizializza PORTB con tutti i LED spenti
  PORTB &= ~LED_MASK;
  
  Serial.println("Pin configurati:");
  Serial.println("- Pin 8  (PB0) -> LED1");
  Serial.println("- Pin 9  (PB1) -> LED2");
  Serial.println("- Pin 10 (PB2) -> LED3");
  Serial.println("- Pin 11 (PB3) -> LED4");
  Serial.println();
  
  // Stampa configurazione iniziale registri
  Serial.print("DDRB iniziale:  0b");
  Serial.println(DDRB, BIN);
  Serial.print("PORTB iniziale: 0b");
  Serial.println(PORTB, BIN);
  Serial.println();
}
/*
 * FUNZIONE DI RITARDO PRECISO
 * ============================
 * 
 * Per creare un ritardo di 500 ms usando un loop, calcoliamo:
 * 
 * 1. Tempo desiderato: 500 ms = 500.000 µs = 500.000.000 ns
 * 
 * 2. Cicli necessari: 500.000.000 ns / 62.5 ns = 8.000.000 cicli
 * 
 * 3. Un loop tipico di ritardo in assembly:
 *    loop_delay:
 *        subi r24, 1      ; 1 ciclo  - sottrai 1 dal contatore
 *        sbci r25, 0      ; 1 ciclo  - sottrai con carry
 *        brne loop_delay  ; 2 cicli se salta, 1 se esce
 *    
 *    Totale per iterazione: 4 cicli (quando salta)
 *    Ultima iterazione: 3 cicli
 * 
 * 4. Iterazioni necessarie: 8.000.000 / 4 ≈ 2.000.000
 * 
 * 5. Overhead della chiamata funzione:
 *    - CALL: 4 cicli
 *    - Inizializzazione parametri: ~4 cicli
 *    - RET: 4 cicli
 *    Totale overhead: ~12 cicli
 * 
 * FORMULA GENERALE per ritardo in ms:
 * 
 *   Iterazioni = (ms × 16.000 - Overhead) / CicliPerLoop
 *   
 * Per 500 ms:
 *   Iterazioni = (500 × 16.000 - 12) / 4 = 1.999.997 ≈ 2.000.000
 */
void delayPrecise_500ms() {
  /*
   * Implementazione con loop in C che il compilatore ottimizza
   * 
   * NOTA: Il compilatore GCC con -O2/-O3 ottimizza questo loop
   * generando codice assembly efficiente simile a quello mostrato sopra.
   * 
   * Analisi assembly generato (approssimativo):
   * ----------------------------------------
   * Per ogni iterazione del loop esterno (1000 volte):
   *   - Inizializzazione j = 2000: ~2 cicli (LDI)
   *   - Loop interno con decremento e confronto: ~4 cicli per iterazione
   *   - 2000 iterazioni × 4 cicli = 8000 cicli
   *   - Overhead loop esterno: ~4 cicli
   *   
   * Totale per iterazione esterna: 8004 cicli
   * 1000 iterazioni × 8004 = 8.004.000 cicli
   * Tempo: 8.004.000 × 62.5 ns = 500.25 ms
   * 
   * Precisione: ±0.25 ms (errore < 0.05%)
   */
  
  for (volatile uint16_t i = 0; i < 1000; i++) {
    for (volatile uint16_t j = 0; j < 2000; j++) {
      // Ogni NOP aggiunge 1 ciclo = 62.5 ns
      asm volatile("nop");
    }
  }
}
/*
 * VERSIONE ALTERNATIVA: Ritardo con Assembly Inline
 * ==================================================
 * 
 * Per massima precisione, possiamo scrivere il ritardo in assembly inline
 */
void delayAsm_500ms() {
  /*
   * Implementazione diretta in assembly
   * 
   * Obiettivo: 500 ms = 8.000.000 cicli
   * 
   * Strategia: Loop annidato
   * - Loop esterno: 1000 iterazioni (r18:r19)
   * - Loop interno: 2000 iterazioni (r24:r25)
   * - Ogni iterazione interna: 4 cicli
   * 
   * Calcolo dettagliato:
   * 1. Caricamento costanti: 4 cicli (trascurabile)
   * 2. Loop esterno (1000 volte):
   *    a. Carica counter interno: 4 cicli
   *    b. Loop interno (2000 × 4 = 8000 cicli)
   *    c. Decrementa counter esterno: 1 ciclo
   *    d. Confronta e salta: 2 cicli (media)
   *    Totale: 8007 cicli per iterazione esterna
   * 3. Totale: 1000 × 8007 = 8.007.000 cicli
   * 4. Tempo: 8.007.000 × 62.5 ns = 500.44 ms
   */
  
  asm volatile (
    "    ldi r24, lo8(2000)     \n\t"  // Carica parte bassa di 2000
    "    ldi r25, hi8(2000)     \n\t"  // Carica parte alta di 2000
    "    ldi r18, lo8(1000)     \n\t"  // Carica parte bassa di 1000
    "    ldi r19, hi8(1000)     \n\t"  // Carica parte alta di 1000
    "outer_loop:                \n\t"
    "    ldi r24, lo8(2000)     \n\t"  // Reset counter interno
    "    ldi r25, hi8(2000)     \n\t"
    "inner_loop:                \n\t"
    "    sbiw r24, 1            \n\t"  // Sottrai 1 da r25:r24 (2 cicli)
    "    brne inner_loop        \n\t"  // Salta se non zero (2 cicli se salta)
    "    sbiw r18, 1            \n\t"  // Decrementa counter esterno (2 cicli)
    "    brne outer_loop        \n\t"  // Salta se non zero (2 cicli se salta)
    :
    :
    : "r24", "r25", "r18", "r19"       // Registri utilizzati (clobber list)
  );
}
void loop() {
  // Stampa stato corrente
  Serial.print("Pattern: 0b");
  Serial.print(ledPattern, BIN);
  Serial.print(" | LED acceso: ");
  
  // Identifica quale LED è acceso
  for (int i = 0; i < 4; i++) {
    if (ledPattern & (1 << i)) {
      Serial.print(i + 1);
      Serial.print(" (Pin ");
      Serial.print(i + 8);
      Serial.print(")");
    }
  }
  Serial.println();
  
  // Aggiorna PORTB con il pattern corrente
  // Preserva gli altri bit di PORTB (pin 12 e 13)
  PORTB = (PORTB & ~LED_MASK) | (ledPattern & LED_MASK);
  
  /*
   * ANALISI TEMPORALE DELL'ISTRUZIONE PRECEDENTE:
   * =============================================
   * 
   * PORTB = (PORTB & ~LED_MASK) | (ledPattern & LED_MASK);
   * 
   * Assembly generato (approssimativo):
   * 1. IN   r24, PORTB           ; 1 ciclo  - Leggi PORTB
   * 2. LDI  r25, ~LED_MASK       ; 1 ciclo  - Carica maschera negata
   * 3. AND  r24, r25             ; 1 ciclo  - Applica maschera
   * 4. LDS  r26, ledPattern      ; 2 cicli  - Carica ledPattern da SRAM
   * 5. LDI  r27, LED_MASK        ; 1 ciclo  - Carica maschera
   * 6. AND  r26, r27             ; 1 ciclo  - Applica maschera
   * 7. OR   r24, r26             ; 1 ciclo  - Combina
   * 8. OUT  PORTB, r24           ; 1 ciclo  - Scrivi su PORTB
   * 
   * Totale: 9 cicli = 562.5 ns
   * 
   * Confronto con digitalWrite() [~50 cicli = 3.125 µs]:
   * Speedup: 50 / 9 = 5.5x più veloce!
   */
  
  // Stampa timing
  Serial.print("PORTB dopo:  0b");
  Serial.println(PORTB, BIN);
  Serial.print("Tempo aggiornamento PORTB: ~562.5 ns (9 cicli)\n");
  Serial.println();
  
  // Ritardo usando la funzione precisa
  // Usa la versione in C (più portabile) o assembly (più precisa)
  delayPrecise_500ms();
  // delayAsm_500ms();  // Alternativa con assembly inline
  
  /*
   * SHIFT DEL PATTERN
   * =================
   * 
   * Operazione: ledPattern <<= 1
   * 
   * In assembly AVR:
   * LSL r24              ; 1 ciclo - Logical Shift Left
   * 
   * Sequenza completa:
   * 1. LDS r24, ledPattern   ; 2 cicli - Carica da SRAM
   * 2. LSL r24               ; 1 ciclo - Shift a sinistra
   * 3. STS ledPattern, r24   ; 2 cicli - Salva in SRAM
   * 
   * Totale: 5 cicli = 312.5 ns
   * 
   * NOTA: Lo shift in C viene compilato in una singola istruzione LSL
   * se il compilatore ottimizza correttamente!
   */
  
  // Shift a sinistra del pattern
  ledPattern <<= 1;
  
  // Se il pattern esce dalla maschera dei 4 LED, riparti da LED1
  if ((ledPattern & LED_MASK) == 0) {
    ledPattern = 0b00000001;
    Serial.println(">>> Reset pattern <<<");
    Serial.println();
  }
  
  /*
   * CALCOLO TEMPO TOTALE UN CICLO COMPLETO:
   * =======================================
   * 
   * 1. Stampe seriali: variabile (dipende dal baud rate)
   * 2. Aggiornamento PORTB: 9 cicli = 562.5 ns
   * 3. Ritardo: ~8.000.000 cicli = 500 ms
   * 4. Shift: 5 cicli = 312.5 ns
   * 5. Controllo reset: ~10 cicli = 625 ns
   * 
   * Totale (senza seriale): ~500 ms
   * 
   * Precisione: La seriale introduce jitter, ma il timing
   * dei LED è determinato principalmente dal ritardo preciso.
   * 
   * Per applicazioni real-time critiche, rimuovere Serial.print()
   * e usare solo manipolazione diretta dei registri.
   */
}
/*
 * DOMANDE DI AUTOVALUTAZIONE
 * ==========================
 * 
 * 1. Quanti cicli di clock impiega l'istruzione LSL su AVR?
 *    a) 1 ciclo ✓
 *    b) 2 cicli
 *    c) 4 cicli
 *    d) Dipende dal valore
 * 
 * 2. Con un clock di 16 MHz, quanto tempo impiega un'istruzione
 *    che richiede 4 cicli?
 *    a) 62.5 ns
 *    b) 125 ns
 *    c) 250 ns ✓
 *    d) 500 ns
 * 
 * 3. Perché usiamo (PORTB & ~LED_MASK) prima di impostare i nuovi LED?
 *    a) Per velocizzare l'esecuzione
 *    b) Per preservare gli altri bit di PORTB non usati dai LED ✓
 *    c) Per evitare flicker
 *    d) È obbligatorio in AVR
 * 
 * 4. Quante volte più veloce è la manipolazione diretta rispetto a digitalWrite()?
 *    a) 2x
 *    b) 3x
 *    c) 5.5x ✓
 *    d) 10x
 * 
 * 5. In un loop di ritardo, perché BRNE impiega 2 cicli quando salta?
 *    a) Per controllare la condizione
 *    b) Perché deve modificare il Program Counter ✓
 *    c) Per sincronizzazione
 *    d) È un errore del datasheet
 */
/*
 * ESERCIZI PROPOSTI
 * =================
 * 
 * 1. Modifica il codice per far lampeggiare i LED in sequenza inversa
 *    (da LED4 a LED1) usando shift a destra (>>).
 * 
 * 2. Calcola e implementa un ritardo di 250 ms usando la stessa tecnica.
 *    Mostra i calcoli nei commenti.
 * 
 * 3. Crea un pattern "ping-pong": i LED vanno da sinistra a destra
 *    e poi tornano indietro senza reset.
 * 
 * 4. Implementa un contatore binario a 4 bit usando i 4 LED.
 *    Calcola quanti cicli impiega l'operazione di incremento.
 * 
 * 5. Usa un timer interrupt invece del ritardo software per gestire
 *    lo shift dei LED. Calcola la configurazione del timer per 500 ms.
 * 
 * 6. Misura il tempo reale di esecuzione usando micros() e confrontalo
 *    con i calcoli teorici. Spiega eventuali differenze.
 */