Since the code doesn't have an address inside the emulated memory space, it kind of makes sense for there to be a separate return stack. Problem is that you're trying to emulate 8086 and not a Forth machine :)
One solution might be to have 'CALL' decrement SP (perhaps writing some dummy value like 0000 or FFFF to memory), and also remember where SS:SP points now. The interpreter's return stack could store that address, along with the reference to the line of code it should return to.
When executing 'RET', check if the stack pointer is at the same location as it was after the last 'CALL', and halt with an error message if it is different. I suggest you also allow 'JMP' to procedures, or just get rid of the separation entirely and make everything a label. The procedure syntax could still be allowed as an alternative but just declare a normal label.
Another small but impactful bug: REP seems to always perform one more iteration when CX is zero!