Maker Pro
Maker Pro

Converting Switch/Case statement in C to ASM?

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Apparently TMR0 was functioning correctly. My code was overwriting TMR0's value. I am waiting for a better explanation of what is going wrong and how I can avoid it. There is a difference between code and data spaces on the pic and apparently I was addressing one variable expecting the literal to be passed to the function, but instead the literal was anding the address of TMR0 resulting in b'00000000'!

http://www.microchip.com/forums/FindPost/918446
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,722
Joined
Nov 17, 2011
Messages
13,722
John,
what about this poece of your code:
Code:
movf  TMR0,W
  movwf  RANDOM
  ...
 
LOOP
 
  movf  RANDOM,W 
  ANDWF  three,0
You read TMR0 once, before the loop, and assign its value to the variable Random. Within the loop you always use this value stored in Random, which doesn't change. Why should it? Instead you'll have to read TMR0 at the beginning of each loop iteration, like this (I left out the details what with al the XOR operations you're doing):
Code:
LOOP
  movf  TMR0,W
  movwf  RANDOM
  ...
  movf  RANDOM,W 
  ANDWF  three,0

This should give you changing "random" numbers with each pass of the loop.
Harald
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
John,
what about this poece of your code:
Code:
movf  TMR0,W
  movwf  RANDOM
  ...

LOOP

  movf  RANDOM,W
  ANDWF  three,0
You read TMR0 once, before the loop, and assign its value to the variable Random.

Yes, thank you Harald - I have the file written that way too, but for testing I moved the tmr outside of the loop to isolate it for debugging.
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Ok, the successful resolution of the issue has been attained thanks to the good folks over at microchip forums and you guys of course!

The problem was fixed by removing the lines of code in each function that were passing the literal to the function Delay100ms. Apparently W was being passed anyway as the first lines of Delay100ms, has a movlw that does this. The commented code lines were the offending members. I still do not understand why, the explanation given was that the assembler does not know the difference between CODE and DATA space. I was simply pushing a literal into a variable... how can I prevent such a gremlin in the future!?

Thanks again :D
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
Ok, the successful resolution of the issue has been attained thanks to the good folks over at microchip forums and you guys of course!

The problem was fixed by removing the lines of code in each function that were passing the literal to the function Delay100ms. Apparently W was being passed anyway as the first lines of Delay100ms, has a movlw that does this. The commented code lines were the offending members. I still do not understand why, the explanation given was that the assembler does not know the difference between CODE and DATA space. I was simply pushing a literal into a variable... how can I prevent such a gremlin in the future!?

Thanks again :D

John post the original offending code for us to see.
Adam
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
John post the original offending code for us to see.
Adam
Offensive code on its way ;-)

Code:
;*******************************************************************************
;  *
;  Filename:  *
;  Date:02/23/2016  *
;  FileVersion:0.6  *
;  Author:  *
;  Company:  *
;  Description:Simple prog. to blink one port using ASM and repeated code  *
;  *
;*******************************************************************************
;  *
;  RevisionHistory:  *
;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.-true100ms at 32k clock *
;0.5Create functions for different time delays  *
;0.6Randomizewith 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   
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  .1;.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  .1;.60  ;all delays lowered to '1' to walk through steps efficiently
  movwf  Delay100ms
  call  Delay100ms
  call  Off
  movlw  .1;.5
  movwf  Delay100ms
  call  Delay100ms
  goto  LOOP ;return  ;retlw  0
   
Slow   
  call  On
  movlw  .1;.175
  movwf  Delay100ms
  call  Delay100ms
  call  Off
  movlw  .1;.5
  movwf  Delay100ms
  call  Delay100ms
  goto  LOOP ;retlw  0
   
Fast
  movlw  .1
  movwf  dc4
One
  call  On
  movlw  .1;.25
  movwf  Delay100ms
  call  Delay100ms
  call  Off
  movlw  .1;.4
  movwf  Delay100ms
  call  Delay100ms
  decfsz  dc4
  goto  One
  goto  LOOP ;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  b'00000001'
  movwf  OPTION_REG
   
   
