EDIT: The problem is my op-code catch is op >= 0x20 || op <= 0x2F
rather than op >= 0x20 && op <= 0x2F
and so most of the switch-case was being optimised away!
This is a really difficult question to ask, because I cannot make a minimal-verifiable version of it as its own sketch.
For fun, I'm writing an ATtiny85 byte-code interpreter. It reads op codes from EEPROM and executes them. Keeping my eye on the program size, I've been completely stumped by what's happening in the following.
I really must stress though - I could not recreate this in a minimal sketch :(
//_mem[] is a uint8_t[] for RAM allocation
//_pMem is a moveable pointer to _mem[]
//NextByte() retreives the next byte from EEPROM
//Before this is if-else statements catching ranges of similar op codes
...
//This one catches similar math functions (mem op= num1):
} else if (op >= 0x20 || op <= 0x2F) {
uint8_t num1 = _mem[NextByte()];
if (op == 0x20) *_pMem += num1; //1A
else if (op == 0x21) *_pMem -= num1; //1S
//For all other op codes:
} else {
//These can mostly be ignored
switch (op) {
case 0x00: J(*_pMem); break; //J_
case 0x01: _o = NextWord(); break; //JA
case 0x0A: ++_pMem; break; //MI
case 0x0B: --_pMem; break; //MD
case 0x0C: _pMem += NextByte(); break; //MA
case 0x0D: _pMem -= NextByte(); break; //MS
case 0x10: *_pMem = NextByte(); break; //S_
case 0x11: _mem[NextByte()] = _mem[NextByte()]; break; //CP
case 0x12: *_pMem = _mem[NextByte()]; break; //V_
case 0x13: _mem[NextByte()] = *_pMem; break; //_V
case 0x80: SV(); break; //SV
case 0xFF: noHalt = false; break; //HT
}
The program in total, when in this configuration, is 2030 bytes.
However, when I instead put just the two op codes, 0x20 and 0x21, into the switch-case, the program balloons to 2680 bytes! Code:
//the if-else statement for 0x20 and 0x21 is completely removed
...
case 0x13: _mem[NextByte()] = *_pMem; break; //_V
case 0x20: *_pMem += _mem[NextByte()]; break; //1A
case 0x21: *_pMem -= _mem[NextByte()]; break; //1S
case 0x80: SV(); break; //SV
...
Immediately, I thought "it must be something to do with replacing num1
with _mem[NextByte()]
each time", but when I tried the same thing in the if-else configuration, it was only 2044 bytes.
Obviously there's waaay more code besides this out-of-context slice, and I'd be happy to share the whole sketch, but why does it even matter? I cannot imagine why there is a 600+ byte jump, other than maybe the logic behind the switch()
. Hence I'm asking!
It's not as if it's suddenly using floating point or any other gotcha...
While trying to make a minimal sketch, the if-else and switch statement configurations were only several bytes different in size, even while propping up various parts with unoptimisable code.
Thank you for all your time.
else
- it's the 2nd one to use a range match.break
in cases of theswitch
?op >= 0x20 || op <= 0x2F
rather thanop >= 0x20 && op <= 0x2F
, so the whole switch-case was being optimised away.