Maker Pro
Maker Pro

Converting Switch/Case statement in C to ASM?

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).
 

Harald Kapp

Moderator
Moderator
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.....
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.
 

hevans1944

Hop - AC8NS
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:

Harald Kapp

Moderator
Moderator
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

Hop - AC8NS
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: ...
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... :(
 

Harald Kapp

Moderator
Moderator
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.
 

hevans1944

Hop - AC8NS
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.
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...
 

Harald Kapp

Moderator
Moderator
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.
One could use a global variable / address.
One could replace the subroutine call by a few lines of inline code...

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.
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 ?
 
(CASE2^CASE1) is evaluated at build time
Thanks Harald, I wasn't sure if the entire line of code was acted upon at that moment.
I really like the snippet of code you posted from the Microchip forum. Very clever use of the xor statement.
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.


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.
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); 
}

The ninth case just set RA2 = 0 and returns.
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 ;)

..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.
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...

Let's see what John makes of all this...
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!

but what if John uses module 4 instead?
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!
 

Harald Kapp

Moderator
Moderator
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!

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.
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.

So the act of calling a library to do a function, i.e. modulo has a high overhead cost for the pic.
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.

I wonder if modulo 3 would be even better
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.
 
Thank you Harald, you have given me a lot to think over!

(Random<>3)
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?

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.
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.

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.
I am not familiar with AND 0x03. How does it work? could I AND 0x02?
 
Last edited:

hevans1944

Hop - AC8NS
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.
 

Harald Kapp

Moderator
Moderator
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.
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"

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?
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.
 
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:
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
 

Harald Kapp

Moderator
Moderator
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.
 
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.
 

Harald Kapp

Moderator
Moderator
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.
 
Top