LOOP
  movf  TMR0,W
  ;movwf  RANDOM  ;commented outfor clarity while debugging
  ;RLF  RANDOM
  ;RLF  RANDOM
  ;BTFSC  RANDOM,1
  ;XORLW  1
  ;BTFSC  RANDOM,0
  ;XORLW  1
  ;BTFSC  RANDOM,2
  ;XORLW  1
  ;MOVWF  RANDOM
  ;movf  RANDOM,W   
  ANDLW  0x03
   
  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
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Ah was it the movwf dc3?
Actually it was the second line of each function: slow, standard and fast that was causing the fault. It was resetting the TMR0 value to zero thus causing the low randomness.

Code:
movlw  .1;.175
movwf  Delay100ms  <----------------
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
Actually it was the second line of each function: slow, standard and fast that was causing the fault. It was resetting the TMR0 value to zero thus causing the low randomness.

Code:
movlw  .1;.175
movwf  Delay100ms  <----------------

But your movwf could be anything that's in the working register at that time?
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
It looks like you are trying to move a literal to a call function.
Adam
It is a function and it does get called, maybe that is what the chaps at the other forum were referring to! I really did not understand what they were getting at:
http://www.microchip.com/forums/FindPost/918642
.

But your movwf could be anything that's in the working register at that time?
I disagree. That is what the line of code ahead of the movwf was for, to move the literal value of decimal 1 into the working register and then load it into the function Delay100ms.That is how I intended to ensure the correct value would be in the accumulator at the time.
http://www.microchip.com/forums/FindPost/918642
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
Ok I think I see what you were trying to do. Assembler is not like C where you can pass a variable to a function, assembler is a bit clunky you have to move something into W and then to where you want it to go. You could use the movff if the PIC you are using allows it. Some of the basic ones don't allow this.
Adam
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
Ok I think I see what you were trying to do. Assembler is not like C where you can pass a variable to a function, assembler is a bit clunky you have to move something into W and then to where you want it to go. You could use the movff if the PIC you are using allows it. Some of the basic ones don't allow this.
Adam
Yes, its an extra step, but not too bad once you 'think' like the assembler does. No movff available in this series. Were you able to understand what those folks were referring to in the link above?
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,722
Joined
Nov 17, 2011
Messages
13,722
I did some quick reading. It realy helps to understand the so called processor architecture you are using when it comes to assembly language programming.
The PIC uses a "modified Harvard Architecture" which means that it treats code memory and data memory as separate entities. Having separate memories for code and data allows simultaneous acces to code and data. The "modified" means that the CPU can also access instruction memory as if it were data memory.
In your case the program in in the PICs FLASh memory, so you cannot write to it at runtime (you need to program it).
In this instruction
Code:
movwf Delay100ms
the assembler detects a symbol "Delay100ms" in the contect of a "movwf" instruction. The assembler isn't "aware" that this symbol is a location in code memeory, namley the address of the Delay100ms subroutine. The assembler simply looks up the value of this symbol in its internal symbol table (a list of symbols, or names and the respective values which allows you to use symbols instead of numbers and addresses). This list is built by the assembler, and as far as I know also used by the linker when linking separately assembled code fragments, e.g. libraries. You usually don't have to care about this list.
The assembler then takes the value it has found in the symbol table and uses it to generate a valid "movwf" instruction. In the "movwf" instruction, however, the value associated with the symbol "Delay100ms" points to a location in data memory. It so happens that this location is identical with the offset of teh TMR0 register. Add some code before the definition of the "Delay100ms" label in the code and the address of the Delay100ms subroutine will change.

I think this is an "unfortunate" behavior of the assembler. In theory it should be possible to detect that a symbol belongs to CODE space and that is typically not useful to use it to work in DATA space (exceptions welcome). The assembler could at least have flagged this as a warning. Maybe there are options to make the assembler do just this?
If not, you may use a mnemonic technique to allow yourself to spot such errors more easily: Use unique prefixes to separate symbols in CODE space and DATA space. For example:
  • Use C_symbol1, C_symbol2 etc. for labels in CODE space.
  • Use D_symbol1, D_symbol2 etc. for addresses in DATA space,
