You get the same behaviour on real hardware when you load the object file to the start of BASIC RAM (with ",8") and then (try to) RUN it. From the view of the BASIC interpreter, it is an invalid BASIC program. Some parts of it are interpreted as line number - the byte sequence $8D $0F, which is part of the STA $900F instruction, gets interpreted as $0F8D = 3981.
Some time ago, the standard autostart behaviour of VICE was changed from loading with ",8,1" ("absolute", to the original address once saved from) to ",8" ("relative", to the BASIC RAM start). That is the main reason your example does not work.
When you load the object file with ",8,1" and start the execution of the included machine code with
SYS 4352, you should get the expected result: storing $28 into VIC register $900F results in a red background with black exterior border and non-inverted, i.e. normal characters. You should follow up the load procedure with NEW to correct some pointers of the BASIC interpreter.
As Marco already wrote, one method to 'harden' machine code programs against these issues is to pre-face them with a
BASIC stub that includes the SYS instruction necessary to start the machine code. This way, one also does not need to remember that start address, and can simply RUN the loaded program. You find more details in the thread "
How to compile assembly with a basic start line?".