If you want to execute code out of a memory region which is mapped with a read handler, you need to set things up right. The following explains how:

opbase_handler memory_set_opbase_handler(int cpunum, opbase_handler function)

This provides a callback for each CPU that is called whenever a branch is taken (assuming the use of change_pc(), which most CPUs do). The opbase_handle function looks like:

offs_t opbase_handler(offs_t address)

It can do one of two things. It can either use the address and fix up the ROM base pointers itself by changing the opcode_base and opcode_arg_base globals to point to (base of memory - offset of memory range start). In this case, the return value is ~0.

Or, the opbase handler can return a different PC value which is used to compute which regular RAM bank to use. For example, if you had range 0000-03ff mapped to a read handler, and had range 0400-07ff mapped to a RAM block, and they were supposed to mirror each other, you could write an opbase handler that returned (pc+0x400) for any pc < 0x400.

Obviously, if you don't want to do anything in particular, you can just return the original address that was passed to you and it becomes a no-op, letting the memory system do its normal thing.

You can see a simple example of this usage in drivers/missile.c.