Of course, use speaking names like Delay100ms, Random etc. instead of symbol1, symbol2,,,
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,880
Joined
Jun 21, 2012
Messages
4,880
... I still do not understand why, the explanation given was that the assembler does not know the difference between CODE and DATA space. I was simply pushing a literal into a variable... how can I prevent such a gremlin in the future!? ...
The problem, as pointed out by dan1138 in his post #9 on the Microchip forum, was your instruction that attempted to move a literal that you had placed in the accumulator with the movlw instruction into a symbol allocated to code space:

Code:
    movlw   .1 ;.175
    movwf   Delay100ms
    call    Delay100ms
The symbol Delay100ms is not a DATA space variable, it has a constant (address) value that is the address of a CODE space location, namely the address of the Delay100ms subroutine. Hence, the movwf instruction had the effect of moving the accumulator contents to DATA space (all the instructions operate on DATA space!), which just happened to be the address of TMR0 because the value of the symbol Delay100ms happened to be 0x01, from its location at the beginning of your code. So every time that instruction was executed the contents of the accumulator overwrote the contents of TMR0.

The assembler assumes you know what your are doing. Trying to move the accumulator contents into a CODE space address is not allowed, but is also apparently not checked by the assembler. Being that the assembler is not very smart, it doesn't notice that the address you are moving DATA to is not referenced by a DATA space symbol but is instead represented by a CODE space symbol. The symbol table doesn't distinguish between DATA space symbols and CODE space symbols. It converts both to a hexadecimal value. If the symbol table did distinguish between DATA and CODE symbols, the assembler would most likely be able to detect the syntax error and flag it for you. Since it doesn't, you have to keep track of that detail.

To prevent this from occurring in the future, declare and create symbols for all your DATA space variables (whether you use them or not) and never use a CODE space symbol as variable.

I hope this helps.

Hop
 

Arouse1973

Adam
Dec 18, 2013
5,178
Joined
Dec 18, 2013
Messages
5,178
As mentioned above. The call function name is like a pointer to some code that does something. You cant put a value in it.
Adam
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
I did some quick reading. It realy helps to understand the so called processor architecture you are using when it comes to assembly language programming.
I don't and that is making it much harder for me to work with these... I do understand that instructions and data run on two different buses. I am treating what I do not understand as a black box until I can fill in the gaps with experience or trial and error. The datasheet is expecting the user to have the background to make sense of the information!

Your explanation and suggestion are brilliant, thank you!!:D:D
Now it's a matter of learning how to sort out which 'variable' is a CODE space item vs. DATA space item.

To prevent this from occurring in the future, declare and create symbols for all your DATA space variables (whether you use them or not) and never use a CODE space symbol as variable.
Also excellent advice, can you guys elaborate on how to discern between the two spaces?

For example, in this program at the beginning I have set constants equal to certain decimal values. I then declared some user data, variables dc1-4 and RANDOM. I then create some functions, On, Off, Delay100ms, etc.

Would the functions be considered CODE space whereas the others are DATA space since they are either declared before hand or set equal to some value initially?
To some degree, this starts to make some sense as I review the code and seeing that the error should have replicated at the lines below, but didn't since we are moving a literal into a variable just as we did to Delay100ms - the only difference was that dc2 and dc1 were previously declared as UDATA!

Code:
dly2   
  movlw  .1
  movwf  dc2
  movlw  .1;.127   
  movwf  dc1


Have I got it?
 

hevans1944

Hop - AC8NS
Jun 21, 2012
4,880
Joined
Jun 21, 2012
Messages
4,880
Would the functions be considered CODE space whereas the others are DATA space since they are either declared before hand or set equal to some value initially?
Yes, but it is a good idea to always declare the variables in the DATA space but you have to initialize or assign values to DATA variables in your code. It is possible for some PICs (but not the PIC10 series) to have variables initialized with the assembler idata directive when code is assembled and downloaded but this requires an initialization block of code be downloaded with your program and executed each time the PIC is reset. Don't try to go there with your PIC10(L)F320 because data variable initialization is not supported. I would also avoid the linker for this and other small PICs and assemble absolute rather than relocatable code.

