| 
    
   
   
  Il Program Counter (letteralmente, contatore del programma) o
  Instruction Pointer è uno speciale registro del processore che
  contiene l' indirizzo della prossima istruzione (opcode) da eseguire nella
  memoria programma. 
  
  
  Il Program Counter, o PC, è il contatore 
  ordinale del processore, ovvero il sistema con cui vengono messe in sequenza e
  avanzate le istruzioni. 
  
    
      
          | 
        
           La sua caratteristica,
           
  
          
          in tutti i PIC, è quella
          di essere inizializzato al reset in modo da partire con il
          conteggio dalla locazione 0000h, ovvero dal vettore del
          RESET.
          In questa locazione si troverà la PRIMA istruzione
          che verrà eseguita.  
          La sua lunghezza in bytes e la sua funzione stabilirà il valore
          successivo del PC, che andrà a puntare all' istruzione logicamente
          seguente. 
           | 
       
     
   
  
   
  Il suo funzionamento si può schematizzare così: 
  
    - al Reset, il Program Counter viene precaricato con l' indirizzo che
      contiene la prima istruzione da eseguire. 
 
    - Il meccanismo del Program Counter fa si che il contenuto della locazione
      di memoria a quell' indirizzo venga prelevato, decodificato ed eseguito. 
 
    - a seconda di quale opcode si tratti, il  Program Counter viene aggiornato con l'
      indirizzo che contiene l' istruzione successiva.
 
    - e così via
 
   
  
    
      
        | Nei PIC, l'indirizzo iniziale al Reset è posto a 0000h, che è definito come
  
  Vettore del Reset. | 
       
     
   
  Qui trova posto la prima istruzione da eseguire. La direttiva: 
      ORG  0000h 
         prima_istruzione 
  serve a posizionare l' istruzione a questo punto della memoria programma. 
   
  
    
      
        | Per inciso, va notato che la direttiva ORG
          non fa parte dei codici di istruzione inseriti nella memoria
          programma, ma è semplicemente un "ordine" inviato all'
          Assembler per posizionare l' istruzione che segue in quella
          determinata posizione.
         | 
       
     
   
  L' avanzamento del Program Counter lungo la lista delle istruzioni  è
  automatico, nel senso che la decodifica di ogni istruzione informa il
  Program Counter di quale sarà l' indirizzo successivo. 
  Consideriamo un esempio: 
  
    
      
        |   | 
        Memoria programma  | 
        P.C. | 
        
           Operazione  | 
       
      
        | 0 | 
        
           PowerOnReset  | 
        0000h | 
        Al Power On Reset (POR), il Program Counter viene caricato con l'
      indirizzo del Vettore di Reset. | 
       
      
        | 1 | 
        0000 | 
        nop | 
        0001h | 
        All' indirizzo della memoria programma  0000h viene prelevata la
      prima istruzione. La sua decodifica indica che si tratta di una istruzione
      generica lunga 1 byte, per cui il P. C. viene incrementato di 1,
      puntando alla successiva a 0001h. | 
       
      
        | 2 | 
        0001 | 
        movlw
          0x7F | 
        0002h | 
        Lì viene prelevato il contenuto, che è di nuovo una istruzione
      generica lunga 1 byte. Il Program Counter viene ulteriormente
      incrementato  | 
       
      
        | 3 | 
        0002 | 
        movwf
          TRISC | 
        0003h | 
        e così via. | 
       
      
        | 4 | 
        0003 | 
        
               . . . 
         | 
          | 
          | 
       
     
   
  Questo meccanismo è valido anche per le  chiamate di subroutine, dove viene
  abbandonata la lista principale per eseguire una porzione di codice posta in
  un altra zona della memoria programma. 
  L' istruzione  CALL (o RCALL) fa si che il Program Counter punti alla locazione
  di inizio della subroutine chiamata. 
  La conclusione della subroutine è costituita dall' istruzione RETURN
  (o RETLW). Le istruzioni di
  chiamata hanno modificato il Program Counter con il nuovo indirizzo, ma hanno
  anche fatto si che il contenuto del Program Counter stesso, ovvero l'
  indirizzo dell' istruzione seguente a quella di chiamata, venisse salvato
  nello stack.  
  Ora l' istruzione di ritorno recupera questo indirizzo e lo ricarica nello
  stack: la prossima istruzione che eseguita sarà quella successiva alla  CALL. 
  Consideriamo un esempio: 
  
    
      
        |   | 
        Memoria programma | 
        P.C. | 
        Stack | 
        Operazione | 
       
      
        | 0 | 
        
           POR  | 
        0000 | 
        - | 
        POR - P.C.= 0000h | 
       
      
        | 1 | 
        0000 | 
        nop | 
        0001 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 2 | 
        0001 | 
        call 
          SUB1 | 
        0050 | 
        0003 | 
        Chiamata a subroutine - 2 bytes | 
       
      
        | 3 | 
        0003 | 
        bsf  
          PORTC,7 | 
        0003 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 4 | 
        0004 | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
        . | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
          | 
          | 
          | 
          | 
          | 
       
      
        | 5 | 
        0050 | 
        SUB1 movlw 
          0x7F | 
        0051 | 
        0003 | 
        Entry subroutine - 1 byte | 
       
      
        | 6 | 
        0051 | 
              movwf  
          TRISC | 
        0052 | 
        0003 | 
        Istruzione a 1 byte | 
       
      
        | 7 | 
        0052 | 
              return | 
        0003 | 
        - | 
        Rientro da subroutine | 
       
      
        | 8 | 
        0053 | 
        SUB2   ... | 
        . | 
        . | 
          | 
       
     
   
  
    - Al Power On Reset (POR), il Program Counter viene caricato con l'
      indirizzo del Vettore di Reset.
 
    - All' indirizzo della memoria programma  0000h viene prelevata la
      prima istruzione. La sua decodifica indica che si tratta di una istruzione
      generica lunga 1 byte, per cui il Program Counter viene incrementato di 1,
      puntando a 0001h.
 
    - Lì viene prelevato il contenuto, che è una istruzione di chiamata a
      subroutine (call).
      La sua decodifica produce le seguenti operazioni:
 
      - Il contenuto del Program Counter, che  contiene l' indirizzo dell'
      istruzione successiva alla call
      viene copiato nello stack 
      - il contenuto del Program Counter viene sostituito con l' indirizzo della
      subroutine che è l'oggetto della chiamata (SUB1
      ). 
    - Di conseguenza il Program Counter punta alla locazione dove si trova la
      prima istruzione della subroutine, che esegue. Nell' esempio si tratta di
      una istruzione generica lunga 1 byte, per cui il Program Counter viene
      incrementato di 1, puntando a 0051h.
 
    - Qui preleva il successivo opcode, che dà risultati come il precedente.
 
    - Nella locazione seguente viene incontrata una istruzione return
      . Questa à luogo al seguente risultato:
 
      - il contenuto del Program Counter, che avrebbe puntato all' istruzione
      successiva (SUB2 ) viene invece
      sostituito con il il contenuto dello stack. Il quale conserva l' indirizzo
      precedentemente salvato a seguito dell' istruzione call. 
    - Ne deriva che ora il Program Counter punta "all' indietro"
      all' istruzione successiva alla call,
      riprendendo il flusso del programma principale.
 
   
  Va quindi considerato che il Program Counter, in alcune condizioni, si
  appoggia allo stack per conservare questo indirizzo di rientro. 
  
    
      
        Da notare che lo stack è una "pila" che
          viene caricata in seguito alle necessità del Program Counter e
          scaricata in conseguenza delle istruzioni di ritorno. 
          Nell' esempio sopra, fino al punto 2 lo stack è vuoto. Qui viene
          caricato un primo indirizzo. 
          Al punto 7, quando l' istruzione di ritorno sostituisce il contenuto
          del Program Counter con il contenuto dello stack, lo stack si trova di
          nuovo ad essere vuoto.
         | 
       
     
   
  Dunque, ricapitolando,  CALL
  o RCALLL settano un valore
  specifico nel Program Counter e fanno si che esso conservi l' indirizzo dell'
  istruzione successiva alla  CALL. 
  RETURN  (o RETLW)
  fanno si che il Program Counter recuperi questo indirizzo e che quindi l'
  istruzione successiva da eseguire sia quella dopo la  CALL. 
  Del tutto analoga la chiamata ad interrupt, che agisce come per la
  subroutine, ma con alcune differenze: 
  
    - prima di tutto, la chiamata ad interrupt non comporta istruzioni, ma
  viene generata dall' hardware specifico, che modifica il Program Counter
 
    - in secondo luogo, mentre nella chiamata a subroutine l' istruzione di
      chiamata informa il Program Counter dell' indirizzo di destinazione, che
      può essere qualunque nell' area di memoria programma indirizzabile, nell'
      interrupt esiste un indirizzo specifico unico verso cui dirigersi
 
   
  Questo indirizzo, detto  Vettore dell' Interrupt, per i PIC è posto a 0004h (e
  0008h) ed è questo valore che il meccanismo avviato dalla sorgente di
  interruzione carica nel Program Counter.  
  Ne deriva che la prima istruzione eseguita dopo l' interruzione è quella
  posta al vettore di interrupt. 
  Anche l' interruzione, come dice il nome, è una sospensione momentanea dell'
  esecuzione del programma principale, a cui veine fatto ritorno attraverso un
  sistema identico a quello visto per le subroutines:  
  
    - alla chiamata dell' interrupt, per prima cosa il contenuto del PC viene
      salvato nello stack
 
    - poi viene caricato l' indirizzo del vettore di interrupt
 
   
  Ovvero anche qui lo stack serve da deposito dell' indirizzo di ritorno, a
  cui si accede a seguito dell' istruzione RETFIE. 
  
    
      
        
            
          ATTENZIONE:
          se è chiaro quanto finora detto, una linea che comandi 
              
          GOTO  Vettore_Interrupt 
          oppure 
               GOTO
          Subroutine 
          creerà rilevanti problemi all' esecuzione del programma. 
           
          Infatti, a seguito di queste istruzioni, il Program Counter punterà
          al Vettore di Interrupt o all' inizio della subroutine. 
          Abbiamo visto come l' istruzione di salto modifichi il Program
          Counter, ma non operi alcun salvataggio di indirizzi di
          "ritorno", che non sono previsti. 
          Però, ad un certo punto la gestione dell' interrupt o la subroutine
          avranno termine con una istruzione di ritorno (RETURN, RETLW, RETFIE). 
          La quale per prima cosa provvederà ad estrarre dallo stack l'
          indirizzo di ritorno e iniettarlo nel Program Counter. Ma questo
          indirizzo di ritorno NON c'è e dallo stack verrà estratto il
          primo indirizzo della pila col risultato di inviare il Program Counter
          ad eseguire istruzioni in un punto casuale del programma, se
          non in un' area vuota !!! 
          Per contro, un: 
              
          GOTO  Vettore_diReset 
          sarà possibile, in quanto si limiterà ad iniziare il programma
          dal suo punto di ingresso, che è il vettore del Reset. 
          Ma anche in questo caso va considerato che il salto al vettore di
          Reset NON è un Reset, nè tanto meno un POR !!! 
          Infatti il Reset generato dal pin MCLR o dal POR non
          solo modificano il contenuto del Program Counter caricando il vettore
          0000h, ma svolgono anche numerose altre funzioni, come i
          settaggi di default degli I/O ed altre inizializzazioni che il
          semplice salto all' indirizzo 0000h NON svolge. 
             | 
       
     
   
  Dunque, allo scatto dei meccanismi hardware di interrupt, il Program
  Counter abbandona la lista di istruzioni che sta eseguendo, salva l'
  indirizzo dell' istruzione successiva a quella appena eseguita e viene
  caricato con l' indirizzo del Vettore di Interrupt. 
  RETFIE  fa si che il Program Counter recuperi l' indirizzo salvato e
  che quindi l' istruzione prossima da eseguire sia esattamente la successiva a
  quella dove la lista primaria è stata interrotta. 
  Ma esiste un ulteriore gruppo di istruzioni, dette  salti o branch o jump
  (GOTO, BRA,
  BTFSS, BTFSC, ecc) hanno la funzione di modificare il Program Counter. 
  Esse modificano il PC caricandolo con l' indirizzo specificato dall' istruzione stessa. In questo
  modo la prossima istruzione da eseguire non sarà, ad esempio, quella dopo il GOTO, ma quella indicata nell' oggetto del
  GOTO stesso. 
  Qui, però, a differenza della chiamata di una subroutine, dove viene
  salvato un indirizzo di ritorno, i jump e branch  non prevedono alcun  ritorno  e
  la continuità del flusso del programma dovrà essere assicurata  dalla logica  coerente
  dello stesso.  
  Consideriamo un esempio: 
  
    
      
        |   | 
        
           Memoria programma 
         | 
        
           P.C. 
         | 
        Stack | 
        Operazione | 
       
      
        | 0 | 
        
           POR  | 
        0000 | 
        - | 
          | 
       
      
        | 1 | 
        0000 | 
        nop | 
        0001 | 
        - | 
          | 
       
      
        | 2 | 
        0001 | 
        goto   
          JUMP1 | 
        0050 | 
        - | 
        
           GOTO - cambia P.C. ma non salva nulla nello stack  | 
       
      
        | 3 | 
        0003 | 
        bsf    
          PORTC,7 | 
        0003 | 
        - | 
          | 
       
      
        | 4 | 
        0004 | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
        . | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
          | 
          | 
          | 
          | 
          | 
       
      
        | 5 | 
        0050 | 
        JUMP1 movlw 
          0x7F | 
        0051 | 
        - | 
          | 
       
      
        | 6 | 
        0051 | 
              movwf 
          TRISC | 
        0052 | 
        - | 
          | 
       
      
        | 7 | 
        0052 | 
               movlw 
          0x55 | 
        0053 | 
        - | 
          | 
       
      
        | 8 | 
        0053 | 
               ... | 
        . | 
        . | 
          | 
       
     
   
  
    - Al punto 2, viene incontrata una istruzione di salto (goto).
      La sua esecuzione produce le seguenti operazioni:
 
      - Il contenuto del Program Counter, che  contiene l' indirizzo dell'
      istruzione successiva al goto 
      viene sostituito con l' indirizzo del salto che è l'oggetto della
      chiamata (JUMP1 ). 
    - Di conseguenza il Program Counter punta a questa nuova locazione, dove
      preleva il codice e lo passa all'  esecuzione
 
   
  Da osservare che lo stack non viene modificato in quanto NON esiste una
  istruzione di ritorno da un salto, per cui l' esecuzione delle istruzioni
  continua linearmente. 
  Questo vuol dire che l' istruzione alla locazione 0003h e seguenti non saranno
  eseguite a meno che vengano puntate con una istruzione che cambi il contenuto
  del Program Counter puntando a queste locazioni.  
  Come detto, tutti questi sono meccanismi automatici in cui l' utente non mettere mano. 
  Ma è possibile attraverso istruzioni modificare in tutto o in parte il
  contenuto del Program Counter e anche lo stack ed attraverso queste
  manipolazioni ottenere funzioni particolari. 
  E' evidente la delicatezza con cui è necessario intervenire su questi
  registri da cui dipende la corretta esecuzione del programma. 
   
  E' opportuno notare anche che le operazioni determinate dalle istruzioni di
  ritorno (RETURN, RETLW, RETFIE)
  consistono esclusivamente nel
  prelevare dallo stack l' ultimo indirizzo salvato. Ovvero, se è
  stata effettuata una chiamata a subroutine in un certo punto, ad esempio all'
  indirizzo 0345h, il   di quella subroutine NON contiene nè è
  collegato al l' indirizzo di rientro, nell' esempio 0347h) ! 
   
  Il collegamento è effettuato dal fatto che gli indirizzi di ritorno vengono
  salvati sovrapponendoli nella pila dello stack e da questa prelevati nell'
  ordine opposto al caricamento (LIFO = last in first out - l' ultimo messo è
  il primo ad essere prelevato). 
  Questo meccanismo funziona se la sequenza di salvataggio e recupero nello
  stack non viene alterata da latre azioni, ad esempio un intervento manuale, o,
  come indicato prima, un salto senza rientro   che in contra durante
  l' esecuzione un rientro senza chiamata. 
  In questo caso viene scaricato dallo stack l' ultimo valore contenuto, che non
  corrisponderà ad alcun rientro, alterando l' esecuzione del programma. Questo,
  però, si presta ad alcuni trucchi, tra i quali il fatto che qualsiasi
  chiamata a subroutine (o interrupt) può rientrare con qualsiasi istruzione
  di  , dovunque essa sia posta. 
  Ad esempio: 
   
    
      
        |   | 
        Memoria programma | 
        P.C. | 
        Stack | 
        Operazione | 
       
      
        | 0 | 
        
           POR  | 
        0000 | 
        - | 
        POR - P.C.= 0000h | 
       
      
        | 1 | 
        0000 | 
        nop | 
        0001 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 2 | 
        0001 | 
        call 
          SUB1 | 
        0050 | 
        0003 | 
        Chiamata a subroutine - 2 bytes | 
       
      
        | 3 | 
        0003 | 
        bsf  
          PORTC,7 | 
        0003 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 4 | 
        0004 | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
        . | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
          | 
          | 
          | 
          | 
          | 
       
      
        | 5 | 
        0050 | 
        SUB1 btfsc 
          PORTC,7 | 
        0051 | 
        0003 | 
        Entry subroutine - 1 byte | 
       
      
        | 6 | 
        0051 | 
              return | 
        0003 | 
        - | 
        Rientro da subroutine | 
       
      
        | 7 | 
        0052 | 
                    bsf  
          PORTC,6 | 
          | 
        - | 
          | 
       
      
        | 8 | 
        0053 | 
        SUB2 bsf  
          PORTC,7 | 
        - | 
        - | 
          | 
       
      
        | 9 | 
        0054 | 
             return | 
        . | 
        - | 
          | 
       
     
   
  
    - Al punto 2, indirizzo 0001h,  il programma incontra una  call
      .
 
      Lo stack  viene caricato con l' indirizzo dell' istruzione 
      successiva (003h) e il P.C. con l' indirizzo di destinazione del salto 
    - Viene eseguita la prima istruzione della SUB1
      .
 
    - Se il bit 7 di  è a 1, il branch non è eseguito e l' istruzione
      successiva è un return.
 
    - Il return 
      preleva dallo stack l' indirizzo di rientro e riporta il P.C. al punto 3
 
      Lo stack si è svuotato. 
   
  Se però il branch viene eseguito: 
   
  
    
      
        |   | 
        Memoria programma | 
        P.C. | 
        Stack | 
        Operazione | 
       
      
        | 0 | 
        
           POR  | 
        0000 | 
        - | 
        POR - P.C.= 0000h | 
       
      
        | 1 | 
        0000 | 
        nop | 
        0001 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 2 | 
        0001 | 
        call 
          SUB1 | 
        0050 | 
        0003 | 
        Chiamata a subroutine - 2 bytes | 
       
      
        | 3 | 
        0003 | 
        bsf  
          PORTC,7 | 
        0003 | 
        - | 
        Istruzione a 1 byte | 
       
      
        | 4 | 
        0004 | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
        . | 
            . | 
        . | 
        . | 
          | 
       
      
        | . | 
          | 
          | 
          | 
          | 
          | 
       
      
        | 5 | 
        0050 | 
        SUB1 btfsc 
          PORTC,7 | 
        0052 | 
        0003 | 
        Entry subroutine - 1 byte | 
       
      
        | 6 | 
        0051 | 
              return | 
          | 
        0003 | 
        non eseguita - saltata | 
       
      
        | 7 | 
        0052 | 
                    bsf  
          PORTC,6 | 
        0053 | 
        0003 | 
        Istruzione a 1 byte | 
       
      
        | 8 | 
        0053 | 
        SUB2 bsf  
          PORTC,7 | 
        0054 | 
        0003 | 
        Istruzione a 1 byte | 
       
      
        | 9 | 
        0054 | 
             return | 
        0003 | 
        - | 
        Rientro da subroutine | 
       
     
   
  
    - Se il bit 7 di  è a 0, il branch  è eseguito e l' istruzione
      successiva è bsf
      PORTC,6.
 
      Lo stack non viene toccato. 
    - Viene quindi eseguita l' istruzione alla riga 7.
 
    - Viene poi eseguita l' istruzione alla riga 8. Il fatto che a questa
      posizione sia applicata una label, non ha alcuna importanza in questa
      situazione.
 
    - L' istruzione successiva, al punto 9, è un return.
 
    - Il return 
      preleva dallo stack l' indirizzo di rientro, che non è stato modificato,
      e riporta il P.C. al punto 3. Lo stack si è svuotato.
 
   
  Osservare quindi come la relazione tra stack e P.C. sia di pura dipendenza
  sequenziale. 
  E come la funzione delle label sia relativa al fatto di avere valore per la
  logica del programma e per l' assemblaggio, sostituendo semplicemente con una
  "etichetta" un indirizzo assoluto.
    
    
    
  
    
      
         Ultima
          nota: RETURN, RETLW, RETFIE
          non sono la stessa cosa, pur operando similmente
           
            - RETURN
              ha lo scopo specifico di effettuare il rientro da una call,
              chiamata a subroutine.
 
              Il suo effetto è quello di recuperare dallo stack un indirizzo
              per il P.C. 
            -  RETLW agisce
              esattamente come la precedente, ma in più carica il registro WREG
              con il valore literal che è oggetto dell' istruzione stessa. Il
              suo scopo è di rientrare da una chiamata con un valore
              indicativo, ma è anche parte essenziale delle tabelle RETLW
 
            -  RETFIE
              è specificamente indicata per il rientro dopo una
              chiamata di interrupt. Ha sempre la funzione di recuperare dallo
              stack un  indirizzo per il P.C., ma aggiunge l' operazione di
              abilitare il bit GIE (switch generale dell' interrupt), che era
              stato disabilitato all' ingresso della chiamata dell' interrupt. 
 
           
          Sarebbe dunque possibile utilizzare anche  per un ritorno da
          subroutine, ma l' abilitazione dello switch generale dell'interrupt
          potrebbe non essere una buona idea. 
          Inoltre, nei PIC18F, il suffisso  FAST sommato all' istruzione, fa si
          che i registri salvati automaticamente al momento della chiamata
          interrupt vengano recuperati. 
          Dunque è opportuno utilizzare ogni opcodes nell' ambito previsto.
          | 
       
     
   
   
    
   
  |