Mapping something beyond its own accessible address isn't so bad. You have to approach it from a hardware address line perspective.
Let's say you have a 16 byte space, and you have 11 bytes worth of unique data. How would you make such a board for real? Keeping in mind that virtually every ROM is a power of two ...
8 + 2 + 1 = 11
Now think about how you'd wire up such a board.
A3 == 0 ? ROM0 { A2, A1, A0 }
Else A1 == 0 ? ROM1 { A0 }
Else ROM2 { }
That would give you an interesting pattern:
0123456789AA89AA
0-7 = ROM0
8-9 = ROM1
A = ROM2
Of course, virtually all our sizes can be done with only one or two ROMs, so it's usually just very simple mirroring.
But we can make a simple algorithm to transform an address with the above mirroring.
unsigned Bus::mirror(unsigned addr, unsigned size) {
unsigned base = 0;
if(size) {
unsigned mask = 1 << 23;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
base += addr;
}
return base;
}
It's a bit prettier in recursive form; but it's slower that way and some of my maps are dynamically modified through MMCs, so