PIC12F509 & PIC18F26K20 - Page 1
Forum section: electronics / general

author: ocset
author_rank: Super Contributor
timestamp: June 04, 2017, 01:16:46 pm
content: Hello,We need to program these two in XC8 C.Can we do this?Also, do you know what configuration bit commands we have to use, and/or where to find out.Also, the libraries that we need to #include.As you know, there is a certain amount of "setup" code needed...any info on where to find out which "setup" code to use is needed.Our software engineer has just left, telling us his code wouldnt work because our hardware is injecting noise into the pic......so we need to write our own code...just something simple, to see if the micro is indeed getting corrupted by circuit noise...we will just write simple code which eg takes one pin high when an input on another pin goes low, say....just to see if it works, and does/does not get corrupted by circuit noise.On the pic18f26k20, it was the DALI code which wasnt working. The micro wasnt dimming leds according to the dali commands sent to it.

author: Andy Watson
author_rank: Super Contributor
timestamp: June 04, 2017, 01:33:11 pm
content: Unless you are prepared to publish hardware details, only you can determine the configuration bits. You will have to consult the datasheets but the first criterion is usually deciding what/where the clock source is. Keep the watchdog disabled until you have the basics working.

author: voltsandjolts
author_rank: Unknown
timestamp: June 04, 2017, 01:36:06 pm
content: Sounds like you needs some extensive help.Would you consider posting in the Jobs section and contracting someone for a few days. (Not me, but plenty of PIC experts around here)

author: JPortici
author_rank: Super Contributor
timestamp: June 04, 2017, 01:36:42 pm
content: read the devices datasheet (answer is yes you can use a pickit 3, it's written there)then read the XC8 manualShort version: select "configuration memory" under device -> memory views. set the configuration words. hit generate code, copy that and paste it in a .c file. not in an header file. that's the only "startup" code you need for an 8bit and 16bit pic.only include you really need is <xc.h> which then includes the selected MCU include file (register definitions)i suggest you also include <stdint.h> so you can use fixed width typesstandard c libraries, builtin functions like delays etc are documented in the compiler manualif you absolutely need peripheral libraries you need to download and install MCC, but there is no need for them at all

author: jpanhalt
author_rank: Super Contributor
timestamp: June 04, 2017, 03:21:35 pm
content: Quote from: treez on June 04, 2017, 01:16:46 pmDid that engineer leave behind his source code?If not, blinking LED's so to speak may not show you the noise source.Those two chips are quite different. What was the 12F509 tasked to do? Did both chips show the same problem? What was the problem, e.g., resetting?John

author: ocset
author_rank: Super Contributor
timestamp: June 04, 2017, 04:52:43 pm
content: Hello,Do you know why this code for PIC12F509 will not compile?It is written in MPLAB X IDE and using XC8 C compiler.All the code does is pick out a low to high transition on PORT GP4 and then wait 3 ms, then take PORT GP2 high.I am festooned with build errors at the moment.Code:/** File: zapper.c* Author:** Created on 03 June 2017, 23:55*///This code turns the product ON at the mains peak voltage//It does this by using the zero crossing detector input.//This uses PIC12F509//MPLAB X IDE//XC8 C compiler (free)#define _XTAL_FREQ 4000000#include <xc.h>#include <stdint.h>#pragma config OSC = ExtRC // Oscillator Selection bits (external RC oscillator)#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config CP = OFF // Code Protection bit (Code protection off)#pragma config MCLRE = ON // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is MCLR)//I have not set the MCLR pin as an input because it will be noise susceptible.//Therefore i set MCLR up as reset, but i will never use it as reset..but will//simply tie the pin top Vdd on the PCB.//Define output#define FETS LATBbits.RB2;//Define input#define zero_x PORTBbits.RB4;//Define actions#define ON LATBbits.RB2 = 1 /*Turn ON FETs*///Declare functions which set up the microcontrollervoid disable_interrupts(void); //How do this?void disable_pullups(void); //How do this?void setup_ports(void);void setup_ports(void) {TRISB = 0b00011000;return;}//Declare variablesuint8_t count;void main(void) {setup_ports();//10 second delayfor (count=1;count<=100;count++) {__delayms(100);}here:if {zero_x = 1} {goto here;}here1:if {zero_x = 0} {goto here1;}//When it gets to this point, the zero crossing input has just gone high__delayms(3); //delay to get to the mains peakON; //Turn ON FETs...at the mains peakwhile(1){;}return ();}

author: jpanhalt
author_rank: Super Contributor
timestamp: June 04, 2017, 05:08:34 pm
content: For starters, the 12F509 doesn't have a port B. They are GP0..GP5. TRISx is just "TRIS." It doesn't have any LATx register. It also doesn't have interrupts. I don't do C, so I don't know how the "disable interrupts" will be handled.John

author: Andy Watson
author_rank: Super Contributor
timestamp: June 04, 2017, 05:34:48 pm
content: This may not do what you want, but it should at least get you through the compile stage:Code:/** File: zapper.c* Author:** Created on 03 June 2017, 23:55*///This code turns the product ON at the mains peak voltage//It does this by using the zero crossing detector input.//This uses PIC12F509//MPLAB X IDE//XC8 C compiler (free)#define _XTAL_FREQ 4000000#include <xc.h>#include <stdint.h>#pragma config OSC = ExtRC // Oscillator Selection bits (external RC oscillator)#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config CP = OFF // Code Protection bit (Code protection off)#pragma config MCLRE = ON // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is MCLR)//I have not set the MCLR pin as an input because it will be noise susceptible.//Therefore i set MCLR up as reset, but i will never use it as reset..but will//simply tie the pin top Vdd on the PCB.//Define output#define FETS GP2//Define input#define zero_x GP4//Define actions#define ON GP2 = 1 /*Turn ON FETs*///Declare functions which set up the microcontrollervoid disable_interrupts(void); //How do this?void disable_pullups(void); //How do this?void setup_ports(void);void setup_ports(void) {TRISGPIO = 0b00011000;return;}//Declare variablesuint8_t count;void main(void) {setup_ports();//10 second delayfor (count=1;count<=100;count++) {__delay_ms(100);}while (zero_x == 1) ;while (zero_x == 0) ;//When it gets to this point, the zero crossing input has just gone high__delay_ms(3); //delay to get to the mains peakON; //Turn ON FETs...at the mains peakwhile(1){;}}

author: ocset
author_rank: Super Contributor
timestamp: June 04, 2017, 08:41:11 pm
content: Thankyou Andy Watson !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!You are a genius as far as i am concerned !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!BUILD NOW SUCESSFUL !!!!!!!!!!!!!!!You may just have saved my job, and saved my company from going bust !Thankyou!

author: KL27x
author_rank: Super Contributor
timestamp: June 05, 2017, 01:27:50 am
content: QuoteWithout even looking at the datasheet, I'm sure 12F509 has at least 2 timers which can generate an interrupt aside from the WDT and some GPIO digital input interrupt available on at least 2 of the pins. I've never seen a PIC without an interrupt. I can't imagine any microcontroller that does not have at least a timer interrupt.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 05, 2017, 03:50:08 am
content: Quote from: KL27x on June 05, 2017, 01:27:50 amMaybe that's why there is a datasheet:http://ww1.microchip.com/downloads/en/DeviceDoc/41236E.pdfJohn

author: KL27x
author_rank: Super Contributor
timestamp: June 05, 2017, 09:21:48 am
content: Thanks for the correction. I am quite used to being wrong, by now. I have the datasheet in hard copy that is practically wore out from use. But it has been many years and many upgrades later.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 05, 2017, 09:46:53 am
content: Quote from: KL27x on June 05, 2017, 01:27:50 amThe 12F509 is a very old design, and it shows. The first PIC I used was its predecessor the 12C509, which has EPROM instead of Flash ROM. I used the OTP version. No in-system debugging - so the development cycle was write program, burn chip, test operation, throw in bin and get a new chip! Teaches you to be really careful about getting the code right first time...

author: JPortici
author_rank: Super Contributor
timestamp: June 05, 2017, 09:57:02 am
content: pic10 (baseline) do not have interrupts!

author: jpanhalt
author_rank: Super Contributor
timestamp: June 05, 2017, 10:02:45 am
content: @Bruce,Last year was my first experience with an early 16Cxx chip. The project was done to verify that the known problem with an interrupt occurring while clearing GIE could not be demonstrated with later chips (other then the three that Microchip recognized as being affected).Here is a statement of the problem from Microchip's datasheet for the 16C84:QuoteAnyway, those chips -- at least authentic ones -- are quite dear in today's market. I only had 2 to test. I can't imagine programming every day under those conditions. Fortunately, MPLab worked for me. I still have one virgin 16C84 left.John

author: jpanhalt
author_rank: Super Contributor
timestamp: June 05, 2017, 10:06:41 am
content: Quote from: JPortici on June 05, 2017, 09:57:02 amThat's true, but not all 10Fxxx chips are baseline. The 10F32x do have interrupts and output latches, and they are cheaper than the 12F509.John

author: KL27x
author_rank: Super Contributor
timestamp: June 05, 2017, 10:30:35 am
content: QuoteThis is why we have these archaic IC socket things, eh? I remember buying a bunch of them because I was supposed to need them and they looked cool. Then I had no use for them. I guess EEPROM and ICSP and self-writing devices enabling bootloaders weren't always the norm.But wow, looking back, I remember trying to use up some of these old PICs. I had to use 12F629 over the 508, for even very simple stuff. Even the way I write code would not work with baseline architecture, anymore. First, I'd run out of stack. Second, the whole not having a return command... i.e. you can't even preserve the W register upon returning from a subroutine. Third... no interrupts..? It is painful to even write assembly for these baseline devices.I understand why C compilers are not efficient with these devices. It is because assembly isn't even efficient. You spend 3 or 4 times as much code to do the same thing, if you can even do it at all. Even for the minimal cost savings in a high volume project, the baseline PIC is more trouble than it is worth if you need accuracy of the internal oscillator. It is quite easy for the factory tuned calibration number to be lost or corrupted by a bad ICSP connection and create a big problem. Putting in a defaut middle of the road setting will result in error of up to 20%. And recalibrating it while in circuit takes a lot of time, if it is even possible. This is not quite the same problem as OTP, but even this is quite a pain.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 05, 2017, 08:22:04 pm
content: Quote from: KL27x on June 05, 2017, 10:30:35 amPreviously to PICs I was using 8031 and 68HC05 with external EPROM, and before that Z80. Most MCUs back then were mask programmed. You could get windowed EPROM versions for development, but they were very expensive and took 20 minutes to erase (and often failed after a few write/erase cycles). I also obtained an 8031 variant that had a 'piggyback' EPROM socket on top of the chip.QuoteAnd yet they could still be useful. My first PIC project was anESC for RC models. That little PIC was able to read a 1-2ms servo pulse with glitch filtering and failsafe, generate 2.7kHz PWM, apply the brake and check for low battery voltage, all in real time. It replaced an analog circuit that used several ICs and was less sophisticated.QuoteActually this isn't quite true. PIC has a 'RISC' instruction set that might appear to need more code, but every instruction is only 1 word and most execute in a single CPU cycle (4 clock cycles). You may need more lines of assemblersource codeto do the same job as a typical CISC MCU, but the machine code is often more compact and faster. For example the original 8031 took several bytes and 12 clock cycles or more to do simple things like testing a bit in a register.The PIC just does things a bit differently. On baseline PICs you set W to a literal value when returning from a subroutine, so you have to put variable return values into a 'file register' (RAM) instead. But file registers in the PIC are like CPU registers in other architectures, so in reality all valuesarereturned in registers! The stack only has 2 levels, but with no interrupts and no PUSH or POP you know exactly how much stack is required. You can only call subroutines 2 levels deep, but most of the time this is plenty for the things you can do in 511 words of ROM or less (and when it isn't you just inline the code).QuoteThis was true of older chips such as the 12C509, but modern versions are a lot more accurate. Loosing the calibration value is only a problem if you use bad tools. At least PICs don't brick themselves if you choose the wrong oscillator option like AVRs do.Baseline PICs certainly are limited, but when introduced they were a big advancement over other MCUs when you needed a compact device to do a simple job. And with the 10F series they still are. Of course now with the 10F322 you have even more power (midrange instruction set, 16MHz clock, ADC, PWM, Configurable Logic Cell etc.)

