Connect with us

Converting Switch/Case statement in C to ASM?

Discussion in 'Microcontrollers, Programming and IoT' started by chopnhack, Feb 29, 2016.

Scroll to continue with content
  1. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    Hi guys, I have already done some research on this task, which I thought was going to be simple, but its not(at least for not for me)!



    I am trying to convert a switch/case statement in C from my previous program (see below):



    Code:
    /*
    * PIC10LF320
    *
    * 02/2016
    */
    
    #include <xc.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    
    // Configuration Bits
    #pragma config FOSC = INTOSC  // Oscillator Selection bits (INTOSC oscillator: CLKIN function disabled)
    #pragma config BOREN = OFF  // Brown-out Reset Enable (Brown-out Reset disabled)
    #pragma config WDTE = OFF  // Watchdog Timer Enable (WDT disabled)
    #pragma config PWRTE = OFF  // Power-up Timer Enable bit (PWRT disabled)
    #pragma config MCLRE = OFF  // MCLR Pin Function Select bit (MCLR pin function is MCLR)
    #pragma config CP = OFF  // Code Protection bit (Program memory code protection is disabled)
    #pragma config LVP = OFF  // Low-Voltage Programming Disabled (Low-voltage programming enabled)
    #pragma config LPBOR = OFF  // Brown-out Reset Selection bits (BOR disabled)
    #pragma config BORV = LO  // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
    #pragma config WRT = OFF  // Flash Memory Self-Write Protection (Write protection off)
    
    
    #define _XTAL_FREQ 32000
    
    void delay(int ms)
    {
      int i;
      for (i = 0; i < ms*8; i++)  //32KHz/4 cycles = 8k cycles/sec 8cycles=1ms 1cycle=125us
      {
      asm ("NOP");
      }
    }
    
    void STANDARD(void)
    {
      RA2 = 1;
      delay (60);
      RA2 = 0;
      delay (8);
      //__delay_us(100);
    }
    
    void SLOW(void)
    {
      RA2 = 1;
      delay (120);
      RA2 = 0;
      delay (8);
      //__delay_us(100);
    }
    
    void FAST(void)
    {
      RA2 = 1;
      delay (40);
      RA2 = 0;
      delay (6);  
    }
    
    
    // Main application
    void main(void)
    {
      OSCCON = 0b00000010;  // IRCF 32kHz_INTOSC;
      ANSELA = 0b00000110;  // Clear analog input for RA0 - now digital
      TRISA0 = 1;  // Set Channel RA0 as input
      TRISA2 = 0;  // Set Channel RA2 as output
      LATA0 = 0;  // Set RA0 to off initially
      LATA2 = 0;  // Set RA2 (LED D1) off
      OPTION_REG = 0b00000000;  // Clearing WP register
      WPUA = 0b00000001;  // Enabling WP resistor on A0
      TRISA1 = 0;  // Set Channel RA1 as output
      LATA1 = 0;  // Set RA1 off
      // update code to terminate RA3 as output - update , rework circuit assign RA3 to RA0's position since RA3 is input only!! then terminate RA0
      
      
       while (1)
      {
      unsigned int Random = TMR0;  //taking value of TMR0 and assigning to var
      Random = Random % 10;  //modulo 10 to get last digit
      
      if (RA0 == 0 )  //if input is low randomize between STANDARD, SLOW and FAST
      { 
      switch(Random)
      {
      case 0: case 1: case 2:
      STANDARD();
      break;
    
      case 3: case 4: case 5:
      SLOW();
      break;  
      
      case 6: case 7: case 8:
      FAST();
      break;
      
      case 9:
      RA2 = 0;
      break;
      }  
      
      }
      }
    }
    
    
    


    I found a tip from Microchip - http://microchip.wikidot.com/tip:3

    Code:
    movf  SWITCH, w
    xorlw  CASE1
    btfsc  STATUS, Z  ; If SWITCH = CASE1, jump to LABEL1
    goto  LABEL1
    xorlw  CASE2^CASE1
    btfsc  STATUS, Z  ; If SWITCH = CASE2, jump to LABEL2
    goto  LABEL2
    xorlw  CASE3^CASE2
    btfsc  STATUS, Z  ; If SWITCH = CASE3, jump to LABEL3
    goto  LABEL3
    
    In the text they talk about using a macro, which run at build time and not during operation of the pic.... this leads me to believe that the contents of the W register which I intend to use to take in a variable from TMR0 may become static as it will be polled at build time and not continuously.....


    Can anyone assist me with finding a way to implement this in ASM correctly. I do not want to write a nested if, else statement for fear of run time performance and lack of clarity.


    Thanks in advance! (I also cross posted this on Microchips forum).
     
  2. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    This idea is not correct. The contents of w change with each XOR operation. What the Microchip tip tries to explain is that the part (CASE2^CASE1) is evaluated at build tima, as CASE2 and CASE1 are known constants.. However, "xorlw" is performed at runtime and changes the contents of the w register. The code could have been written this way:
    Code:
    movf  SWITCH, w
    xorlw  CASE1
    btfsc  STATUS, Z  ; If SWITCH = CASE1, jump to LABEL1
    goto  LABEL1
    //xorlw  CASE2^CASE1
    movf  SWITCH, w
    xorlw  CASE2
    btfsc  STATUS, Z  ; If SWITCH = CASE2, jump to LABEL2
    goto  LABEL2
    //xorlw  CASE3^CASE2
    movf  SWITCH, w
    xorlw  CASE3
    btfsc  STATUS, Z  ; If SWITCH = CASE3, jump to LABEL3
    goto  LABEL3
    By XORing CASE2^CASE1 at build time, one operation (movf SWITCH,w) is saved at runtime.
     
    chopnhack and hevans1944 like this.
  3. hevans1944

    hevans1944 Hop - AC8NS

    4,547
    2,122
    Jun 21, 2012
    I really like the snippet of code you posted from the Microchip forum. Very clever use of the xor statement. The only variable in that code is the value SWITCH which is placed in the accumulator to begin the case tree. The "magic" is the way the assembler preserves the value of SWITCH during subsequent xor comparisons. For your code, you will need ten xor case comparisons, one each for 0, 1, 2, 3, ... , 7, 8, and 9. However, you will only have four labels to branch to when you get a case match: STANDARD, SLOW, FAST, and whatever RA2 = 0 does when it falls through to CASE9. Note there is no assembly instruction that allows you to move a literal value into a memory variable, such as RA2. You have to move the literal into the accumulator first, then move the accumulator into the memory variable. They don't call it a Reduced Instruction Set Computer (RISC) for nothing!

    I would write this assembler version of a C case instruction as a subroutine that is called with RANDOM as the variable. Each of the LABELS would then be branches to your time-delay routines with a RETURN instruction at the end of each one. Thus each of the first eight cases calls jumps to a STANDARD, SLOW, or FAST subroutine that does the timing and then returns from the case construct. The ninth case just set RA2 = 0 and returns.

    You could hand-compute and hard-code as literals all the xorlw instructions, but that has two disadvantage: (1) it won't make any sense after you do this what the code is actually doing, and (2) by defining the CASEn literals elsewhere, they can be any values you want from 0 to 255, not just the particular sequence 0, 1, 2, ... 7, 8, 9.

    Note that each case is represented by only three instructions: xorlw, btfsc, goto. Not bad coding IMHO:

    Code:
    ;CASE SUBROUTINE
    
    movf      RANDOM, w
    xorlw     CASE0
    btfsc     STATUS, Z                ; If RANDOM = CASE0, jump to LABEL1
    goto      LABEL1
    xorlw     CASE1^CASE0
    btfsc     STATUS, Z                ; If RANDOM = CASE1, jump to LABEL1
    goto      LABEL1
    xorlw     CASE2^CASE1
    btfsc     STATUS, Z                ; If RANDOM = CASE2, jump to LABEL1
    goto      LABEL1
    xorlw     CASE3^CASE2
    btfsc     STATUS, Z                ; If RANDOM = CASE3, jump to LABEL2
    goto      LABEL2
    xorlw     CASE4^CASE3
    btfsc     STATUS, Z                ; If RANDOM = CASE4, jump to LABEL2
    goto      LABEL2
    xorlw     CASE5^CASE4
    btfsc     STATUS, Z                ; If RANDOM = CASE5, jump to LABEL2
    goto      LABEL2
    xorlw     CASE6^CASE5
    btfsc     STATUS, Z                ; If RANDOM = CASE6, jump to LABEL3
    goto      LABEL3
    xorlw     CASE7^CASE6
    btfsc     STATUS, Z                ; If RANDOM = CASE7, jump to LABEL3
    goto      LABEL3
    xorlw     CASE8^CASE7
    btfsc     STATUS, Z                ; If RANDOM = CASE8, jump to LABEL3
    goto      LABEL3
    movlw      0
    movwf      RA2                     ; If RANDOM = CASE9, set RA2 = 0 and
    RETURN                             ; return
    
    LABEL1      call STANDARD TIME DELAY
    RETURN
    LABER2      call SLOW TIME DELAY
    RETURN
    LABEL3      call FAST TIME DELAy
    RETURN
    
     
    Last edited: Feb 29, 2016
    chopnhack likes this.
  4. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    So you have three routines (Standard, Slow, Fast) that you call depending on the outcome of the switch statement. You haven't shown us what these routines do, but let's assume they all perform the same task, only at different speeds. You may save a lot of program space (and incidentally make the code easier to maintain) by using ony one routine for these tasks and giving SPEED as a parameter to the routine.
    You even may be able to compute the value of SPEED by some simple mathematic operation (or lookup table) from the value of the RANDOM variable. AS I'm not familiar with PIC Assembler, allow me to post a Code snippet in C:
    Code:
    #defineSLOW 2                // 2, 4 and 6 are placeholders for the real values required in your application
    #defineSTANDARD 4
    #defineFAST 6
    
    int main()
    {
        int speed[SLOW; SLOW; SLOW; STANDARD; STANDARD; STANDARD; FAST; FAST; FAST ];
    
    while (1) {
        unsigned int Random = TMR0;  //taking value of TMR0 and assigning to var
        Random = Random % 10;  //modulo 10 to get last digit
    
        if Random == 9
            RA2 = 0;
        else
            MyRoutine(speed[Random]);
        }
    }
    int MyRoutine(int MySpeed)
    {
        Do_something_at_Myspeed;
    }
     
    hevans1944 likes this.
  5. hevans1944

    hevans1944 Hop - AC8NS

    4,547
    2,122
    Jun 21, 2012
    Gee, Harald, I had almost convinced John of the joys of assembler coding and how it would result in more compact code than the C equivalent... :(
     
  6. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    So sorry Hop ;)
    I definitely don't want to pull John away from coding in assembler. Only I do not speak PIC assembler, that's why I put in some C-code. I'm sure this can easily be compiled into neat asssembler.
     
  7. hevans1944

    hevans1944 Hop - AC8NS

    4,547
    2,122
    Jun 21, 2012
    No problem, Harald. John has been trying to fit ten kilos of C code into a one kilo memory container... well, less than that with the PIC he's currently using. So I suggested writing some of the C code in assembly to see if that helped. He is using a non-optimizing, free, C compiler, but I doubt paying for a optimizing compiler would help any.

    The Microchip PIC doesn't make passing arguments to subroutines easy. You can't push stuff on the stack and pop it off for use in a subroutine. The only thing that gets pushed is the program counter during a CALL, for use later with the RETURN instruction. Of course, given the limited available memory, that usually isn't necessary... everything is global... so if a routine is only used once or twice, coding it in-line might be simpler. This came up when John discovered just how much machine code C generated for a simple modulus conversion. It would be interesting to see how much machine code C generates for a CASE construct, to compare with the three instructions per case that the Microchip example provided.

    My whole point, with regard to PIC programming, is that RISC architecture and small amount of space for code and data does not lend itself well to high-level language programming. I don't know why this should be so: the end result is machine language in either case and the machine doesn't care where it's code originated. I've found that high-level languages are useful for managing large programs and complex algorithms, such a file sorting or pseudo-random number generation or floating-point computations, but have usually attributed that advantage to more powerful CPUs, more memory, and an operating system with a file structure and mass storage devices. You get used to that if your previous programming environment was a modern PC or a mainframe. Embedded microprocessors are a whole 'nother ball game when they are sized appropriately to the task at hand.

    Let's see what John makes of all this...
     
  8. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    One could use a global variable / address.
    One could replace the subroutine call by a few lines of inline code...

    You're probably and generally right: hand optimized assembler code should be faster and less memory hogging (at least this used to be so). On the other hand, C as a high level langage is rather near to the machine code and a good compiler can generate code (almost) as good as hand written assembler. Depending on the skils of the assembler programmer maybe even better :D

    A factor of 10 (required memory to available memory) is hard to achieve by simply coding the same routines in assembler instead of C. Assembler tricks (as demonstrated in the Microchip code example) can and do save a byte here and there, but to achieve a reduction on the scale we're talking about here requires re-thinking of the whole program architecture or structure. This is what I wanted to show by my short code snippet.

    A point to illustrate: John uses the last digit of the "Random" variable to select one of four options in his switch statement. To do this he performs a modulo 10 operation, probably time and memory consuming. I don't know the environment where his routine operates, but what if John uses module 4 instead? This operation is a simple AND 0x03, fast and small.
    The result is a number between 0...3, which can be used to select any of the 4 cases. But: the probability of the former case Random=9 (last case within the select statement) is now 25% compared to 10% in the original code. Can you live with this, @chopnhack ?
     
    chopnhack and hevans1944 like this.
  9. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    Thanks Harald, I wasn't sure if the entire line of code was acted upon at that moment.
    Indeed, I was thrilled to have landed on something like this!! it seemed very well aligned for what I wanted to do.

    You guys are too much! Harald - I understand, he is in a different time zone, but Hop - you respond with all that in 16 minutes...at 2AM no less LOL
    My replies to you guys take forever!!! I learn quite a bit and have to think of my replies, for which I am very grateful to you both.


    Yes!! Exactly - here is an excerpt of the very same thought I had sent to Hop, yesterday concerning this :

    <<I had a thought occur when I finished writing them ((calls to slow,fast,etc.)) and I looked back at them: In C, these could be made more compact by turning them each into functions themselves - they all go on/off! They would need two variables, one for each on/off times to be passed to the function. I don't know where to start in ASM to do something like that.>>

    I barely know how to write a function in C, let alone passing two arguments to a function in ASM :( I am not aware of its feasibility so I went the brute force method. But the thought from OOP did occur to me :D:cool:

    I did show the routines, I quoted them below for ease. The 'delay' is a function with one numeric argument passed to it which determine the time on and off. As shown below in C, I then rolled that into another function that gets called when the case switches to it.

    Code:
    void STANDARD(void)
    {
      RA2 = 1;
      delay (60);
      RA2 = 0;
      delay (8);
      
    }
    
    void SLOW(void)
    {
      RA2 = 1;
      delay (120);
      RA2 = 0;
      delay (8);
      
    }
    
    void FAST(void)
    {
      RA2 = 1;
      delay (40);
      RA2 = 0;
      delay (6); 
    }
    
    Yes, this was a place holder, I didn't want the program to 'fail' because it received a number outside of the range of expected results. To be truthful, this is all predicated on the result from TMR0 being a single decimal value of 0-9!!! I haven't gotten that far in my analysis of the values from that module. Looking through the datasheet for info on TMR0 all it says about it is that its a 8 bit timer that can be used as a counter or timer... There are no examples of what values can be expected or what size the values are. There is mention of prescalers. I can say that the actual implementation in C seemed to function randomly, but this was a limited observation, just because it works, doesn't mean it's working correctly ;)

    What I have found is that despite it being all machine language in the end, the HLL come with their own baggage. So the act of calling a library to do a function, i.e. modulo has a high overhead cost for the pic. Library functions are 'verbose' when it comes to code...

    Mostly a great big mess ;):p:D Hey, I'm programming in ASM, that in itself is a small miracle in just a few weeks!

    Ah! Very clever indeed :D
    I hadn't thought of that and it certainly simplifies the case switch!! Since I don't need 4 cases, but 3, I wonder if modulo 3 would be even better - but I do see how 0 would turn up 4 out of ten times whereas the 1 and 2 would turn up 3/10. Not really important in my application, its random enough.

    For some perspective, calling the math library involved for the modulo operation in C for this ASM compiler costs 63 words on this device out of a total of 256 words.... You can easily see why I went to a larger case switch!
     
    hevans1944 likes this.
  10. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    Your three routines can be unified this way (of course, use assembler to minimize code size, I use C for clarity amd because I don't "speak" PIC asm):
    Code:
    unsigned int speed; // global variable to avoid parameter passing issues when doing this in PIC assembler
    
    // while loop determining speed from TMR0
    ...
    Random = TRM0 & 0x03; // module 4 the fast way -> Random=0...3
    if  (Random==3)
       speed = 0; //special case, not relevant
    else
       speed = 40+ (Random*Random)*20; // Multiplication  should be rather fast
                                                                   // Random==0 -> speed=40, Random==1 -> speed=60, Random==2 -> speed=120
    do_at_my_speed();
    ...
    
    
    void do_at_my_speed(void)
    {
      if (speed <> 0) {
         RA2 = 1;
         delay (speed);  //use global variable to set speed
         RA2 = 0;
         delay (8); //only drawback: delay=8 for the fast mode, too. Can you live with that?
       }
    }
    
    Next step: eliminate the subroutine call by inline code:
    Code:
    unsigned int speed; // global variable to avoid parameter passing issues when doing this in PIC assembler
    
    // while loop determining speed from TMR0
    ...
    Random = TRM0 & 0x03; // module 4 the fast way -> Random=0...3
    if  (Random==3)
       speed = 0; //special case, not relevant
    else {
       speed = 40+ (Random*Random)*20; // Multiplication  should be rather fast
                                                                   // Random==0 -> speed=40, Random==1 -> speed=60, Random==2 -> speed=120
        RA2 = 1;
        delay (speed);  //use global variable to set speed
        RA2 = 0;
        delay (8);
    }
    ...
    }
    
    Next step: save another byte by eliminating the global variable speed:
    Code:
    // while loop determining speed from TMR0
    ...
    Random = TRM0 & 0x03; // module 4 the fast way -> Random=0...3
    if  (Random<>3) {                                   //ignore Random==3 as a special case
        RA2 = 1;
        delay (40+ (Random*Random)*20);  // Random==0 -> speed=40, Random==1 -> speed=60, Random==2 -> speed=120
        RA2 = 0;
        delay (8);
    }
    ...
    }
    
    Note that with increased "optimization" the readability of the code suffers!

    An 8 bit timer can assume all values between 0...255 (unsigned int). Note that an 8 bit timer is possibly not a very good source of random numbers. Depending on the runtime of your code it may happen that you read certain values more often ten others when your code takes just as much time to reach the point where a timer value is being read as it takes the timer to overflow. A good pseudo random number generator has to fulfil a few statistical requirements. A comparatively simple method is the inversive congruential generator.

    Not only for the PIC. Library functions need to be fairly universal and have to cover lots of cases which may not occur in your application. Unless a highly optimizing compiler can throw out all the ballast, not using libraries and manually optimizing your code will achieve smaller code size in any language. At the cost of your having to do do more programming.

    You'd get rid of the unnecessary 4th case, but at the cost of having to implement a modulo 3 operation, which is definitely much slower and more memory consumingb than AND 0x03.
     
    chopnhack and hevans1944 like this.
  11. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    Thank you Harald, you have given me a lot to think over!

    I am not familiar with using those operators together - is this correct or a typo - does that really allow all numbers but 3 to be compared to Random?

    True, and I will have to run some tests when I get more code written to look at that issue. On the other side, I am not sure if I will have room to code for a RNG.

    I am not familiar with AND 0x03. How does it work? could I AND 0x02?
     
    Last edited: Mar 2, 2016
  12. hevans1944

    hevans1944 Hop - AC8NS

    4,547
    2,122
    Jun 21, 2012
    The AND operator filters out (sets to zero) all bits that don't match the literal. So RANDOM AND 0x02 will return a result of either 0x00 or 0x02 depending on whether RANDOM has bit 1 cleared or bit 1 set. Assuming RANDOM is an 8-bit variable, any value from 0x00 to 0xFF (inclusively) will be returned as 0x00 or 0x02 after the AND operation.

    The inverse OR operator is used to set to one all bits that match the literal. So RANDOM OR 0x02 will set bit 1 and leave the remaining bits without change.

    The two operators are often used in assembly to set or clear one or more bits in a status byte without affecting the other bits.
     
  13. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    Right, whereas the result of "Random AND 0x03" is:
    00, 01, 10 or 11, not only 00 or 02.
    Maybe more obvious to notice when written as
    "Random AND 0b00000011"

    Sorry, that should have read Random != 3 (Random not equal 3).

    One on top:
    In this very special case the operation
    Random*Random
    is equivalent to
    Random<<(Random-1) //check for yourself :D
    which may not use less memory but may be faster than a true multiplication.
     
  14. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    I believe I have made some progress gents:

    Code:
    ;*******************************************************************************
    ;  *
    ;  Filename:  *
    ;  Date: 02/23/2016  *
    ;  File Version: 0.6    *
    ;  Author:  *
    ;  Company:  *
    ;  Description: Simple prog. to blink one port using ASM and repeated code  *
    ;  *
    ;*******************************************************************************
    ;  *
    ;  Revision History:  *
    ; 0.0 syntax corrections and can't figure out __config issue!!!      *
    ; 0.1 commented out config, temp. fix, used proper addressing of individual  *
    ;  -bits instead of broadly setting entire PORT        *
    ; 0.2 blinking! - will have to play with values to achieve better timing  *
    ; 0.3 learned how to access OSCCON and changed freq. - times closer to 1 sec  *
    ; 0.4 LOOP'ed just the on/off and updated delay funct.-true 100ms at 32k clock *
    ; 0.5 Create functions for different time delays        *
    ; 0.6 Randomize with TMR0  *
    ;*******************************************************************************
    
    #include "p10LF320.inc"
    
    ;CONFIG
    ;__config 0x3FE6  
      __CONFIG _FOSC_INTOSC & _BOREN_ON & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _LVP_ON & _LPBOR_ON & _BORV_LO & _WRT_OFF
    
    case0 equ  .0
    case1 equ  .1 
    case2 equ  .2
    case3 equ  .3  
      
    My_var   UDATA
    dc1   res    1
    dc2   res    1
    dc3   res    1
    dc4   res    1  
    switch   res    1
    three   res    1  
    
    RES_VECT  CODE  0x0000  ; processor reset vector
      GOTO  START  ; go to beginning of program
    
    Delay100ms
       movwf   dc3
    dly2   movlw   .1
       movwf   dc2
       movlw   .127  
       movwf   dc1    ;clrf   dc1
    
    dly1   decfsz   dc1,f
       goto   dly1
       decfsz   dc2,f
       goto   dly1
       decfsz   dc3,f
       goto   dly2
      
       retlw   0  
      
    On   movlw   b'0100'        ;setting RA2 high
       movwf   PORTA
       retlw   0
      
    Off   movlw   b'0000'        ;RA2 low
       movwf   PORTA  
       retlw   0
      
      
    Standard  
       call   On
       movlw   .60
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .5
       movwf   Delay100ms
       call   Delay100ms
       retlw   0
      
    Slow    
       call   On
       movlw   .175
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .5
       movwf   Delay100ms
       call   Delay100ms
       retlw   0
      
    Fast
       movlw   .2
       movwf   dc4
    One
       call   On
       movlw   .25
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .4
       movwf   Delay100ms
       call   Delay100ms
       decfsz   dc4
       goto   One
       retlw   0
    
    ;*******************************************************************************
    ; MAIN PROGRAM
    ;*******************************************************************************
    
    MAIN_PROG CODE  ; let linker place main program
    
    START
      movlw   b'00000010'
      movwf   OSCCON
      clrf   ANSELA        ;digital i/o
      clrf   TRISA        ;output
      clrf   PORTA        ;clearing PORTA, all low except RA3
      movlw   .3 
      movwf   three
    LOOP
      movf   TMR0,W        ;move contents of TMR0 (0-255)into working register
      ANDWF   three        ;AND three and TMR0 into 'switch' variable
      movwf   switch
      
      movf  switch, w
      xorlw  case0
      btfsc  STATUS, Z        ; If SWITCH = CASE0, jump to Standard
      goto  Standard
      xorlw  case1^case0
      btfsc  STATUS, Z        ; If SWITCH = CASE1, jump to Slow
      goto  Slow
      xorlw  case2^case1
      btfsc  STATUS, Z        ; If SWITCH = CASE2, jump to Fast
      goto  Fast
      xorlw  case3^case2
      btfsc  STATUS, Z        ; If SWITCH = CASE3, jump to LOOP
      goto  LOOP
      
      goto   LOOP
      
    
      
    end
    
    
    
    I just need to figure out a way to view the contents of switch in real time... of which there may be no easy way of doing. If that is the case I will need to find a compact RNG to implement.
     
    Last edited: Mar 7, 2016
  15. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    Indeed, no easy way to check the register in real time! It requires another piece of equipment at $50. I rewrote the code to make use of a small RNG. The problem I am having with it as I watch it in simulation is that the working register is passed a number (decimal 10) by the time it gets to the 'ANDWF three' statement. (Three is defined as decimal three above in the code.) After it is AND'ed the value of the working register is 8! 10 AND 3 should work out to 1 if modulo is being applied. I looked through the datasheet and the only thing mentioned is that (pg. 149) the values are AND'ed together.... further research states that in assembly, AND is boolean, just comparing the two variables passed to it (one from W and the other from the register you tell it) and if they are equal - 1 if not 0.

    I may have misunderstood, but it seems that the AND function in ASM can not be used in the fashion we thought above?

    Code:
    ;*******************************************************************************
    ;  *
    ;  Filename:  *
    ;  Date: 02/23/2016  *
    ;  File Version: 0.6    *
    ;  Author:  *
    ;  Company:  *
    ;  Description: Simple prog. to blink one port using ASM and repeated code  *
    ;  *
    ;*******************************************************************************
    ;  *
    ;  Revision History:  *
    ; 0.0 syntax corrections and can't figure out __config issue!!!      *
    ; 0.1 commented out config, temp. fix, used proper addressing of individual  *
    ;  -bits instead of broadly setting entire PORT        *
    ; 0.2 blinking! - will have to play with values to achieve better timing  *
    ; 0.3 learned how to access OSCCON and changed freq. - times closer to 1 sec  *
    ; 0.4 LOOP'ed just the on/off and updated delay funct.-true 100ms at 32k clock *
    ; 0.5 Create functions for different time delays        *
    ; 0.6 Randomize with TMR0  *
    ;*******************************************************************************
    
    #include "p10LF320.inc"
    
    ;CONFIG
    ;__config 0x3FE6   
      __CONFIG _FOSC_INTOSC & _BOREN_ON & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _LVP_ON & _LPBOR_ON & _BORV_LO & _WRT_OFF
    
    case0 equ .0
    case1 equ .1
    case2 equ .2
    case3 equ .3
    three equ .3   
       
    My_var   UDATA
    dc1   res    1
    dc2   res    1
    dc3   res    1
    dc4   res    1   
    RANDOM   res    4
       
    RES_VECT  CODE  0x0000  ; processor reset vector
      GOTO  START  ; go to beginning of program
    
    Delay100ms
       movwf   dc3
    dly2   movlw   .1
       movwf   dc2
       movlw   .127   
       movwf   dc1    ;clrf   dc1
    
    dly1   decfsz   dc1,f
       goto   dly1
       decfsz   dc2,f
       goto   dly1
       decfsz   dc3,f
       goto   dly2
       
       retlw   0   
       
    On   movlw   b'0100'        ;setting RA2 high
       movwf   PORTA
       retlw   0
       
    Off   movlw   b'0000'        ;RA2 low
       movwf   PORTA   
       retlw   0
       
       
    Standard   
       call   On
       movlw   .60
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .5
       movwf   Delay100ms
       call   Delay100ms
       retlw   0
       
    Slow     
       call   On
       movlw   .175
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .5
       movwf   Delay100ms
       call   Delay100ms
       retlw   0
       
    Fast
       movlw   .2
       movwf   dc4
    One
       call   On
       movlw   .25
       movwf   Delay100ms
       call   Delay100ms
       call   Off
       movlw   .4
       movwf   Delay100ms
       call   Delay100ms
       decfsz   dc4
       goto   One
       retlw   0
       
         
    ;*******************************************************************************
    ; MAIN PROGRAM
    ;*******************************************************************************
    
    MAIN_PROG CODE  ; let linker place main program
    
    START
      movlw   b'00000010'
      movwf   OSCCON
      clrf   ANSELA        ;digital i/o
      clrf   TRISA        ;output
      clrf   PORTA        ;clearing PORTA, all low except RA3
       
      movf   TMR0,W
       movwf   RANDOM
       RLF  RANDOM,W
      RLF  RANDOM,W
      BTFSC  RANDOM,4
      XORLW  1
      BTFSC  RANDOM,5
      XORLW  1
      BTFSC  RANDOM,3
      XORLW  1
      MOVWF  RANDOM
       
    LOOP
       
      movf  RANDOM,W   
      ANDWF  three,0
       
      xorlw  case0
      btfsc  STATUS, Z        ; If SWITCH = CASE0, jump to LABEL0
      goto  Standard
       
      xorlw  case1^case0
      btfsc  STATUS, Z        ; If SWITCH = CASE1, jump to LABEL1
      goto  Slow
       
       
      xorlw  case2^case1
      btfsc  STATUS, Z        ; If SWITCH = CASE2, jump to LABEL2
      goto  Fast
       
       
      xorlw  case3^case2
      btfsc  STATUS, Z        ; If SWITCH = CASE3, jump to LABEL3
      goto  LOOP
       
      goto  LOOP
       
    
       
     end
    
    
    
     
  16. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    ANDWF operates on registers W and F. Use ANDLW instead to AND register W with a literal (3).
     
    chopnhack likes this.
  17. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    I will give that a try - the 'three' was a constant assigned value decimal three, shouldnt that have worked?
     
  18. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    Im not familiar with PIC assembler, but from the description in the manual I doubt ANDWF is the correct instruction. I wonder how the assembler accepted these lines of code.
     
    chopnhack likes this.
  19. chopnhack

    chopnhack

    1,573
    354
    Apr 28, 2014
    I don't see why it would not have accepted it - the 'three' was a constant holding a literal value of 3 - when it was used in ANDWF three - the literal value of 3 was AND'd with the working register's value. Problem is though, I think the assembler/linker is doing a strict boolean comparison... For instance, I stepped through while watching the value of the working register while the code was AND'ing literal three with Wreg of 4 and it yielded 0! I was expecting the result to be 1 if it was following the method we thought it would produce.

    I need a new way of converting a random number into 0,1 or 2.
     
  20. Harald Kapp

    Harald Kapp Moderator Moderator

    10,764
    2,424
    Nov 17, 2011
    As I understand it, ANDWF performs a logical AND between the W register and the file register (f), not between W and a literal (constant), as you wish. Your code would have ANDed the contents of file register 3 (status) with the contents of W. Since the contents of file register 3 is not necessarily 3 (in fact, probably only rarely), your result is completely unexpected.

    AND 0x03 will yield a number between 0...3. I know of no fast way to do a MOD 3 operation. If you can't simply ignore one of the cases (see my code example in post #10), then repeating the random generation and AND operation within a loop until the result is <> 3 can do the job.
     
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day

-