PIC assembly - swapping #define

Thread Starter

redtree

Joined Feb 3, 2014
14
I've got it loaded, built and running (I think).
I'm trying to track the changes as they progress through the program.
I'm configured to fire off changes to the Ports, and I'd like to see them show up in a variable change. I usually would do this in the Watch window and simply add my variable names to the list. This doesn't seem to work when using the simulator. All I've been able to do is look at the PIC pins, but nothing internal to the pseudo-PIC.

Can this be done with the simulator or is this only for the device level debugging?

Thanks!
 

JohnInTX

Joined Jun 26, 2012
4,787
I usually would do this in the Watch window and simply add my variable names to the list. This doesn't seem to work when using the simulator. All I've been able to do is look at the PIC pins, but nothing internal to the pseudo-PIC.
Can this be done with the simulator or is this only for the device level debugging?
One of the reasons I spent the time I did on this was to further hone my chops on MPLABX/MPASMX and the enhanced midrange stuff. I encountered the same problem you have i.e. no stinkin' variables in the watch window. I've been looking through the help files but nothing yet. There are fleeting references to 'fast stepping' in the debugger and I've occasionally seen the *multiple* instances of IO_ModeBase that I've typed in but they go away as soon as I step.

I'm sure its easy. The method is somehow obvious.

My workaround was to just open the SFR and File Register windows and watch the registers at the hard-coded addresses change. After posting, I've been playing around with it to no avail. (All of my real projects are still on MPLAB 8.x; my encounters with X to date have been with XC8).

If you have 8.x, the code imports as is and works just fine under MPSIM with the conventional watch windows.
BTW: When you look at the outputs, watch the LATx registers.

I suspect the MPLABX issue has to do with absolute vs. relocatable modes but I think I will punt and ping Microchip on it. Just because I am old and crusty does not mean that some things aren't counter-intuitive.

Keep in touch.

EDIT: If anybody else knows what's wrong with the Watch/Variable windows, chime in.
 
Last edited:

takao21203

Joined Apr 28, 2012
3,702
I can hover over a variable in the source, and then I see the current value. Simple. The disassembly view is now shown for the function which is executed currently (place a breakpoint there).

Playing around with the new Configurator but the code it generates is not neccessarily useful. And only available for selected devices.
 

JohnInTX

Joined Jun 26, 2012
4,787
I can hover over a variable in the source, and then I see the current value. Simple. The disassembly view is now shown for the function which is executed currently (place a breakpoint there).
In this case, hovering over the variable shows it as 'Not Recognized'

But you got me to thinking again and searching on Microchip yielded the answer. It indeed was related to absolute vs. relocatable. The assembler has to be explicitly set for absolute mode for the globals to get into the symbol table it seems. I missed the setting when poking around. Hmmm...

Here's what to do.
 
Last edited:

Thread Starter

redtree

Joined Feb 3, 2014
14
Thanks very much, John.
I'm going through the code that you made and I have some beginner questions about it.
Does the colon following the name of the subroutine have any special meaning?
I'm familiar with calling different subs, but I just name them (without the colon).
You use variable(?) _sizeofIOmode (in the READ BCD subroutine). I see it defined later as " equ $ - _IOmode0). I thought equ were to specify an actual number. What does the "$" signify in this case. I've used it for use in my goto's - is it the program counter? How does the _IOMode0 figure into the equation? Isn't this the name of the subroutine?

I think I see the basics of what is going on. It seems pretty cool. I'm just getting hung up on some of the details to be able to effectively implement it.

Thank You!
 

Markd77

Joined Sep 7, 2009
2,806
The colon is just so that you can put code on the same line as the label. If there is nothing else on the line it is just a wasted keystroke.
Rich (BB code):
Delay_0:	decfsz	d1, f
          	goto	$+2
        	decfsz	d2, f
        	goto	$+2
        	decfsz	d3, f
        	goto	Delay_0
 

MaxHeadRoom