author: KL27x
author_rank: Super Contributor
timestamp: June 05, 2017, 10:57:31 pm
content: QuoteYes, I appreciate that. I code in PIC assembly, extensively. But then you have to move this data back into the W register half the time, which is extra code space and execution time. Often it is the case you need to transfer data between subroutine and main code loop using registers in addition to W, but W is almost a given that you might not want to overwrite that with a literal. That's an extra code and execution on both ends, in the subroutine and the main code loop. At least. W transcends banks, too, so it could be even worse.QuoteIndeed. I appreciate this fact quite a bit. This is one reason I do not use 18F unless I am using C. Many of the instructions are 2 or 3 lines of code and execution. And this takes away one of the elegant things about assembly in PIC midrange devices. This essentially wrecks the ability to branch by making relative changes to the PC. What I meant was that compared to midrange devices, for which this observation also holds true, assembly is quite a bit less efficient in the baseline PICs. This is true both in regards to code space and execution cycles.I bet you could have done your ESC for RC models in significantly less code and end up with less latency (fewer instructions executed) on a midrange device, by utilizing just a few of the midrange instructions and special registers (like LAT), and having an ISR. Then, just with the larger stack, you could halve the remaining code space, perhaps more.Regarding OSCCAL, the first thing you do with any dev board is read it and write it down, somewhere. This extra labor is what makes cost savings of baseline questionable in any large quantity project. And in small quantity, the cost savings are not existent. I know 509 was advanced when it came on the market. I don't know if it will ever die, but maybe it should.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 06, 2017, 08:45:22 pm
content: Quote from: KL27x on June 05, 2017, 10:57:31 pmMe too - and Z80, MC68000 etc. 90% of my code is assembler.QuoteW is the accumulator, equivalent to the A register in 68xx, Z80 etc. where it is a similar bottleneck. However the PIC has instructions such as DECF and BTFSS which don't involve W. MCU applications often do more bit manipulation than math operations, so the single accumulator isn't that bad.QuoteI use W to pass a literal value because MOVLW is only 1 cycle. Variables are passed in file registers.Compared to CISC you need to write more lines of code, but macros can cut the typing down dramatically. To get a true comparison of code efficiency you need to count the number of words and cycles used in the entire operation, not just focus on individual instructions. Where PIC falls down compared to more powerful 8 bit MCUs is cycle time. AVRs are faster because they only need 1-2 clock cycles per instruction, whereas PIC needs 4. Newer PICs with 32MHz clocks are about as fast as a 16MHz AVR.QuoteI mostly program midrange 12F/16F and do use the extra instructions, but most of time they don't make a lot of difference. The biggest improvement by far is peripherals like PWM which take the load off the CPU. But newer chips are becominglesscode efficient (and a pain to code in assembler) due to all the bank switching required to access them. This doesn't really matter though because they have faster clock speeds and more memory.Baseline PICs will eventually die because they can be replaced by midrange chips at lower cost. When I run out of a particular PIC I upgrade to the more modern equivalent eg. 12C509 to 12F675, 10F200 to 10F322.QuoteContrary to popular belief, ISRsincreaselatency, and my ESC code would totally break if interrupts were used. I did get lower latency and higher resolution by using the PWM and 16 bit timer modules in a 12F617.QuoteBetter tools avoid overwriting the OSCAL value, so you shouldn't have to do this.Slightly OT - I have a Cypress CYKIT-049 dev board which comes pre-programmed with a bootloader. But the bootloader is part of your application, so if you forget to include it then the board cannot be reprogrammed via the onboard interface and so is effectively bricked!

