PIC18: problemas para hacer funcionar la rutina del ensamblador en línea

(EDITAR: Se agregó el código de desmontaje C18)

Necesito optimizar una función escrita en C para un PIC18f4585. Estoy usando C18 para compilar.

La función que estoy tratando de reescribir en ensamblador es:

void readResetTimers (void)
{
   register unsigned char tmr0_temp;
   register unsigned char tmr1_temp;

   tmr0_temp = TMR0L;
   TMR0L = 0;
   tmr1_temp = TMR1L;
   TMR1L = 0;

   tmr0_value = tmr0_temp;
   tmr1_value = tmr1_temp;
}

Pensé que, en cambio, podría usar el código ensamblador para cargar el valor de cada registro en las variables tmr0_value y tmr1_value tal como se declaran en el espacio de nombres global, sin embargo, mi sistema no funciona cuando sustituyo el siguiente código en:

void readResetTimers (void)
{
_asm
    MOVF    TMR0L, 0, 1
    MOVWF   tmr0_value, 1
    CLRF    TMR0L,0

    MOVF    TMR1L, 0, 1
    MOVWF   tmr1_value, 1
    CLRF    TMR1L,0
_endasm
}

Cualquier idea sería apreciada, Saludos.

 void readResetTimers (void)
  04D4    CFD9     MOVFF 0xfd9, 0xfe6
  04D6    FFE6     NOP
  04D8    CFE1     MOVFF 0xfe1, 0xfd9
  04DA    FFD9     NOP
  04DC    0E02     MOVLW 0x2
  04DE    26E1     ADDWF 0xfe1, F, ACCESS
405:               {
406:                  register unsigned char tmr0_temp;
407:                  register unsigned char tmr1_temp;
408:               
409:                  /* Read the two timers into CPU registers and reset them
410:                   * as quickly as absolutely possible.
411:                   */
412:               
413:                  tmr0_temp = TMR0L;
  04E0    50D6     MOVF 0xfd6, W, ACCESS
  04E2    6EDF     MOVWF 0xfdf, ACCESS
414:                  TMR0L = 0;
  04E4    6AD6     CLRF 0xfd6, ACCESS
415:                  tmr1_temp = TMR1L;
  04E6    0E01     MOVLW 0x1
  04E8    CFCE     MOVFF 0xfce, 0xfdb
  04EA    FFDB     NOP
416:                  TMR1L = 0;
  04EC    6ACE     CLRF 0xfce, ACCESS
417:               
418:                  tmr0_value = tmr0_temp;
  04EE    CFDF     MOVFF 0xfdf, 0xe4
  04F0    F0E4     NOP
419:                  tmr1_value = tmr1_temp;
  04F2    CFDB     MOVFF 0xfdb, 0xe5
  04F4    F0E5     NOP
431:               
432:               }
¿Cómo se compara el desmontaje del C18 con su ensamblador manual?
No puedo imaginar por qué el compilador no generaría un código que sea al menos tan bueno como el enfoque que se está tomando en el código escrito a mano.
Agregué la lista de desmontaje a la publicación.

Respuestas (1)

Como desea reemplazar toda la subrutina, el ensamblador en línea es un poco tonto. Escribe toda la rutina en ensamblador. De esa manera, el compilador no puede interferir haciendo cosas al entrar y salir de la rutina y cosas por el estilo.

También sería mejor explicar lo que realmente quiere que haga esta rutina que definirla implícitamente con código C. Tienes:

  tmr0_temp = TMR0L;
  TMR0L = 0;
  tmr1_temp = TMR1L;
  TMR1L = 0;

  tmr0_valor = tmr0_temp;
  tmr1_valor = tmr1_temp;

Aparentemente, desea una instantánea del temporizador 0 en modo de 16 bits en una variable separada. Su código anterior no funcionará para eso en todos los casos, ya que no se ocupó del incremento de bytes altos entre las dos lecturas.

A menos que acceda a los dos registros finales en una rutina que podría verse interrumpida por esta, no es necesario el registro temporal. En realidad, sin deshabilitar las interrupciones, no puede garantizar que el valor final de 16 bits se escriba atómicamente de todos modos. O te importa la actualización atómica o no. Su método no tiene sentido, ya que parece importarle una parte, pero al final no lo garantiza.

También está reiniciando el temporizador a 0, lo que suele ser una mala idea. Esto suele ser el resultado de querer hacer mediciones secuenciales de intervalos de tiempo sin haberlo pensado muy bien. Si la razón por la que necesita que esta rutina se ejecute tan rápido es porque desea minimizar las instrucciones que pierde al reiniciar el temporizador, entonces esta es definitivamente la forma incorrecta de hacerlo. Tome una instantánea en cada punto entre el que desea medir el intervalo, luego haga una resta de 16 bits sin signo. Eso le dará el intervalo sin ciclos perdidos cuando se reinicie el temporizador.

Si puede permitirse que el temporizador se apague durante algunos ciclos, simplemente apáguelo, léalo, bórrelo y reinícielo. Realmente no veo un caso en el que tu método tenga sentido.

En cualquier caso, aquí hay un fragmento de código de ejemplo que toma una instantánea constante del temporizador 0 mientras lo deja en ejecución (no probado, ni siquiera verificado la sintaxis):

         instantánea externa t0; instantánea de 16 bits del temporizador 0

;**************************************************** *******************
;
; Subrutina T0_GRAB
;
; Tome el valor actual del temporizador 0 en T0SNAPSHOT. El temporizador
; continuar corriendo todo el tiempo.
;
         global t0_grab
t0_grab
         banksel t0snapshot; establece el banco para acceder a la variable de salida
t0_retry ;volver aquí si cambió el byte alto
         movf tmr0h, w ;obtiene el byte alto del temporizador
         movwf t0snapshot+1 ;guardarlo
         movff tmr0l, t0snapshot+0 ;toma el byte bajo
         xorwf tmr0h, w ;compruebe el byte alto de nuevo
         bnz t0_retry; el byte alto cambió, ¿intentar de nuevo?

         devolver

Tenga en cuenta esta construcción llamada "comentarios" en esta rutina. Esas son cosas muy útiles que necesita aprender. El código no documentado realmente no es ningún código.

El código que se muestra lee el byte bajo de TMR0 y TMR1. Puede haber algún significado semántico en el hecho de que tmr0_value y tmr1_value no se actualizan hasta que se borran los temporizadores, aunque, como usted, considero dudoso borrar los temporizadores. Si uno quisiera leer y borrar un temporizador sin preescalar en lugar de dejar que funcionara libremente (por ejemplo, porque quería que sucediera algo si el temporizador alcanzaba un cierto valor sin haber sido leído y borrado), uno podría leer el valor, agregar tres , y luego restarlo del temporizador.
@Super: Ups, tienes razón. De alguna manera no me di cuenta de que estaba leyendo el byte bajo de dos temporizadores diferentes. Eso es particularmente extraño ya que el temporizador 1 tiene 16 bits de ancho. Supongo que necesitamos escuchar del OP lo que realmente está tratando de hacer. Pasar de un código no documentado y sin descripción no funciona.