T&T - PIC

 

 

Far calcolare  MPASM


Far calcolare l' Assembler MPASM

L' Assembler non serve solamente a compilare istruzioni, ma ha anche possibilità di calcolo che spesso il programmatore neofita non utilizza.

Ad esempio, MPASM è in grado di valutare, durante la compilazioni, espressioni logiche e aritmetiche e con questo sollevare il programmatore da un lavoro a volte complicato o noioso.

Il calcolatore dell' Assembler lavora in modo analogo a quello dei linguaggi più complessi ed anche le dichiarazioni possono assumere aspetto molto simile, ma è importante comprendere che questo lavoro avviene DURANTE la compilazione e non ha niente a che fare con la struttura logica del programma.

MPASM riconosce una serie di operatori logici e aritmetici :

Operatore Funzione Esempio
! NOT if ! (a == b)
- Negazione (complemento a 2) -1 * Length
~ Complemento flags = ~flags
high high byte movlw high CTR_Table
low low byte movlw low CTR_Table
upper upper byte movlw upper CTR_Table
* Moltiplicazione a = b * c
/ Divisone a = b / c
% Modulo entry_len = tot_len % 16
+ Addizione tot_len = entry_len + 1
- Sottrazione entry_len = tot - 1
<< Left Shift flags = flags << 1
>> Right Shift flags = flags >> 1
>= Maggiore o uguale if entry_idx >= num_entries
> Maggiore if entry_idx > num_entries
< Minore if entry_idx < num_entries
<= Minore o uguale if entry_idx <= num_entries
== Uguale if entry_idx == num_entries
= Non uguale if entry_idx != num_entries
& Bitwise AND flags = flags & ERROR_BIT
^ Bitwise XOR flags = flags ^ ERROR_BIT
| Bitwise OR flags = flags | ERROR_BIT
&& AND logico if (len == 512) && (b == c)
|| OR logico if (len == 512) || (b == c)
= Set equal to entry_index = 0
+= Add and set entry_index += 1
-= Subtract and set entry_index -= 1
*= Multiply and set entry_index *= entry_length
/= Divide and set entry_total /= entry_length
%= Modulus and set entry_index %= 8
<<= Ledt shift and set flags <<= 3
>>= Right shift and set flags >>= 3
&= AND and set flags &= ERROR_FLAG
|= OR and set flags |= ERROR_FLAG
^= XOR and set flags ^= ERROR_FLAG
++ Incremento i ++
-- Decremento ì --

Questi operatori, tipici del C, sono poco conosciuti in Assembly, ma permettono diverse e potenti strutture anche in questo ambito.


AVVERTENZA IMPORTANTE:

Gli operatori logici aritmetici dell' Assembler, assieme alle direttive condizionali (if, while, ecc), anche se simili a quelli del C, NON sono codici di istruzione o generano codici di istruzione !

Questi operatori, nell' Assembly, servono per realizzare assemblaggi condizionali.

Quindi NON agiscono sulla logica del programma, ma sulla struttura dell' assemblaggio e non trasformano il Macro Assembler in un linguaggio C.


Detto questo, vediamo alcuni esempi.


Operatore incrementa/decrementa (++/--)

Il suo scopo è di incrementare (o decrementare una variabile), ad esempio in un loop per inserire una serie di istruzioni a seconda del valore di una variabile o di una costante.

; Loop for rotate register
some equ 4

LoopCounter = some       ; set counter
  while  LoopCounter >0
       rrncf  register
loopCounter --
  endw

E' da considerare che questo loop while riguarda non l' esecuzione del programma, ma la sua compilazione.
Fissata la variabile some, l' istruzione
rrncf register  viene ripetuta nel listato un equivalente numero di volte.


Operatori di confronto (>=, <=, <, >, ==, !=)

Some può essere una costante, ma può essere anche il risultato di una o più operazioni, dipendenti da altre variabili o condizioni.

; set some depending from environment
; condition
s
 
  #if  ((UserTag >= 8) | (Debug = 1))