author: jpanhalt
author_rank: Super Contributor
timestamp: June 06, 2017, 09:00:46 pm
content: @Bruce AbbottI agree in large part, but have moved to the "enhanced" mid-range and the 2nd generation of that species. They are not significantly more expensive (for a hobbyist) than the older mid-range and have greater capabilities. Some of which are direct operation on WREG, instructions for addition and subtraction that incorporate the carry or borrow bit inherently, and enhanced math.John

author: KL27x
author_rank: Super Contributor
timestamp: June 06, 2017, 09:13:50 pm
content: QuoteI have difference of opinion, here. IMO, nearly everything that is good about the baseline is gooder in the midrange and the enhanced midrange. And if the improvement between the enhanced midrange and the regular midrange is moderate, the gap between the baseline and midrange is enormous. More banks are there due to more features... if you don't use those extra features, you aren't doing much more bank switching, IMO. Many of the most commonly accessed special registers are accessible in all banks. In addition, when you get to the enhanced, there are some nice additions which help in this regard. The enhanced can access all banks of its memory linearly, and they also have 16 bytes of what they call "nonlinear" user memory that is accessible from all banks.QuoteI suppose it depends on what you're doing and how you write code. But stack size alone... if that doesn't make a difference even on the simplest of programs, I ...Regarding ISR, I may have jumped the gun on your ESC project. I dunno what all it's doing. Entering and exiting the ISR eats some instruction cycles. But wherever you are going to use one is usually going to reduce latency and almost always increase efficiency in code volume and/or power consumption. I should not assume your code could possibly be improved in efficiency in any way regarding latency.. maybe you had to work very had to optimize this aspect.QuoteI will personally not touch a macro, ever, under any circumstance. There is no occasion where it is worth the trouble for me. When you get errors, and the IDE points to your macro.... which is used 20 times....the IDE can't show you which one is throwing an error. It can't even show you where you put all these macros.... So after you change the name of your macro and use the duplicate label error to locate them all, you still have to suss out the actual problem by studying the surrounding context of potentially each and every one... rather than knowing the exact one, single epicenter which you would otherwise be handed on a silver platter.Just from this statement alone, I can appreciated the effort and pain you go through to get the highest speed performance out of a baseline PIC to perform relatively simple tasks. I'll take the extra 4 cycles wherever possible to make my code more manageable by using subroutine over a macro. There are some things macros can do in one line which subroutines can't. I'll use two or 3, or however many it takes. If the latency is a real issue, I'll cut and paste the actual code over using a macro.

author: ocset
author_rank: Super Contributor
timestamp: June 07, 2017, 07:33:53 am
content: Hello,Please could you help..the code is building now but not working.....please coudl you see why the following code does not turn a port on/off/on...Quote

author: jpanhalt
author_rank: Super Contributor
timestamp: June 07, 2017, 10:08:40 am
content: Are you testing the on/off with a scope or something like and LED? An LED may not show a change (30 ms is quite short) -- use 200 ms.If scope, what do you see? Just flat line?Sorry, if those are stupid comments as I do not do C.I assume GP3 is tied high with a resistor.John

author: ocset
author_rank: Super Contributor
timestamp: June 07, 2017, 11:00:20 am
content: Thanks, im testing with scopeIve got this far now, but the delay isnt working....either that or the for loop isnt working?Quote

author: Andy Watson
author_rank: Super Contributor
timestamp: June 07, 2017, 11:38:47 am
content: This statement (and the one after it):Quotetranslates to this assembly code:Code:423:;Ab.c: 58: };Ab.c: 60: while(GPIObits.GP4 = 1);bsf6,4;volatilegotol423line61It's stuck in an infinite loop because the "GPIObits.GP4 = 1" always resolves as true. You need to replace the assignment operator (=) with a comparison operator (==), i.e. while(GPIObits.GP4 == 1);

author: jpanhalt
author_rank: Super Contributor
timestamp: June 07, 2017, 12:43:01 pm
content: Isn't GP4 an input with a weak pull-up enabled? (TRIS = 0x18, OPTION = 0x00)That shouldn't toggle unless you have added a switch. Then what happens to GP2, which was your output, but does not seem to have a function defined for a changed input in the revised code?Again, my apology for being C-disabled.Good luck.John

author: android
author_rank: Regular Contributor
timestamp: June 07, 2017, 01:15:48 pm
content: Quote from: Andy Watson on June 07, 2017, 11:38:47 amI was wondering when someone would point that out. This has got to be one of the most common C programming errors and I think the blame lies squarely on the inventors of the language.Better still IMHO is to avoid comparison operators when testing for zero or not-zero...Code:while (GPIObits.GP4);...and to make it even more readable use a DEFINE to name your bits and then codeCode:while (SIGNAL_ASSERTED);

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 07, 2017, 10:14:29 pm
content: Quote from: KL27x on June 06, 2017, 09:13:50 pmThe core code is very simple - measure servo pulse width by incrementing a counter whenever the pulse is high, which is done with this macro:-Code:DoServoCount MACRObtfsc GPIO, ServoBitincf ServoCount, FENDMThis takes 2us to execute. Servo pulse width is measured with a resolution of 7us, so 5 instructions can (must) be inserted between each invocation of the macro. This also creates the step time for the PWM pulse, which requires continuous execution of this code:-Code:decf PwmCount,Fskpnz ; end of PWM hi pulse?bcf GPIO, RunBit ; yes, then turn off motorThese two pieces of code are interleaved repetitively so they execute 'simultaneously'. That leaves 2 instructions per 7us to do other things, which I use to check the battery voltage.I considered using Timer0 to measure the servo pulse width, but it turned out that there was no advantage. Even if the 12C509 did have a timer interrupt it wouldn't help because it would upset the PWM timing. The 12F675 has a gate on the timer clock which makes it possible to measure pulse time in hardware, however it is active low so you need an external inverter to measure positive pulses!QuoteYou can be a masochist if you want, but there's no doubt that macros make asm coding easier. Here's one of the macros I used in my 12F617 ESC, which needed 16 bit math to get 1us resolution:-Code:lsl16 MACRO aaclrcrlf aarlf aa+1ENDMThis generates 3 words which effectively create a single 16 bit instruction. Why bother having to type in that repetitive stuff by hand when a macro can do it for you? And it makes the code a lot easier to read and understand.Here's the macro in use:-Code:; multiply by 10lsl16ServoCount; *2mov16ServoCount,AARGB0 ; temp = * 2lsl16ServoCount; *4lsl16ServoCount; *8add16AARGB0,ServoCount; *8 + *2 = *10QuoteOften I will create a subroutine to do a small job, then when the algorithms are sorted I will go back and inline it. I will also break a large block of code into simpler subroutines that can be reused - but always bearing in mind the speed penalty. This is the sort of stuff you have to do in asm that a compiler generally does for you automatically. But I prefer doing it manually because then Iknowwhat code will be produced.