Joined Jul 18, 2013
28,697
Although one of the original/traditional methods of assembly programming was as John showed.
The Label was on its own line with a colon, which is optional.
Just makes for easier reading of the code on its own line.
Old habits are hard to break!
Max.
 

JohnInTX

Joined Jun 26, 2012
4,787
The colon is optional whether its on the same line or not. I personally use it as I do on its own line for readability and I can use long names without pushing the first line of the source way out. A bonus is that the ':' allows me to search on the label in the source while ignoring all the lines that refer to it. But Max has a point. Its something I picked up when punch-cards were still gee-whiz. I do find that the source is easier to understand and track visually when the formatting is consistent. Things like mismatched conditional directives (if, else, endif) can cause MPASM to go into the weeds, requiring intervention by the Windows Task Manager so I like that sort of thing to jump out at me. Just my .02

A side note: MPASM is the only assembler I can recall that doesn't enforce labels beginning in column 1. It issues a warning but that's it. This can cause problems if you use macros. If you misspell a macro, MPASM takes it as a label and issues a warning instead of expanding the macro, thus making defective code. Hilarity ensues - especially if you miss the warning.. and MPASM didn't always even issue that...

You use variable(?) _sizeofIOmode (in the READ BCD subroutine). I see it defined later as " equ $ - _IOmode0). I thought equ were to specify an actual number. What does the "$" signify in this case. I've used it for use in my goto's - is it the program counter? How does the _IOMode0 figure into the equation? Isn't this the name of the subroutine?
This is making MPASM do some of the work of keeping track of offset sizes etc. The '$' means current program counter so $-_IOmode0 is the the number of goto's between those two statements and in turn, calculates the size, in PC counts, of the goto array that make up 1 mode. This size (8) is important since the code needs the offset of the first goto for each mode. That's why the simulated BCD switch gets multiplied by the size of one mode (8). It works just to use '8' directly but consider what happens when you want to expand the table - maybe more IO or an separate entries for set and reset an output. The table will grow and you'll to recalculate the new size of one mode's goto list and then make sure that everything that uses it is also updated. Failure to do this will ensure pandemonium and since its a jump table, can present some very challenging debugging as the program can sail off into space at seemingly random times. Not recommended.

The last one, _Nmodes equ ($-_IOmode0) / _sizeofIOmode , calculates the number of modes that have been implemented. The arithmetic here is 24 / 8 = 3 modes implemented, numbered 0-1-2. The last line checks this number against what the SimulatedBCDinput is to make sure that you don't set it to 3 when you only have modes 0-1-2 implemented. Its a build time check. Try it by setting SimulatedBCDinput equ 3. The resulting error will save you from a lot of unnecessary pain.

Note that if you add another 8-goto mode, _Nmodes will be 4 (32 gotos/8gotos per mode). SimulatedBCDinput equ 3 will now pass since there are 4 modes (0-3) implemented in the table. MPASM does the calculations and updates automatically.

Note also that these constructs are assembly time calculations. They don't generate any code, just values. After the initial investment in learning to use them, you can coerce MPASM to do lots of looking over your shoulder and issue warnings and errors that can identify problems before they get to the PIC.

Two things (at least) missing are
1) A mechanism to ensure that each mode is correct (exactly 8 gotos) and
2) Most importantly, ensuring that the value of W used by _procModeChanIO is valid; maybe the program clobbers IO_ModeBase, for example. Since this is a computed jump, it can compute and jump right out of the table into unknown program memory. NOT good.

When you are comfortable with the basic code, we can introduce some helpful constructs to firm things up.

How's that?

EDIT: also on labels, the others in the table _IOmode1: etc are not used by the program. But they DO show up in the symbol table so if there is an issue, I can manually verify that each mode is the same size and their location in ROM.

Labels like in0true: are likewise superfluous but visually, it does complete the set of actions based on in0true/false, even though in0true is just flowed into. Arguably its a bad construct since it implies an entry point for the code. I'd probably agree in most cases.
 
Last edited:

Thread Starter

redtree

