I came to the conclusion of it being "switchable memory banks". (Feel free to correct me.) How would I implement this?
Yes, while the region from 0x0000 to 0x3FFF always maps to the first 16KiB, the region from 0x4000 to 0x7FFF can be switched to any ROM bank depending on the MBC (memory bank controller) in the game cartridge.
There are different ways to implement this. I use a lookup table with 16 entries so that each entry maps 4KiB of memory, using the highmost 4 bits of the address. That size also fits nicely for the switchable RAM banks in the GBC and some other special regions.
This of course means that you can no longer access the whole memory as a single array.
Related to your second question, you might also want to treat memory access to 0xFF00-0xFFFF (and maybe some other regions) differently than regular memory, as some of them will trigger special behaviour.
For example, writing to 0xFF0F might trigger an interrupt after the current instruction, or writing to the ROM banks accesses the MBC.
Your console output looks fine, at least if you call it only after a write to 0xFF01.
And a (not so) small update from me: I reimplemented the CPU core in arm assembly, this time avoiding some of the bottlenecks of my old implementation, mainly interrupts and timer handling. Instruction decode/dispatch should also be faster.
The CPU passes almost all tests, with the only failures due to missing timers. Next up are the callbacks for special registers (timers, interrupts...) and graphics. It looks like I can't reuse too much of my old code, either due to the different structure of the emulator or because it is too slow. For the graphics, I'm considering caching the tiles to speed up the rendering, as the tile accesses seem to take a considerable amount of time.
It will probably take some weeks before I can work on it again, but I plan to have it running on the arm board at usable speed in the next few months.
In other news, it looks like one of my favorite gameboy emulators goomba color is under development again.