Я не видел объявления P, но когда вы говорите, что это байтовый массив, я предполагаю, что конкретный тип - беззнаковый символ.
Если бы оно было подписано, приведение могло бы вести себя так, как вы хотели, потому что, например, символ -1 стал бы -1 int, бит знака сдвигался, а остальные биты инвертировались, как требовалось. Но когда вы преобразуете неподписанный символ в подписанный int, результат всегда будет положительным.
Итак, чтобы решить проблему, один из способов - привести указатель / массив перед разыменованием, и он даст желаемый результат:
int o1 = (int)(((char *)P)[pc+1]);
Еще одна вещь: сдвиг бита не будет хорошо работать со знаковыми значениями, потому что он просто сместит знаковый бит. Это будет работать в вашем примере, потому что у вас есть 0xFF, но если бы у вас было 0x80, это было бы 0x80000000 как int, и стало бы 0x00000000 после сдвига.
Итак, вместо 8-битного сдвига, сделайте умножение:
int offset =(int)( (o1 * 256) + o2 );