Joined Feb 3, 2014
14
Darnn, where were you when I needed you?
Scratch that, I STILL need you.
I did something similar already when I created a table to read data from.
My first table entry was a data point telling the program how much data was in the table to be read out. I didn't know there was a better way to determine the size.
Which, of course, leads me to another question.
How does the PIC make that determination?
I thought the PIC would individually step through the program and perform tasks as it approached them and that the program counter would tell it where the next instruction is.
Seems to me, that when it comes to the "sizeofIOmode"; it doesn't know what that is, does it? Does it then search out the equation for "sizeofIOmode" and then calculate it?
When performing this type of calculation, what if the parameters used for the calculation have changed? Does it use the collected data when the statement calculation is needed?
Or does it make the determination once for the entire program and never change it?
Seems to me like the equ statement gets executed only once per program build and is not changeable, right?
 

takao21203

Joined Apr 28, 2012
3,702
Darnn, where were you when I needed you?
Scratch that, I STILL need you.
I did something similar already when I created a table to read data from.
My first table entry was a data point telling the program how much data was in the table to be read out. I didn't know there was a better way to determine the size.
Which, of course, leads me to another question.
How does the PIC make that determination?
I thought the PIC would individually step through the program and perform tasks as it approached them and that the program counter would tell it where the next instruction is.
Seems to me, that when it comes to the "sizeofIOmode"; it doesn't know what that is, does it? Does it then search out the equation for "sizeofIOmode" and then calculate it?
When performing this type of calculation, what if the parameters used for the calculation have changed? Does it use the collected data when the statement calculation is needed?
Or does it make the determination once for the entire program and never change it?
Seems to me like the equ statement gets executed only once per program build and is not changeable, right?
correct. Is only evaluted at compile/assembly time.
 

JohnInTX

Joined Jun 26, 2012
4,787
I did something similar already when I created a table to read data from.
My first table entry was a data point telling the program how much data was in the table to be read out. I didn't know there was a better way to determine the size.
Which, of course, leads me to another question.
How does the PIC make that determination?
I thought the PIC would individually step through the program and perform tasks as it approached them and that the program counter would tell it where the next instruction is.
Seems to me, that when it comes to the "sizeofIOmode"; it doesn't know what that is, does it? Does it then search out the equation for "sizeofIOmode" and then calculate it?
You may be confusing what happens at build-time vs. run-time. Simply put, the PIC is only active at run-time and it just executes a sequence of bits and bytes. It knows nothing of labels, equs or any fancy sizeof stuff. It merely reads the bits from the memory location addressed by the PC, executes the instruction they represent, bumps the PC and repeats. Gotos etc modify this sequence by jamming a new value into the PC then off it goes reading memory from the new address. If you look at the detailed instruction list, the bit patterns for each are shown. Imagine having to manually create and stuff those patterns into memory to program the PIC.

The assembler takes care of all of that and you talk to the assembler using instruction mnemonics and directives.

Instructions are just that. The assembler reads the instruction string e.g. "nop" and runs off to look up the bit pattern that will make the PIC do a NOP. It also keeps a running count of the ProgramCounter value, in our case starting at ORG 0.

Its directives i.e. telling the assembler itself (not the PIC) to do something that make MPASM the powerful tool that it is. Pretty much everything that isn't instruction encoding is a directive although some are so common that they are just considered part of the language. Labels and EQUates are examples of simple directives. A label tells the assembler to associate the label with the current value of the PC. An EQU tells the assembler to also associate a name with a user-specified value. As the assembler encounters all of these it builds a big table (the Symbol Table) that it uses to resolve these labeled references to actual numbers as it makes the code. The values in question can be simple,
N_Modes equ 3 or complex. MPASM will attempt to evaluate whatever expression you give it and come up with a number to go with the name. It puts that number in the symbol table and uses it whenever the name is referenced. If it can't evaluate the expression, it will flag an error. If you use a named expression before its been defined, the assembler will hold off on a decision and leave space to see if its eventually defined. If so, it will go back and poke the proper values for that expression into the object code. If its never defined, that's the common Unresolved Reference error. Maybe you spelled it M_Modes or 'Mancy'. In either case, its a hard error.