some = (UserTag - 4)
  #else
some = ((UserTag /2) + 1)
  #endif

Va osservato che una sequenza condizionale come quella presentata qui sopra viene eseguita UNA sola volta al momento dell' assemblaggio, fornendo un listato differente a seconda dei valori impostati per UserTag  e Debug.
Quindi questa sequenza NON viene eseguita nel programma a seconda del valore assunto al momento da variabili durante l' esecuzione del programma.

Un altro esempio :

; check bits in a register
 
  while (i < 8              ; for 8 times
    #if  ((Register & 1) != 0  ; if bit 0 is set
    .. instructions...
    #endif
 
; shift right for check next bit
    Register = Register >> 1    
i = i + 1 
  endw 

Ecco un utile check automatico di superamento dello spazio durante le assegnazioni in RAM con lo statement CBLOK.
La label di una assegnazione "vuota" (dummy) che occupa 0 bytes viene attribuita all' ultima locazione di RAM assegnata; quindi viene verificato se il suo indirizzo supera quello del banco (in questo caso il Banco0, Access Ram). Se supera viene segnalato un errore.:

  CBLOCK 0x00   ; bank 0
    ssTXBuf
    ssRXCnt
    DlyTcyCnt   ; for DelayUS
    ...etc...

 end_AccBank0:0 ; dummy for overrun check
  ENDC

 #if end_Accbank0 > 0x7F ; check for overrun
   error "AccessRAM Bank0 space overrun"
 #endif

Con questa semplice aggiunta non c'è il rischio di aver assegnato memoria al di fuori dei limiti consentiti e non c'è bisogno di effettuare alcun calcolo : l' Assembler calcola per noi  e ci avvisa dell' eventuale errore.
 


Operatore di shift (>> e <<)

Esegue lo shift di un valore (0 o 1) di n volte.

; Make GIE and TOIE = 1
; other bits = 0


  movlw (1 << GIE) | (1 << TOIE))
  movwf INTCON

L' esempio qui sopra porta a 1 i bit 7 (GIE) e 5 (TOIE) del registro INTCON
La stessa operazione è eseguibile con una somma :

; Make GIE and TOIE = 1
; other bits = 0


  movlw (.128 + .32)  ; move b'10100000'
  movwf INTCON

o con un OR.

; Make GIE and TOIE = 1
; other bits untouched


  movlw b'10100000'
  xorwf INTCON, f

Ma si nota come nel primo caso l' uso delle lablel al posto di numeri assoluto facilita la comprensione e la stesura del sorgente; infatti si sfrutta l' assegnazione presente nel picxxx.ini in cui è dichiarato GIE = 7 e TOIE = 5, non necessitando così di altra dichiarazione nel sorgente.


XOR (^)

L' operatore XOR inverte il valore dei bit.
Una applicazione può essere quella di invertire lo stato di alcuni bit con una maschera oppure con  shift

; Make bit 0, 2, 6 = 0

  movlw 0xFF ^ ((1<<0) |(1<<2) | (1<<6))
 
; this is equal to
 
  andlw 0xBAh
 
; or
 
  andlw
~ ((1<<0) |(1<<2) | (1<<6))

Anche qui i numeri indicatori dei bit possono essere sostituiti con label equivalenti, rendendo più semplice e chiara la scrittura.


Operatore Complemento  (~)

L' operazione precedente equivale a

; Make bit 0, 2, 6 = 0

  andlw
~ ((1<<0) |(1<<2) | (1<<6))

 


 High/Low/Upper

Questi operatori restituiscono il byte basso, alto e superiore di un numero (a 3 bytes) o di un indirizzo a 20/21 bit.
Se l' estensione dell' indirizzamento è limitata a 64k, Upper non ha importanza.

; Pointer to Table (over 64k)

  movlw low Table      ; low of address
  movwf save_low
  movlw high Table     ; high of address
  movwf save_mid
  movlw upper Table    ; upper of address
  movwf save_up

