Turning one bit on
Hardware registers pack unrelated switches into one halfword. DISPCNT holds the video mode, which backgrounds are on, whether sprites are enabled and half a dozen other flags, so code that wants to change one of them has to leave the rest alone. That means a read, an or, and a write back.
Here is a function that sets three interrupt-request bits in DISPSTAT:
0 ldr r0, [pc, #8] (->12)
2 ldrh r1, [r0, #0]
4 mov r2, #56
6 orr r1, r2
8 strh r1, [r0, #0]
10 bx lr
12 .word 67108868
Five instructions in a fixed shape. The address is loaded once into r0 and stays there across the whole sequence, since the load and the store need it. r1 holds the value that came back from the hardware. 56 is 0x38, the three bits being set, and it needs a register of its own — Thumb's orr takes two registers and no immediate, so there is no way to write "or in 0x38" directly. Every |= on this machine costs an extra instruction to materialise the mask.
A mask over 255 costs two instructions, plus the copy you met when building a stored value: gcc assembles the constant into a scratch register with mov/lsl and then moves it into the register the orr wants. All three land in the object file.
One honest caveat. A single read-modify-write like this compiles the same with or without volatile, because the source performs one read and one write either way and there is nothing for the optimiser to merge. This shape does not prove the qualifier was there; the double load from the previous lesson does.
Your target sets a bit too high to name with a single mov, and it addresses a register the compiler can build without the pool.
Your task
Write func_08367e08 to reproduce the target assembly.