You can view the contents of the Symbol Table in the assembler listing. Its also what MPLAB uses when you enter a variable or label name into a watch window to know what value it should be using to display stuff.

So this long answer is basically what takao21203 said, its used at build time only. Whether you say movlw N_Modes or movlw 3, the object image (those executable bits and bytes) is the same. The difference is how readable the code is and what happens if you modify it e.g. add a mode. If all you've written is movlw 3, you have some editing to do.. and make sure the 3 you find and edit is actually supposed to be N_Modes and not LED blinks etc.

When you have the time, look in MPASM help and search 'directives'. You'll see the familiar EQU and ORG as well as dozens of others that can be used to write better code and more importantly, write defensive code like the if - ERROR gizmo at the bottom. As indicated, that little jewel will keep you from setting SimulatedBCDswitch to something that would cause the table access to go into wierd-land. These defensive techniques are absolutely free in terms of code-usage but pay off huge dividends in keeping your sanity.

BTW: Having a value at the top of the table that describes it is not a bad way to do it and rather than manually counting numbers you can use the TableSize equ $-start_of_table construct to do the counting for you then use
dtm start_of_table ; .. to poke it in

The possibilities are endless.
Have at it!
 
Last edited:

takao21203

Joined Apr 28, 2012
3,702
Sure if you use the maximum of your mental powers, you can do amazing things in assembler.

One tought I had is what if you use the maximum of your mental powers in C? How does the resulting assembler look like?

That using assembler for regular coding is out of question is totally clear to me.

I mean, you use the maximum of your mental powers, and your assembler code is quite sophisticated, and the program does something cool.

But think what you could do in C if you use the maximum of your mental powers.

For me, hard coded I/O is a thing of the past- totally. I write the pins from the datasheet into a table, and then just solder randomly.

It is much slower yes that's correct, absolutely.

I wish I would not have spent years with assembler.

