You are creating a pointer variable, not a normal variable.
That pointer variable is, until told otherwise, pointing at address 0x00. It covers 4 bytes.
Addresses 0x00 to 0x1F are the internal CPU registers R0 to R31. Your pointer variable points to R0 plus three more addresses above it (a long is 4 bytes in total).
So when you write to your pointer you are directly writing to CPU registers R0, R1, R2 and R3.
According to the AVR-GCC ABI:
- R0 is the scratch register
- R1 is the zero register
- R2 & R3 are general purpose (call-saved) registers.
So if you write 255 to your pointer variable you are doing:
R0 = 255
R1 = 0
R2 = 0
R3 = 0
And that's no big deal: some things may go a little awry, but nothing major.
However, writing 256 results in:
R0 = 0
R1 = 1
R2 = 0
R3 = 0
The big thing here is that the zero register R1 is now 1, not zero. The ABI states:
R1 always contains zero. During an insn the content might be destroyed, e.g. by a MUL instruction that uses R0/R1 as implicit output register. If an insn destroys R1, the insn must restore R1 to zero afterwards. This register must be saved in ISR prologues and must then be set to zero because R1 might contain values other than zero. The ISR epilogue restores the value. In inline assembler you can use __zero_reg__
for the zero register.
So R1 must be zero except for a brief moment when you use it for something else. Since you're setting it to 1 and leaving it as one, any other operations that then rely on that register being zero will have a hard time knowing just what they are doing.
Having a "zero" register is a common thing. There are many times (especially in a RISC architecture) when you want the number zero - and having a register that is guaranteed to always be zero saves a lot of time. For instance, in MIPS the register $0 is hard-wired to be zero and can never change. AVR doesn't have that luxury, but the GCC compiler imposes the rule for R1 which you have to abide by - since there are huge amounts of library code and other auto-generated code that rely on that register being zero.
So when it's set to one, all gloves are off, and no one can ever really know just what is happening - you'd have to disassemble the code (or get an intermediate listing out of the compiler) to see just what is using R1, and work out what the effect of that being 1 instead of 0 would be.
As an example, digitalWrite
has this:
if (val == LOW) {
370: 11 11 cpse r17, r1
372: 05 c0 rjmp .+10 ; 0x37e <digitalWrite+0x50>
That's "COMPARE SKIP IF EQUAL". It's looking at the value you pass (val
) to see if it's equal to zero - by using R1 as the zero value. But if R1 isn't zero then it will never know that what you passed as LOW is LOW - instead it would find HIGH as LOW, since HIGH is 1 and R1 is 1. So you've basically swapped HIGH and LOW over as far as digitalWrite
is concerned.
There are also places where the number 255 is wanted. This is easy to generate with "eor r1,r1" - exclusive-or of the zero register. EOR turns bits off that are on and on that are off - so zero becomes 255. Except if zero is actually 1, you end up with 254 not 255, and more strange things happen.