16 bit math on PIC

I’ve been racking my brain on this for hours and I’m starting to get a headache.

I have a 12F683. My program (written in assembly) has gotten to the point where there is a 16 bit number somewhere. One byte is the upper 8 bits and the other byte is the lower 8 bits. Now the problem is that I need to do some math with this number. The first step is to subtract 2000 (decimal) from it. If the number is less than 2000, it either needs to leave it at 0 or I need some way of determining that it has become negative. If I can get that part, I should be able to figure out the rest on my own.

I thought of simply subtracting 7 from the upper byte then subtracting 215 from the lower byte but that presents all sorts of carry issues if the lower byte is less than 215. It seems that many of the mathematical instructions on the PIC are designed for signed bytes. This is trouble for the lower byte that needs to remain unsigned.

I tried Google, but there doesn’t seem to be much info on doing multibyte math.

its not as hard as you have psyched yourself up for.

You subtract the lower byte first, and check the carry flag. If there is a carry you subtract one from the upper byte, then subtract the upper bytes. If the number was less than 2000 the upperbyte will have the msb set (negative) when you are done.

I dont remember the instruction set off the top of my head, but many µC have ‘subtract with carry’ instructions, that take care of the carry for you.

Basically, the trick here is to think way back to when you first learned to add and subtract multi-digit numbers on paper. Then just apply the same methods here. Subtract the least significant bytes of each number first, and if the carry bit is set after the subtraction then that means you have to “borrow” from the more-significant byte by decrementing it and then performing the subtraction on those upper bytes.

This same method applies to addition, and as you can tell it’s easy to build up routines that will add or subtract numbers of any number of bytes.

(edit) Guess Ken beat me to it :slight_smile:

Alright, thanks guys, I’m surprised I didn’t figure that out.

But I still have a problem

Let’s say that I want to do the following:

00000010 01000001

  • 00000001 01001010

00000011 10001011

The lower byte of the result takes a full 8 bits. However, according to the datasheet, the addwf instruction that I would use only goes up to 127 (7 bits which I presume is to keep it signed with the 8th bit). How would this affect what I want to do?

I guess a more general question on this topic is what happens if I perform an arthithmatic operation and end up with something larger than 127. addwf, subwf, and a few other instructions only work up to 127 (according to the datasheet). If I get 128 does it overflow and give me garbage (as I would expect from code in a high level language) or does it just overflow into the MSB and behave nicely?

I’m not overly familiar with PIC assembly, but from looking at the datasheet I think I understand the problem. I’m guessing that you’re looking at the part of the datasheet that says “Operands: 0 <= f <= 127”, right? I believe all this is saying is that f (which is specifying a register number) can’t be greater than 127, since that PIC only has 128 registers. The contents of the register can be larger than that, however.

The PIC is sort of different than the x86, theres only one working register in the sense ax, bx, etc of the x86 (that you can use as a location to put data you will preform operations on), “w”. f is either a special function register (io bits, status bits, or control bits) or a general function register (RAM), which is limited to 7 bits, per bank. You change the bank by switching a status bit. (RP0 and RP1 on a 16F87x IIRC).

I’m not sure if this is what you are reading, but f is limited to 7 bits, as each bank goes to 7F.

edit:
switched rb0 to rp0 (the correct bitname in the status register)

This doesn’t change my point at all (the only difference that you pointed out is that there’s 2 banks of 128 registers not just one). f still represents a register, not the contents of that register, and hence the reason that the datasheet says that f must be between 0 and 127.

I think that the contents are limited to 127. If you look at some of the other instructions, they say 0<f<255. I’m pretty sure it has something to do with signed vs unsigned contents, but I don’t know what happens if the instruction causes it to overflow. Various websites mention this 127 limit when it comes to the incf/decf type instructions (ie, if you increment 127, it rolls over to 0). But nobody talks about what happens with the arithmatic operations.

I don’t see any that say 0 < f < 255, however I see some that say 0 <= k <= 255. In that case, though, k is an immediate value, not a register number, so it makes sense that a full 8 bit value would be allowed.

I’m pretty sure it has something to do with signed vs unsigned contents, but I don’t know what happens if the instruction causes it to overflow.
The processor doesn’t care whether or not the number is signed. That’s the beauty of two’s complement notation - you can interpret a number as signed or unsigned, and it doesn’t matter to the processor. Addition and subtraction “just work” whether it’s signed or not.

Ah, thank you. I’ve been looking at that thing on and off for over a year now and I never noticed that. But it is strange that some websites say that incf will overflow at 127. Maybe they’re thinking the same thing I was…

I couldn’t find an example of a site like this, do you have a link? I’m wondering if they meant that it would overflow in the sense that if you’re treating the number as signed, then adding 1 to 127 gives 10000000 in binary which in two’s complement is -128?

http://www.mstracey.btinternet.co.uk/pictutorial/progtut8.htm

Toward the bottom of the page, under the “Increment” heading:

This carries on until 0C equals 127. This time, when we increment 0C by 1, 0C will now equal 0. Our INCFSZ instruction will then tell the PIC to skip the next instruction, which in this case is the goto statement, and so the PIC will continue with the rest of the program.

Copied from http://www.mstracey.btinternet.co.uk/pictutorial/progtut8.htm
This carries on until 0C equals 127. This time, when we increment 0C by 1, 0C will now equal 0. Our INCFSZ instruction will then tell the PIC to skip the next instruction, which in this case is the goto statement, and so the PIC will continue with the rest of the program.

I believe that is simply an error in the tutorial. Other references describe the INCFSZ instruction more reasonably, saying that incrementing 255 yields zero. (The tutorial’s example of usage is rather odd as well; apparently the author thinks it’s a good idea to waste time incrementing a file/register until it overflows, rather than quickly setting it to zero. In my experience, “skip on zero” instructions are more often used to escape a loop, rather than to make a minimally short one.)

this is one of the problems with the internet as a source of information

no editors checking your work

no accountablility if you post mistakes on your website

when in doubt refer to the Microchip data sheets, and if something weird is happening, check the errata data sheet for the part you are using. Some micros do have bugs, so some instructions dont work as originally intended.

But I dont remember any 7 vs 8 bit math bugs on an PIC devices.

Well, that’s the thing, I questioned it enough to think that it was strange, but it seemed plausible enough for me to just accept it. I looked at the datasheet, (mis)read at it as saying that it will only increment up to 127, googled it, and found something to back up the strange claim. That was the only guy that actually gave a solid number as to when it overflows.

As a side not to that, the 12F683 is an 8 pin device with 6 I/Os. 3 of I/Os are multipurpose. If you use the internal clock, you can configure the two oscillator pins to be I/O. And if you don’t need a reset pin, you configure that to be an I/O also. In the process of doing that, it occured to me that the chip would start running the program and disable the reset as soon as it’s plugged into my JDM programmer. But, in order to put it into program mode, you need to do something with the reset pin (I’m fuzzy on the details though). It crossed my mind that it might not be reprogramable under those conditions, but I didn’t pay much attention to it. I figured that the protocol would be smart enough to get around that. Why would they make it impossible to reprogram if I didn’t set the code protect bits? Well, apparently, the protocol was changed to occomidate these new style chips. The JDM programmer, however, was designed before this change occured. With that said, I now have 2 chips that can’t be reprogrammed with the current incarnation of my programmer. The issue was big enough for me to think that it was strange, but it wasn’t so screwed up that I thought it would be a problem.

Now, if you’ll excuse me, I think I have some 2N7000’s in the garage somewhere to fix my programmmer.

I think for the 12F, the code could be done as follows. There isn’t a ADDWFC (add w/carry) as there is on the 18F so I suppose you just test the Carry bit in the status reg directly?). Anyway, I might have flipped the value of the carry bit but I believe it is asserted to 1 upon carry out and asserted to 0 for a borrow - but I might have them mixed up so make sure you test this before using it. Just one way, I’m sure there are lots of others.


Where a is at address 0x20/0x21
      b               0x22/0x23
      c               0x24/0x25

:                   c = a+b;
     MOVF   0x20, W           ; a.lo              -> W
     ADDWF  0x22, W           ; W + b.lo          -> W, set carry bit status
     MOVWF  0x24              ;                      W -> c.lo
     MOVF   0x21, W           ; a.hi              -> W
     BTFSC  STATUS,C          ; test carry bit in STATUS[0x03].<0>
     ADDLW  1,    W           ; W + Carry in bit  -> W
     ADDWF  0x23, W           ; W + b.hi          -> W
     MOVWF  0x25              ;                      W -> c.hi

:                  c = a-b;
     MOVF   0x20, W           ; a.lo              -> W
     SUBWF  0x22, W           ; W - b.lo          -> W, set borrow bit status
     MOVWF  0x24              ;                      W -> c.lo
     MOVF   0x21, W           ; a.hi              -> W
     BTFSS  STATUS,C          ; test carry bit in STATUS[0x03].<0>
     SUBLW  1,    W           ; W - Borrow        -> W
     SUBWF  0x23, W           ; b.hi - W          -> W
     MOVWF  0x25              ;                      W -> c.hi

you are right, if you use the reset and both programmer pins on the chip as outputs, then the device will take off running when its programmed, and the programmer wont be able to gain control of it again.

Making the two programmer pins inputs solves the problem. I did the same thing - check the programming data sheet - I think its only a problem if the SW drives one or both programming pins high (or low? I forget).

As a note to anyone here who might try that modification, I ran into some trouble with it. After spending hours checking and rechecking the hardware, it turned out to be a software issue. (It’s always the other one…) I had to increase the “clock delay”, as PikDev calls it. In IC-Prog it’s called “I/O delay”. I haven’t experimented yet to find the minimum setting, but my arbitrary selection of 25 in IC-Prog worked.