Rich (BB code):
!void refresh_line(unsigned char v_phase)
0x2C5: BCF STATUS, 0x5
0x2C6: BCF STATUS, 0x6
0x2C7: MOVWF v_phase
!{unsigned char i,anode;
! unsigned char* portio;
! 
! for(i=0;i<c_phases;i++)
0x2C8: CLRF i
0x2C9: MOVLW 0x5
0x2CA: SUBWF i, W
0x2CB: BTFSC STATUS, 0x0
0x2CC: GOTO 0x316
0x30E: MOVLW 0x1
0x30F: MOVWF c
0x310: MOVF c, W
0x311: ADDWF i, F
0x312: MOVLW 0x5
0x313: SUBWF i, W
0x314: BTFSS STATUS, 0x0
0x315: GOTO 0x2CD
! {
! anode=anodes;
0x2CD: MOVF i, W
0x2CE: ADDLW 0x9E
0x2CF: MOVWF FSR
0x2D0: MOVLW 0x81
0x2D1: BTFSC STATUS, 0x0
0x2D2: ADDLW 0x1
0x2D3: MOVWF 0x7F
0x2D4: BCF PCLATH, 0x3
0x2D5: CALL 0x100
0x2D6: BCF PCLATH, 0x3
0x2D7: MOVWF c
0x2D8: MOVF c, W
0x2D9: MOVWF anode
! portio=pin_port1[anode];
0x2DA: MOVF anode, W
0x2DB: MOVWF c
0x2DC: ADDWF c, W
0x2DD: ADDLW 0x20
0x2DE: MOVWF FSR
0x2DF: BCF STATUS, 0x7
0x2E0: MOVF INDF, W
0x2E1: MOVWF portio
0x2E2: INCF FSR, F
0x2E3: MOVF INDF, W
0x2E4: MOVWF font_data_size
! if(i==v_phase)*portio|=pin_bit_set1[anode];
0x2E5: MOVF i, W
0x2E6: XORWF v_phase, W
0x2E7: BTFSS STATUS, 0x2
0x2E8: GOTO 0x2FC
0x2E9: MOVF anode, W
0x2EA: ADDLW 0x7A
0x2EB: MOVWF FSR
0x2EC: MOVLW 0x81
0x2ED: BTFSC STATUS, 0x0
0x2EE: ADDLW 0x1
0x2EF: MOVWF 0x7F
0x2F0: BCF PCLATH, 0x3
0x2F1: CALL 0x100
0x2F2: BCF PCLATH, 0x3
0x2F3: MOVWF c
0x2F4: MOVF portio, W
0x2F5: MOVWF FSR
0x2F6: BSF STATUS, 0x7
0x2F7: BTFSS font_data_size, 0x0
0x2F8: BCF STATUS, 0x7
0x2F9: MOVF c, W
0x2FA: IORWF INDF, F
0x2FB: GOTO 0x30E
! else *portio&=pin_bit_reset1[anode];
0x2FC: MOVF anode, W
0x2FD: ADDLW 0x62
0x2FE: MOVWF FSR
0x2FF: MOVLW 0x81
0x300: BTFSC STATUS, 0x0
0x301: ADDLW 0x1
0x302: MOVWF 0x7F
0x303: BCF PCLATH, 0x3
0x304: CALL 0x100
0x305: BCF PCLATH, 0x3
0x306: MOVWF c
0x307: MOVF portio, W
0x308: MOVWF FSR
0x309: BSF STATUS, 0x7
0x30A: BTFSS font_data_size, 0x0
0x30B: BCF STATUS, 0x7
0x30C: MOVF c, W
0x30D: ANDWF INDF, F
! }
!     
! for(i=0;i<(display_width/2);i++)
0x316: CLRF i
0x317: MOVLW 0x7
0x318: SUBWF i, W
0x319: BTFSC STATUS, 0x0
0x31A: RETURN
0x31B: MOVLW 0x1
0x31C: MOVWF c
0x31D: MOVF c, W
0x31E: ADDWF i, F
0x31F: GOTO 0x317


In C language:

Rich (BB code):
void refresh_line(unsigned char v_phase)
{unsigned char i,anode;
 unsigned char* portio;
 
 for(i=0;i<c_phases;i++)
 {
 anode=anodes;
 portio=pin_port1[anode];
 if(i==v_phase)*portio|=pin_bit_set1[anode];
 else *portio&=pin_bit_reset1[anode];
 }
     
 for(i=0;i<(display_width/2);i++)
 {
    
 }
}


I hope some day Microchip adds a hardware bit matrix which is configureable with tables. IO will be much much faster. Otherwise, it is a pain to do it in assembler.

If you use hardcoded IO, port names and bit numbers will be scattered all over your code. It will be IC specific.

Using tables, it all can be found in one section.

I already found it a pain to make the I2C working which is only using 2 port bits.
 

JohnInTX

Joined Jun 26, 2012
4,787
Sure if you use the maximum of your mental powers, you can do amazing things in assembler.

I mean, you use the maximum of your mental powers, and your assembler code is quite sophisticated, and the program does something cool.

But think what you could do in C if you use the maximum of your mental powers.
What makes you think my C isn't amazing as well? ;)
 

takao21203

Joined Apr 28, 2012
3,702
What makes you think my C isn't amazing as well? ;)
I did not say that. However, I have during long nights taken a look at a lot of assembler sources which can be found on the internet. Both professional stuff (including some mysterious Bill Gates sources), and homegrown stuff. One homegrown source was about 1 Megabyte.

None of that stuff is reuseable in any kind, in most cases available special addressing modes are utilized + features of that particular assembler software.

About ten years ago I tried out Windows assembler. Yes it is absolutely possible.
 

Thread Starter

redtree

Joined Feb 3, 2014
14
Thanks for all the help!
I've been side-tracked with another project for a bit.

I got to point where I simulated what I think I wanted.
Now, I'm trying to migrate this into an actual board.
Not going as smoothly as I had hoped, but I think its because I'm being stupid.

If I have specific questions, I'll post them; otherwise I hope to post a SUCCESS soon!
 
Top