Here is a useful factoid: everything the assembler produces will be downloaded to memory locations in CODE space, and the values stored at those locations are NOT subject to change by PIC instructions. I think some of the later PICs DO allow the program to write to CODE space, but don't go there. Self-modifying code is scary and that's why you have a DATA space to store variables in.

Looks like you may have it. The code you just cited works because dc2 and dc1 are DATA variables accessible in RAM, anywhere from address 0x00 to 0x7f. But realize that addresses in the range of 0x00 to 0x3f are used by the Special Function Registers so they are not really available for variables. You can access the locations from 0x00 to 0x3f just like any other location in the RAM address range, but some of the bits are read-only, or are always read with a constant value (either 0 or 1) even if you try to write to them, all depending on the Special Function Register involved. So you store your variables in RAM starting at 0x40 and extending to 0x7f inclusive. If required, use program code to initialize variables. There is a snippet of code in the datasheet that shows how to zero-out a range of DATA memory if that is your desire. This advice is of course specific to this particular model PIC10(L)F320. Other PICs may have more RAM available.

When you create symbols in the CODE space and give them names, they will either be DATA symbols or CODE symbols. For example, you could define TEN equ .10 and then use TEN to represent the literal value decimal 10, or 0x0a, for use in an instruction with an literal immediate operand like movlw TEN. However, if you want to move a variable into the accumulator, you must declare it as a variable, NUMBER for example, whose symbol is an address in the DATA space. Then use movf NUMBER, 0.

I find this pair of mnemonics confusing because both instructions can result in data being moved into the accumulator, replacing whatever was already there, but movlw TEN explicitly names a literal (constant) value to move into W while movf NUMBER, d allows you to move either the contents at the location NUMBER in DATA space to the accumulator (W) if d=0, or to the register NUMBER itself if d=1, the latter for the purpose of setting or clearing the zero bit in the STATUS word for subsequent testing without affecting the contents of the accumulator or the variable addressed at NUMBER. I guess that can be handy if you are using a variable as a counter and want to see if it overflowed to zero after it was incremented, or if it was decremented to zero.

I know this is a might bit confusing, but that's what happens when you only have 256 words of program memory to work with. Gotta make every bit count.:D
 

chopnhack

Apr 28, 2014
1,576
Joined
Apr 28, 2014
Messages
1,576
assemble absolute rather than relocatable code.
When I first started with MPLABs I had read absolute code was obsolete. They recommended (as did some web videos) the use of relocatable code. I think the thought behind it was that with modern computers it was more efficient and it made the code itself more portable? Why are you recommending absolute?

I know this is a might bit confusing, but that's what happens when you only have 256 words of program memory to work with. Gotta make every bit count.:D
Indeed! Just as I get more comfortable with ASM and I am able to write and follow some very basic code I get slapped in the face with an ID10t error!!! Most certainly an HKI error, but very difficult to debug w/o more knowledge. I spent some good time tracing the code line by line to just see the TMR0 value reset! I could see where it occurred, but not why, so again much appreciation.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,722
Joined
Nov 17, 2011
Messages
13,722
Relocatable code is fine if you need to load code at runtime and don't know where in memory the code will be placed. This is a typical scenario when you have an operating system which loads applications from mass media, e.g. a harddisk. Relocatable code will also work when it is loadd to a fixed memory location, therefore it is universal.

Absolute code is fine when you do "bare metal" programming and you know where the code will be found in memory (e.g. at a fixed position in FLASH). This is what you are doing, therefore you can use absolute code.

As far as I remember, an advantage of relocatable code is that (or should I say can be as it depends on the processor architecture) code size can be smaller as often a short offset (8 bit) to the current program counter can be used to address e.g. jump targets whereas absolute code may require a fully qualified 16 bit address.
On the other hand, with relative code the fully qualified long address needs to be computed at runtime from the PC and an offset, which takes a tiny bit of time. Nothing you should have to worry, I think.
 
Top