author: KL27x
author_rank: Super Contributor
timestamp: June 08, 2017, 01:10:37 am
content: Wow, cool beans. I have to admit I may be shortsighted on the macros thing. Been a long time since I removed all macros from my code and finished cursing their very existence. The lsl16 macro don't really excite me. But the interesting thing for me are the ones with multiple literals. Is this what they look like?mov16 macro aa,bbmovf aa,wmovwf bbmovf aa+1,wmovwf bb+1ENDMadd16 macro aa,bbclrcmovf aa,waddwf bb,fbtfsc STATUS,Cincf aa+1,waddwf bb+1,fendmOnce tested and verified, I am generally not looking too close at the code again, other than my comments. But the multiple literal ones are hard to bury from sight, and my method of doing that would have been horrible, in comparison.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 08, 2017, 10:10:58 am
content: Since this discussio is way OT, here's another couple of bits:Bit0: For someone who swore off ever using a macro, what is the difference between a macro and pseudo-instruction, such as CLRC?BIT1: For someone who doesn't find the enhanced mid-range instruction set useful, it would help you from using the CLRC in the"lsl16" macro (as just one example):Code: (asm)p=12F1840lsl16 MACRO aalslf aarlf aa+1ENDMJohnEdit: changed pseudo code to pseudo-instruction

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 08, 2017, 03:06:09 pm
content: Quote from: jpanhalt on June 08, 2017, 10:10:58 amThanks for that. I have a couple of 'enhanced' midrange PICs, but haven't done much with them yet because I still have a bunch of older chips to use up.

author: KL27x
author_rank: Super Contributor
timestamp: June 08, 2017, 10:25:48 pm
content: QuotePseudo instructions are treated by the IDE exactly like instructions. Macros are not. To not use banksel, I can't even imagine.#define xxxx is fine, too. Rock solid.equ, no problemassembler/IDE directives... awesome.There are probably a lot of other differences for macros I am not even aware of. But the one that has the biggest suck IME is that there are instances where the assembler will decide that an error is located in a macro. The code in the macro can be 100% perfect. But due to some change to other parts of the code, the error will show up in the macro.The IDE gives you link and line number..... of a line of code in your macro. I think I explained this in detail, already. One of the big reasons to use a macro is so you don't have to write the same thing over and over, so chances are you used it a lot more than once.When your code is 100 pages spread across 5 different *.asm files, this results in a hideous waste of time. If your code is only 10 or 20 pages and/or you use the macro only once or twice, macro may not make an enormous difference, at all. I think in general, I tend to adopt whatever is going to work if and when the code becomes long and complicated. I appreciate the speed you can get with assembly, but I am quick to give up SOME of it to adopt code style that will not fall apart when you find you need to add more straw to the camel's back.Other reason (that may be useless to others) is that every other "line" in midrange/baseline assembly with a few exceptions equals one instruction. Which makes it useful for managing branches/jumps with relative changes to the program counter. Using relative changes to PC allows easier cut and paste of code that you may reuse, with less editing. This is maybe an additional reason I find use of subroutines so useful... not for the ultimate execution speed, but for writing and managing the code.There are many ways to skin a cat, esp in assembly.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 08, 2017, 11:42:36 pm
content: Quote from: KL27x on June 08, 2017, 10:25:48 pmGlad you feel banksel is a useful for you. It is an Assembler directive and is somewhat different than the pseudo-instruction CLRC in that it has no ad hoc translation to opcode(s) without knowing the MCU. That is, it is like other directives, e.g., RADIX, __Config, etc. I tend to use MOVLB <x> as I like to know andtrackwhat bank I am in. I almost never use banksel unless I am very tired. I then disassemble and change to MOVLB at my first opportunity.Every PIC microcontroller has a defined instruction set that maps directly to machine code. CLRC (i.e., BCF 3,0) is a macro that the compiler/Assembler interprets as an allowed instruction or, for other pseudo-instructions, a sequence of allowed instructions. Consider for example, SKPNZ, SKPC, BNZ, LCALL and so forth. That is, not all are single instructions. Many are two instructions, and LCALL is three instructions. The MPASM Assembler User's Guide shows the macro definitions for pseudo-instructions and disassembly of code using them shows the same translations. Disassembly of Assembler directives does not usually show specific opcodes; although, Banksel does.I don't believe the distinction between a macro you define and one defined in the Assembler is a significance difference. They both work the same way, take the same program space, and disassemble to inline code.John

author: KL27x
author_rank: Super Contributor
timestamp: June 08, 2017, 11:54:48 pm
content: If it is the same thing, i will concede to your superior knowledge. But I have never had assembly error point to the actual expanded code for a pseudo instruction or assembly directive, in the linker file or whatnot. Leaving me no clue where my implementation of said macro is causing a problem in my own code.Perhaps it boils down to understanding exactly what u can put in a macro where this is not possible. I have no clue what that is, admittedly.I struggled with banksel, myself. I ultimately decided it is better for me to use it. By default i try to write my code with the assumption it may end up multiple pages and banks and that i may have to do some shuffling. And if changing device. And who knwos what else. I know what bank are most things anyhow and banksel 0h, banksel portx, for instance i know is bank 0. If I don't want to look up bank of cmcon1 or whatnot, I banksel, then banksel back.... if I learn this was redundant, later, I can remove it, later.Im fully aware of many instructions/exceptions which expand to multiple instructions, yeah. Why you get lcall as 3, though? I may be making an incorrect assumption. I always treat it like 2, pagesel x, call x, and my code works this way. Does pagesel expand to 2 instructions (on certain but not all devices, perhaps? That is a lot of pages, lol). Or are you counting instruction cycles? Yeah, call, goto, return, et al, they take 2 to execute, but they are one instruction as far as the program counter and code space are concerned.

