lwc1 takes a %lo too
A float global uses the exact same address machinery — the only change is the final instruction: lwc1, the FPU load you met with the third float argument, carries the %lo half and delivers the value directly into a float register. Here's getSpeed, which returns the global f32 gSpeed:
lui at, %hi(gSpeed)
lwc1 fv0, %lo(gSpeed)(at) # load the float straight into the return reg
jr ra
nop
The address half stays on the integer side — lui into at, because addresses are integers no matter what they point at. Only the data crosses to the FPU. So a float global read is a little hybrid: integer register for the address, float register for the value. (Stores mirror it: swc1 with a %lo, when you meet one.)
The target loads a float global and does one .s operation against the argument before returning. Operand order decides the meaning — check which side the freshly loaded global sits on.
Your task
extern f32 gDrift; is declared for you. Write func_80043638 to reproduce the target assembly.