without any context we can only guess...
jp (hl) was commonly used for:
jump to pointer
this one is obvious
hl points to a memory location that you want to jump to. You can do many things with this like for example you want to decide how to handle some input and you got more choices of handlers so you evaluate which to use in
hl and at the end just jump to it. The same goes for
mode of a program... For example you got more ways of rendering text or something and you want easily switch between them for the whole program ... then simply store the selected routine in some pointer and when needed jump to it...
I do not have any context to your code but this is the most likely scenario:
286Eh is the selected mode handler,
hl read that position using
ix and lastly routine jump to it. I am guessing the final
ret is in the routine somewhere.
call/return to/from subroutine with non standard calling convention
sometimes operands are passed to subroutines in non standard ways (not on stack) which prevent to use
ret directly. In such cases
jp (hl) can solve such issues.
For example take a look at this x86 example:
printl is expecting printed string directly after the call ...
db 'SPEKTRA software & hardware',0
this spares the input data pointer setting and also its more comfortable as the printed text is directly where it is used (not in some table in data section) also you do not need labels. As you can see the
printl can not ret directly to the address stored in stack. Instead it needs to jump after the text which is ideal usage of
jp (hl) example (however my x86 code does it differently due to different instruction set).
resolve self modify code issues
configurable programs do not have static jump/call addresses so
jp (hl) can be used for configurable jumps...
Now getting back to jumping into middle of instruction
This might be or might not be true. If you use disassembler then it does not know where the
ORG statement of each code segment is. So it instead translates from start of the file. When you are tracing then it takes the
PC as the starting position and translates from it relatively so if you scroll the listing you might got wrongly disassembled other parts of code. However after few continuous single byte instructions the code is aligned again so most of the code would be translated correctly.
But there are also cases when we deliberately jump to middle of instruction. Sometimes time sensitive code needs exact number of
[T] tacts to spend on some routine and jumping in the middle of some instruction can resolve missing
[T] states after some conditional behavior.