author: KL27x
author_rank: Super Contributor
timestamp: June 08, 2017, 11:57:16 pm
content: Im dumbing it down here: One visual difference is that banksel and clrc and say movfw (on midrange) all show up blue, not purple like a macro. Purple also for label or an address, literal, constant, etc. I dont know why that is the case if the IDE handles them identically.Maybe i should be asking why u can use macros and not have this problem? Of macros hiding source of error? I mean i used pseudo instructions and assembler directives hundreds of times in my code and never have error pointing to hundreds of locations for one misuse of said.Im not linguistic expert and i didnt rtfm all the way. I'm pretty sure I wouldn't understand all of it if I did. My guess is that the pseudo instruction is blue and treated like a single instruction, even if it expands to 2 or 3. When there is an error, it points to this specific implementation of said (pseudo) instruction. Conversely, the ide treats a "user" macro like the expanded code that it represents, and it will point, unfortunately, to the actual line of code where it determines the fault.... the line of code in the friggin macro.... which means it points nowhere/everywhere.Is there an way to make the IDE treat a macro like a single instruction as far as error reporting goes? To get the error point to the unique implementation of the macro? THIS is my problem with macros. AFAIK, the IDE is never going to tell you "hey, one of your clrc's is fucked, but I'm not going to tell you which one. Here's a hint: it's a bad implementation of BCF STATUS,C." (Which in this case would be obvious.) AFAIK, the IDE will do that with a macro. Maybe I'm missing something. Does the link jump you to the macro, but maybe there's a line number for you to figure out which one?

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 09, 2017, 03:01:38 am
content: Quote from: jpanhalt on June 08, 2017, 11:42:36 pmThe advantage of BANKSEL is that the assembler automatically chooses the correct bank for the register. The disadvantage is you don't know which bank that is so you have to put it beforeeverynew file register reference to make sure the correct bank is always selected.If you know what banks the registers are in then you can use BANKSEL (or MOVLB, BSF RP0 etc.) only when necessary. However you then have to avoid jumping into code that assumes one bank is selected when you are in another. It is easy to introduce bugs this way, and it doesn't help that the registers for a single peripheral may be spread over several banks.So you either have to bloat the code, or optimize bank selects but end up with brittle code. Coding in assembler is hard enough already without having to deal with this issue, which is why Ihatebanked registers! Unfortunately this is the price you pay for having lots of RAM and peripherals and an instruction set that can only address 64 registers.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 09, 2017, 03:30:20 am
content: Quote from: KL27x on June 08, 2017, 11:57:16 pmThe problem occurs when you have an error that gets past the macro invocation line but breaks inside the macro itself. Since the parser is already in the macro at this point it cannot tell which line was the original cause.However it's not that much of an issue because most coding errors (syntax errors, undefined symbols...) will be reported on the line where they occurred. Also it is unlikely for a macro to throw up errors in several places unless there are errors in all those places or in the macro itself, so you generally only have to look at the last bit of code you wrote to find the error.But hey, it's no worse than a C compiler throwing up cryptic error messages 50 lines below where you missed out a bracket or semicolon! (or worse, buried several directories deep in an include file so full of #ifdefines that you can't figure out which bits of the code are active...).

author: KL27x
author_rank: Super Contributor
timestamp: June 09, 2017, 08:22:30 am
content: QuoteMaybe something to do with how I code. But I was able to do this many times even with macros which have no syntax... beyond simple macros that should always be valid and have no additional modifier/literals. No clue as to the origin, short of ripping them all out and replacing with actual code. I wish I had an example, because it never made any sense to me and I can't recreate one, intentionally.QuoteYes, I never got this. What I meant was when the error (super rarely for everyone but me) "parces in the macro" and that is your only clue, you have to assume it is any one of the instances where you used it. The IDE only shows you one source of error, but you have to look at them all.QuoteAny time you write/add new code, you're temporarily breaking it, and you might have to alter several parts before it (hopefully) assembles and works, again. Writing new code is hard enough in assembly... backtracking even my own code to find a ghost error is quite annoying, I have to say, lol. Analogy is where I had 2 shorts on a pcb between power and ground, recently. It's just a 6 square inch board, and the power rail is plainly in view in its entirety, more or less, from one side. And for the life of me I couldn't find a short under the microscope. Making some cuts in the power rail to narrow it down and suddenly it becomes obvious. Even if "the last thing I altered" is the problem, that could be 4 or 5 different areas of the code.I am going to definitely give it another go. Maybe I can start to understand how it is an error ends up "parcing in the macro," better ways to prevent or minimize the damage, and better ways to unravel a solution after the fact. But suffice to say when this happens, it is bigtime problem. If/when I get another bad apple, at least maybe I can share it and hopefully learn something from someone with more experience. It has also been many years since I ditched the macros, and my coding and coding practices have evolved, with code integrity and revision history and documentation being more meticulous than ever. So maybe I brought the problems on myself... Dunno. I don't really remember too well, all of the details. But the pain is hard to forget.QuoteThis is a logical fallacy. If you use banksel you do not HAVE to know which bank you are selecting. This is not the same as not knowing. Even when I know exactly the bank in which said register resides, I still use banksel. Even when I optimize and remove redundant bank changes, I am using banksel command. I personally have no real issue with banks. It is easily second nature. The first time I spilled over into page 1, OTOH.... them were some long nights. That, too, is second nature, now, but the learning curve was much steeper.Even if you prefer to have direct control, you can do it with banksel, after defining your banks:banksel bank0banksel bank1etc.This is probably more sensical than movlb when you have 16 banks. And going back to device with 2 banks... why change the format when this works for them all. I #define my banks according to the memory map for each device, based on the start address of the user memory, if available on that bank. Or the first register if no user memory is present. Even though I never actually use the "bankx", myself, except maybe bank0. I mostly banksel [register name] and learn what bank that is, but I may refer to this "map" in case I need to arrange user memory for direct access. Easy access by clinking on an inc file, rather than opening the datasheet.Example for 16F1938#define bank0 0x020 ;;;;;;;;;;;;;;;;;;;;;#define bank1 0x0A0#define bank2 0x120#define bank3 0x1A0#define bank4 0x220#define bank5 0x2A0#define bank6 0x320 ; thru 0x32F, just 16 register on this page...etc, etc.So you see, I'm not against the principle of macros making code harder for other people to read. I would use them extensively, if it were not for the error reporting.(Why not #define bank0 banksel 0x020, so's you don't have to type out "banksel" ? I can't stand this looking like a label.)

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 10, 2017, 11:46:25 pm
content: Quote from: KL27x on June 09, 2017, 08:22:30 amIt ends up in the macro when a parameter seems to be OK where it was used, but fails when the macro looks at it. For example a symbol which is supposed to represent a register, but resolves to an invalid address, won't fail until the macro tries to reference it.You can find the line with the mistake on it by examining the listing file. Search for the error message and you should see the macro expansion immediately after the line with the bad code.QuoteWhen I said "don't know" I meant that the code doesn't tell you. Of course you can look it up in the datasheet or include file, but BANKSEL is not intended to be used that way.QuoteMe too. Different PICs have different ways of selecting banks. BANKSEL automatically produces the correct code for your PIC, so it makes sense to use it.

author: KL27x
author_rank: Super Contributor
timestamp: June 11, 2017, 01:30:58 am
content: QuoteListing file. Cool beans. I will have to check this out. If this means what I hope it does, this just changed the way I code! If it means what I think it does... i.e. macro is expanded in every instance (seems like this is a given in order to produce a list file... the whole point is to show every line number and instruction) and there is still no way to distinguish (I am convinced you do not get a line number in this type of error... maybe I'm wrong; I hope), let alone automatically jump to, the problem area, then... well, thanks, anyway!Having to scroll through the entire list file is not very convenient, even if there were a visual distinction. In short... if there is a line number showing where the error is, I will feel pretty dumb but also glad to find this out. This is the main reason I removed macros from my code... though there are some others.QuoteThis could be the answer, I suppose. But I am afraid it isn't an answer. Errors happen because I make errors. But if I can manage to get an error to manifest in the macro (and only in the macro), again, I'll be sure to post it. I'll be sure to examine the error report for a line number. (And examine the list file to see if it left any clue.) I'm sure it will likely be a boneheaded error. But I am even more sure I will continue to make even more boneheaded errors.Regarding banksel, I thought of another advantage. Even though I can't tell you what bank LATA is in (other than is it not 0, and it is not the same bank as TRISx), I know without looking that LATB and LATC are in the same bank, and I can manipulate those registers without adding redundant banking instructions. I can think of no real drawback to using banksel. There are quite a lot of times the actual bank doesn't matter. I'm gonna check a flag or change a bit, then go back to bank0. Another place is in the initial configuration: swapping from INTCON to CMCON to TMRx to whatnot. I don't really give a rats ass what banks those are. I am back to bank0 in the end. If you use direct bank bit write, you need to look these up. If you use banksel, you can often gain the same benefit without knowing. And you can still look them up, if you want.I don't really care to really know by memory all the special registers in a bank unless my code is frequently using these registers and/or spending a lot of time in bank other than 0. This is a need-to-learn basis kinda thing. And learning the registers in a given bank is all you need, in this case. You don't need to remember the actual bank numbers.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 11, 2017, 07:21:51 am
content: Quote from: KL27x on June 11, 2017, 01:30:58 amYou don't need a line number. Just search for the error message and the editor will jump to the line where the macro was expanded, which is right below the line where it was invoked.Here's an example.Source code with mistake (invalid address):-Code:#define myvar 4000.rr16 myvarOutput message:-Code:Executing: "C:\Program Files\Microchip\MPASM Suite\MPASMWIN.exe" /q /p12F617 "Speed617.asm" /l"Speed617.lst" /e"Speed617.err"Warning C:\CODE\PIC\ESC\SPEED617.ASM 292 : Invalid RAM location specified.Listing file:-Code:LOC OBJECT CODE LINE SOURCE TEXT00764 rr16 myvarWarning: Invalid RAM location specified.0117 0CA1 M rrf 4000+100764 is the line in the source code with the mistake. Or perhaps the real culprit is the #define? (can't always expect the compiler to find the actual error).If you keep the listing listing file open then it is updated automatically. It doesn't automatically jump to the error for you, but do you need that much hand-holding?QuoteThat's how I do it. Most of the PICs I use only have a few banks so it's fairly easy to remember which SFRs are in the same bank.My complaint isn't with BANKSEL, but the fact that you have to switch banks at all. Some newer chips have dozens of banks (most of which are half empty).

author: KL27x
author_rank: Super Contributor
timestamp: June 12, 2017, 03:08:39 am
content: @Bruce Abbot:Yeah, seems like we agree on banksel. I think japanhalt is the nonbeliever.Your last post has me all kinds of confused, though.First off, I could not reproduce that error. Your code assembles fine on my version of MPLAB.I assume "40000" represents SOME number to the IDE. I dont' even know what the default actually is, tbh. I always use "0x" or "." or "b'" etc. In my MPLAB (I suppose there could be options or differences between versions), there is no invalid number. If you exceed the max for the device's bank, it just wraps around. It doesn't even give me a warning when I turn off all warning suppression, even. The only warning I get is to make sure the proper bankbits are selected, because 4000 doesn't happen to wrap all the way back to bank0, apparently.You say you don't need a line number because it jumps you to the line.Here's is an example macro error that I just generated on my version of MPLAB:Code:#define myvar 4000lsl16 macro aaclrcrrl aa,frrl aa+1,fendm;test codelsl16 20hnopgoto $-1If I assemble that, it's fine. Now I add:Code:lsl16 myvar.TYPO.OOPSAnd I get an error when I assemble it. Because I misspelled "myvar," in case you did not catch that.Now, here's the rub.In my error report, I getError Z:\...\testcode.ASM 33 : Symbol not previously defined (MYVAR.OOPS.TYPO)Error Z:\...\testcode.ASM 34 : Symbol not previously defined (MYVAR.OOPS.TYPO)Those are theonly twoerrors generated. If I click on them, they shoot me over to where I wrote the macro. This tells me I used the macro wrong somewhere...... BUT WHERE?If I go into the .lst file, number 33 and 34 don't immediately mean anything to me. I finally find this:00031 LSL16 MACRO AA00032 CLRC00033 RLF AA,F00034 RLF AA+1,F00035 ENDMThis was much work, because it seems all I have is 2 digits from the error list which match up to ONE of many sets of said numbers in the "line source code" column. Maybe it's cuz I dont know how to read the lst file and need to looks for something else... but don't worry, it gets much worse! Because even after finding it... it is MY definition of the macro. Which it just another kick in the nads.I have to scan the entire lst file to finally find the error. And list files are huge. And finally after finding the needle in the haystack with zero help from the IDE, here it is, as shown in the lst file.Error : Symbol not previously defined (MYVAR.OOPS.TYPO)0087 0D80 M RLF MYVAR.OOPS.TYPO,FError : Symbol not previously defined (MYVAR.OOPS.TYPO)0088 0D81 M RLF MYVAR.OOPS.TYPO+1,FEven if the list is ONLY a few pages, this is still a PITA, to me. On a big project, the list file can be hundreds of pages. I don't call this hand-holding to want a bone, here, which there is so far absolutely none. The address given on the error report has NOTHING to do with where the error is. It only points to where I wrote the actual macro, itself.Now, once I have found the actual error by physically scanning the entire LST file, it shows line number 87 in the code. So I go back to my code and enter this line number... and it takes me... somewhere in the vicinity but not even to the correct line. I have to scan about 10 lines back to find it (depending on how many macros you used, it could be a lot more dead space to scroll back... pages and pages.) This is AFTER scanningthe entirelst file just to find this line number.If I wrote:rrl typoooooooo,for maybe..call subroutine.typoooooooooooor even if I have to go through all the trouble to write this:lcall subroutine.typooooooopagesel $banksel xyzand I try to assemble it, the error will link me right to the spot. It takes 5 seconds to recognize and fix my typo and hit "build." Clicky, typy, f10. Compare that to the game of "where's Waldo?" Now that I am going thru these motions, again, I recall doing these same tests, back when, after finally having enough and ripping out all my macros and wondering if perhaps I was just doing something wrong. I remember specifically being annoyed with this aspect of turning a minor typo into a major hassle. This why I have to wonder if there are settings on the IDE which you have used. I can't imagine never running into this exact thing and exercising the vocal cords with familiar 4 letter words. Now IF the error was in the last file you edited, you can cntrl + Z till something clicks.. make a mental note... cnrl+Y to put everything back. Then go back and fix the error. Or you can look thru the entire list file. You can dump all the work you just did and go back to an earlier revision. Or you can just take the macro out of the code in every instance where you used it, and never go thru this, again.Also, now that I am thinking about it, again, the problem with macros using no parameters is that if you misspell the macro, you will create a label. There is no error. No color change in the text. Just an invisible error that your assembler can't find. You can use the indented label warning, I would think. Or you could use the IDE's jump to  and scan for your typo, assuming you didn't mess up the first couple of letters... But again, it's been years since I investigated this stuff, so whatever solutions I found, they were not satisfactory to me. I really rather have error than to have to keep my code free of all such warnings to even have a chance to notice this --- in fact indented labels are a tool I use, frequently, to make bookmarks. This is why peudoinstructions and assembler directives are fine to me but macros are to be avoided, wherever possible. (I concede there are instances where macros are worth the trouble; but you always have to be careful.)Blue is good. Purple is bad.I have come up with some truly inelegant subroutines to replace nice macros. Thinking more about it, it should be possible to use macros IN subroutines. Once that tests good, you are fairly solid as long as you are calling the subroutine in your future code. The assembler/IDE is the smart one in my partnership. With my bestest thinking cap on, all I do is create and fix errors. I'm not gonna go off the reservation, alone, unless I have to. I can see how macros are the best thing since sliced bread when your code is fairly short and legible. Short and legible do not describe assembly for long. Even modestly complex code will soon turn into a strand of DNA which is navigable only with the help of the IDE.

author: KL27x
author_rank: Super Contributor
timestamp: June 12, 2017, 04:08:51 am
content: Ok, I thought to try something.Config>settings>project>uncheck the "halt on first failure" box.No dice. The only two errors point to where I wrote the macro, still.QuoteOnce you give up on neat and legible, these enhanced midrange are great for assembly. Yes, you give up a lot in terms of elegance. This is the point where if you want to use fancy peripherals and super low power and more memory and EEPROM and PLL with 48MHz and multiple interrupts, you will start letting go of neat and elegant and legible, and you will focus more on making sure the IDE has your back. Cuz it IS going to get fairly messy no matter what you do. I hide whatever I can in subroutines so that the main code loop is manageable. These are my messy suitcases that once closed and tested and verified, I don't have to be too careful when using them, and I don't need to look, again, at what's inside. This is what the huge stack is for. So your subroutines can call other subroutines and branch to other subroutines ad infinitum. Now all you have to do is follow the bank and page protocol when using your subroutines and keep your memory from co-mingling. That sounds easier than it is (cuz I spend 99% of my time writing and modifying subroutines.)

author: KL27x
author_rank: Super Contributor
timestamp: June 12, 2017, 08:22:25 am
content: Some basic obvious subroutines, I'm sure everyone has copied right out of the datasheet, for example:Code:read; loads W with EEPROM @ address loaded into addypointer; global memory: addypointer; returns in bank0;;;;;;;;;;;;;;;;;;;;;;;;;;;BANKSEL EEADRLmovfaddypointer,wmovwfEEADRLBANKSEL EECON1BCFEECON1,CFGSBCFEECON1,EEPGDBSFEECON1,RDBANKSEL EEDATmovfEEDAT,0BANKSEL 20hincfaddypointer,freturnsave; saves W to EEPROM at address indicated in addypointer; global memory: addypointer, temp; returns in bank0; turns GIE off and BACK ONmovwftempBANKSEL EEADRLmovfaddypointer,wmovwfEEADRLmovftemp,wmovwfEEDATLBCFEECON1,CFGSBCFEECON1,EEPGDBSFEECON1,WRENBCFINTCON,GIEmovlw55hmovwfEECON2movlw0AAhmovwfEECON2bsfEECON1,WRBSFINTCON,GIEbcfEECON1,WRENbtfscEECON1,WRgoto$-2banksel 0hincfaddypointer,fmovftemp,wreturnNow in code, I can write new subroutines like soCode:save_stuff;memory: register1, register2; save subroutine;constant: desired.address is EEPROM starting address, 2 bytes;returns in bank0movlwdesired.addressmovwfaddypointer ;global nonlinear registerbankselregister1movfregister1,wlcallsavebankselregister2movfregister2,wcallsavepagesel $returnload_stuff;memory: register1, register2; read subroutine; constant: desired.address;return in register1 bankmovlwdesired.addressmovwfaddypointerlcallreadbanksel register1movwfregister1callreadbanksel register2movwfregister2pagesel $returnThis is not elegant. But in my code, I am just calling and using any of these subroutines from the main code loop with just their name. I can keep "load_stuff" on the local page where it is used, and "read" and "save" can be anywhere.Here is a part of a delay module with several selectable software interrupts:Code:Delay.Module.Subroutinesclearflagsclrf DM.INTFLAG.REGbcfDM.STATUS,1 ;Delay.IF.MasterDM.returnRETURNWx100mScallclearflagsmovwfDM.tempreg2Wx100mS.1btfssDelay.IE.MastergotoWx100mS.2btfscDelay.IF.MasterreturnWx100mS.2movlw.39movwfDM.tempreg1movlw.16movwfDM.tempreg0callDelay_10uS_PLdecfszDM.tempreg2,fgotoWx100mS.1returnWx10mScallclearflagsmovwfDM.tempreg2Wx10mS.1btfssDelay.IE.MastergotoWx10mS.2btfscDelay.IF.MasterreturnWx10mS.2movlw.3movwfDM.tempreg1movlw.232movwfDM.tempreg0callDelay_10uS_PLdecfszDM.tempreg2,fgotoWx10mS.1returnWx1mScallclearflagsMOVWFDM.tempreg2Wx1mS.1btfssDelay.IE.MastergotoWx1mS.2btfscDelay.IF.MasterreturnWx1mS.2movlw.100movwfDM.tempreg0clrfDM.tempreg1callDelay_10uS_PLdecfszDM.tempreg2,fgotoWx1mS.1return;if REV!=0xF1Wx10uScallclearflagsmovwfDM.tempreg0clrfDM.tempreg1gotoDelay_10uS_PLTo use it, I would first have to set the IE bits I want active, then clear the flags, thenmovlw .6call Wx100mSmovlw .4call Wx10mSThis is 640mS delay with whatever software interrupts I enabled. Not as elegant as a macro, but this the price I pay. If a flag is set, the delays exit immediately, and a master IF is set. If this bit is set, I can further check which individual flags were set, if it matters. Basically same way an IOC interrupt works.Here is a snippet of an I2C subroutine "man in the middle" thing I made, which for to hack a device using an I2C port extender and to intercept and modify the data, bouncing between slave and master.Code:I2C.ISR.Subroutine;called at end of TMR4 interrupt... there is also an i2c isr called after iocbanksel bank4btfscSlave.Busy.BitreturnlCALLCHANGE_TO_MASTERlCALLRead.inputslCALLConvert.P.to.Output.FlCALLCHANGE_TO_SLAVEpagesel $returnThere's like no code there, at all. The subroutine just calls other subroutine. Each bit forged with pain until it frigging worked. What does the code look like? Well, it's greek even to me, and I wrote it. It don't make any sense unless I have the datasheet and I2C protocol in front of me. The labels are detailed, and there is tons of comment. And I have a specific format I generally use (some of my indentations/tabs are messed up in my snippets, which sucks - example I always put lcalls/pagesels with 1 fewer indent than the other instructions to make them stand out, but that didn't show up in my copy/paste). But even so, it's long, it's cryptic, and suffice to say, it gets its own page of code and bank of memory for itself. Funny, looking at it, I used Lcall so many times. I am pretty sure I ended up putting everything on one page, but I prolly wrote it like that just in case. Well, the memory was consolidated to one bank, except I put the virtual TRIS and LAT registers on the same bank as the other PIC ports'. But once one part is working, pack it up, tape it over, and don't mess with it. Eat the elephant one bite at a time.Here's a little tidbit. This part is so I can read the port extender and write to the spoofed master exactly the way I can to any port of the PIC, using virtual ports P0 and P1 and F0, F1, with their own virtual PORT, LAT and TRIS registers. This how I really came to appreciate what the LAT register does, lol. This was more fun than a root canal. Slightly.Code:banksel PORTP0movfPORTP0,wbanksel bank4movwfF00movwfP0.tru.input.statebanksel TRISP0;movlw 0xFF;movwfTRISP0movfTRISP0,wbankselLATP0iorwfLATP0,wbanksel bank4movwfMask.Lbanksel TRISP0comfTRISP0,wbanksel LATP0andwfLATP0,wbanksel bank4;hi mask is in wiorwfF00,fmovfMask.L,wandwfF00,f8 bit math:Wx5;global memory: temp, temp1;does not alter bankmovwf tempmovwf temp1clrcrlf temp,frlf temp,waddwf temp1,wreturnload w reg with value, call Wx5 or call Wx3, or whatnot. Saves a lot of codespace if you call it a lot. Often I need some low value multiplication before a BRW instruction where I need 2 or 3 or 4 lines per each branch, or whatnot. (4 lines for a pagesel X, call X, pagesel $, return, for instance. Seeing a pattern here?)

author: jpanhalt
author_rank: Super Contributor
timestamp: June 12, 2017, 10:09:29 am
content: Quote from: KL27x on June 12, 2017, 08:22:25 amUsing the less elegant, less neat, and less legible enhanced mid-range instructions becomes:Code: (asm)X5enhancedmovf temp,wlslf temp,flslf temp,faddwf temp,wreturn;NB: If for whatever reason you want to preserve "temp" it is trivial;to rewrite the above doing the rotates on w instead of temp.;Second, should the seed value not give a x5 value that is 8-bit, the;second method will still give the correct lower byte, but your;method will not. Try d.140 as the seed.BTW, just what is less elegant, less neat, and less legible about the enhanced mid-range chips? For example, I find being able directly manipulate wreg and address the stack as (e.g., use it as a memory stack) quite useful and code saving. The enhanced instruction set is also useful as illustrated above.John

author: KL27x
author_rank: Super Contributor
timestamp: June 12, 2017, 11:22:51 am
content: ^I thought there was an instruction that did the whole shift without the carry. I guess I didn't warm up to the spelling of that. Took me long enough to remember rlf rrf.The initial reason I ever made this subroutine is that after returning from another subroutine (hehe, there's the theme) which loads a value into w, I can call Wx5 and BRW from there in 5 line spacing. I know my subroutine doesn't work for numbers that exceed 256. But in either case you're clipping the higher order bit. So what if your example if off by 256 and mine is off by only 255, sometimes. Mine is more accurate, lol. And since mine loads W you saved only 1 instruction (and the one register, which I could also save, if I had thought about it. In fact, I couldn't find my actual code and just wrote this on the spot, which is why this isn't copypasta'd. I wonder when I do find it if I used two temporary registers or just one? I felt like it was too bulky when I wrote it...)QuoteAs Bruce Abbott has stated his dislike for the banks. I see his point of view. The fact enhance chip might have 4x the code space, you might end up getting only twice as much actual processing done in that space, and also going thru a lot of teething pains managing the extra pages you end up with due to all the bank management, let alone the extra banks. But this is offset somewhat when you make use of that stack. Subroutines don't have to be pretty. And once you get over that, the bank management doesn't need to be hard. You can write your actual code loop with very little of it when you let the subroutines handle it.This bulky subroutine kinda illustrates my point. I'm not trying to squeeze out every instruction unless it matters. Take my tris/lat/port emulator code for instance... speed mattered, there. I think that if you understand what it is doing, it might actually be very miserly on the instruction cycles, maybe even perfect (highly doubtful, but I did wrack my brain on it). But unless it matters, I am not much caring. When it works, I'm moving on.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 12, 2017, 11:57:53 am
content: Quote from: KL27x on June 12, 2017, 11:22:51 amSo somehow, you perceive using the many useful peripherals, which require bank changes, as being less elegant and less legible than doing everything in code.I disagree and except for code I wrote years ago, I go to the enhanced chips as my first choice. I do keep two "cheat" sheets available when I write code -- the instruction set and the memory pages showing banks for what I am doing. Neither is inconvenient nor reflected in the code, except I may be reminded of an instruction such assubwfbandaddwfcthat I might have forgotten since my last exercise in coding. NB: I code mostly just during Winter when I am not tending to the farm and do it only as a hobby.One thing I try to do religiously is keep track of the bank using columns 76-80 on the page, e.g.,Code: (asm);*******************************************************************************;Setup SPI serial port;*******************************************************************************movlb 4 ; |B4clrf WPUC ;disable WPU(default is enabled) |B4clrf SSPSTAT ;CKE=0, SMP=0 (sample at middle) |B4;NB: CKE (bit6) set opposite CPHA, i.e., 10 = mode 11(mode 3)movlw b'00110001' ;enable SSPEN, set SPI CLK to Fosc/16 |B4movwf SSPCON1 ;CKP=1(idle high),master clk =Fosc/x |B4movlb 2 ; |B2bsf CSn ;set chip select high |B2movlb 0 ; |B0;*******************************************************************************John

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: June 12, 2017, 01:53:37 pm
content: Quote from: KL27x on June 12, 2017, 03:08:39 amIt was 4000 not 40000, and I have the default number base set to decimal so that is address 0x0FA0. OnmyPIC (12F617) that is an invalid address.QuoteThis is somewhat disturbing, as it allows you to make serious errors that won't be picked up. But being able to do stuff like that is one of the charms of assembler (actually you can do it in C too, but it's harder to do accidentally). With great power comes great responsibility, and assembler gives you more power than any HLL.QuoteHere's what I got after searching for "Symbol not previously defined (myvar.TYPO.OOPS)" in the listing file:-Code:00016 lsl16 myvar.TYPO.OOPS0003 1003 M clrcError : Symbol not previously defined (myvar.TYPO.OOPS)0004 0D80 M rlf myvar.TYPO.OOPS,fError : Symbol not previously defined (myvar.TYPO.OOPS)0005 0D81 M rlf myvar.TYPO.OOPS+1,fLines with "M" are the macro expansion. Directly above them you see "00016 lsl16 myvar.TYPO.OOPS".Click-drag mouse over error message in output window, right-click/copy or type CTRL-C, click in listing file window, type CTRL-F, paste error message, click 'find next'. Not that hard.Of course in this case you didn't need to do that. Just search for "myvar.TYPO.OOPS" in the source code.QuotePeople today have it so easy! And patience to match.I do most of my coding with a text editor, and having the error message link back to the source line is a novelty. The project I am currently working on has over 10,000 lines of Z80 asm code spread over a dozen files. You make one little typo and the assembler throws hundreds of unrelated error messages. Searching the listing file is often the only practical way to find stuff.QuoteAs far as the assembler is concerned it's not an error, and why should it be? Perhaps youwanteda label with that name, and the assembler can't be expected to know otherwise.QuoteYes, that's a good idea. I always put labels at column 0 and terminate them with a ':', but sadly the assembler does not require this.QuoteYuk. How does that work?QuoteAssembly code gets long fast, but it doesn't have to be illegible.I use macros not just to reduce typing but also to make the code more readable. A well designed macro hides all the messy implementation details and stops you making silly errors (and the less typing you have to do, the fewer typos you make!). I also use macros to create data structures. On other platforms I create GUI elements with them (no crappy form designers for me!).

author: KL27x
author_rank: Super Contributor
timestamp: June 12, 2017, 08:40:28 pm
content: QuoteWow, thanks. Cntrl-F. I did not know this existed. And yes, I am able to find error both ways. (But in the latter method, you must know in which asm file to look.) Thanks a bunch... I get it, now. I have never seen "find file" in the gui, and I didn't know it existed.Now I am seeing it is a thing in basically any text editor..Yes, we have it good when we RTFM.QuoteThe only time I have ever had to look at the lst file is when I first started managing pages and using BRW incorrectly, to debug. You may not believe this, but even on project with multiple files and hundred plus pages, when I have errors, I look at the error report, I click, I find. It's not a guessing game, no matter how big the project is. The error report will link me there. The first error is essentially always going to take me to the source of an error, because I wrote my code in a way where it works this way. I've clung to this like a lifeline, lol. (Maybe I can start letting go, now that I am learning new tricks).QuoteYes, it's not an "error," it's technically a bug. Perhaps I wanted a label? No, I am not going to make a label almost exactly same as a macro name. Perhaps I made a typo, and now we wait until I finally realize I even made thiserrormistake. This is something I do not want to introduce to my code, because I see the grey hairs in advance.QuoteIt works by writing yourlabelAnd now, I get a warning everytime I assemble. Which shows "yourlabel" in the warning.Unlike the bookmark system you can select which bookmark by title, rather than next/previous. And more importantly, unlike the goto  (or find, thank you) in IDE, this works across all of your asm files. You don't have to click on the right file. just click on it in the error report and it opens the correct asm file and brings you to your bookmark. When you're done with it, click on it to jump there, and erase it. This is very useful when modifying multiple asm files. It's also impossible to accidentally erase or lose all your bookmarks, as with the IDE's bookmark function. Even going back to earlier revisions, your bookmarks from that time are preserved, which more the clues to what I was doing and thinking, the better.QuoteI think we are both in the same camp, here. I also would also prefer to use enhanced before any midrange or baseline, in general. The bank management is just part of the deal which becomes second nature. Going back to baseline, it's actually more work for me to streamline my code to take advantage of tris instruction and ability to do so much on one bank. Writing good code on baseline feels like making bad habits for midrange.QuoteI'm learning a lot, fellas. Keep it coming. I'd love to see some examples of this data structure macro.