e anche da label:

; move byte from a multi byte label
LABEL = 0xFA32CB
 
  movlw low LABEL      ; low byte
  movwf save_low       ; save_low  =  CBh
  movlw high LABEL     ; high byte
  movwf save_high      ; save_high =  32h
  movlw upper Table    ; upper byte
  movwf save_up        ; now save_upper = FAh
;    

Nei PIC16, che hanno limite di memoria programma inferiore agli Enhanced, la componente Upper non ha peso (indirizzamento < 32K)


Sono possibili calcoli di ogni genere, anche di formule complesse. 

Ecco alcuni esempi di calcoli effettuati in automatico da MPASM.

Esempio di calcoli relativo al clock. Gli elementi calcolati sono resi sotto forma di label per essere utilizzati o elaborati ulteriormente durante il programma.

; CLOCK frequency = internal osc. - default al POR
XTAL_FREQ equ 1000000          ; INTIO1 1 MHz
CLOCK     equ XTAL_FREQ/4      ; processor clock [Hz]
TCYC      equ 1000000000/CLOCK ; cycle time [ns]

Calcoli relativi a baudrate (USART),  al clock di Timer1 e a quello dell' I2C (MSSP).

; calculates baudrate when BRGH = 1, adjust for rounding errors
#define CALC_HIGH_BAUD(BaudRate) (((10*XTAL_FREQ/(16*BaudRate))+5)/10)-1
; calculates baudrate when BRGH = 0, adjust for rounding errors
#define CALC_LOW_BAUD(BaudRate) (((10*XTAL_FREQ/(64*BaudRate))+5)/10)-1

; calculates timer1 delay when prescale is 1:8, TcikTime in msec
#define CALC_TIMER1(TickTime) (0xFFFF-((TickTime*XTAL_FREQ)/32000))+1

; used for I2C calculations
#define I2CClock D'100000' ; define I2C bite rate
#define I2C_ClockValue (((XTAL_FREQ/I2CClock)/4) -1)
....

; esempio di uso
  movlw LOW(CALC_TIMER1(D'100'))
  movwf TMR1L                ; initialize Timer1 low
  movlw HIGH(CALC_TIMER1(D'100'))
  movwf TMR1H                ; initialize Timer1 high

 

Esempio di settaggio di paramenti per macro

DEST_HIGH   SET (HIGH(LABEL)&0x18)      ; save bit's 4:5 of dest adress
SOURCE_HIGH SET (HIGH($)&0x18)          ; source adress
DIFF_HIGH   SET DEST_HIGH ^ SOURCE_HIGH ; get difference (XOR)

Esempio di calcolo di indirizzo per generare un warning

  IF ((DIFF_HIGH&0x18)==0x18)
   MESSG " WARNING ! Replace SHORT_CALL with LONG_CALL " LABEL
  ENDIF

Esempio di calcolo per PWM (modulo CCP)

PWM_Period = (PR2 + 1)*4*Tosc*TMR2_PreScale_value
PR2 = (PWM_Period/(4 * Tosc * TMR2 Prescale)) - 1
PWM_Duty_Cycle = (CCPR1L_CCP1CON5_4)*Tosc*TMR2_Prescale_Value

 


Come si vede, la flessibilità del calcolatore di MPASM, unita all'uso di label, permette di:

  • evitare una parte consistente di calcoli manuali
  • ottenere sorgenti con parametri facilmente modificabili
  • adeguare automaticamente i parametri alle condizioni imposte
  • trattare costanti come label evitando qualsiasi richiamo a valori assoluti 
  • ottenere costanti che possono essere a loro volta ri elaborate e trattate come label in altri punti del programma

Nel caso si superassero le possibilità di MPASM o fossero introdotti elementi errati o non supportati, l' assembler invia un messaggio di errore.


Argomenti collegati:

  • MPASM: un breve tutorial
     

     

Copyright © afg . Tutti i diritti riservati.
Aggiornato il 18/05/11 .