Ну тогда значит так. По коду. На диске и в страницах памяти хранятся специальным образом подготовленные кодовые модули (которые можно получить конвертированием выхода SDCC). С настройкой адресов. Может быть даже сжатые каким-то простым способом типа хруста. Можно опциональное сжатие. Диспетчер загрузки модуля проверяет его наличие в страницах памяти, если нету, подгружает с диска, перегоняет (с распаковкой при необходимости) из страничной памяти в основную. При этом нужно будет ограничиться пространством только в нижней памяти для рабочего кода, а верхнюю будут впечатываться страницы с упакованным кодом. Если позволить использовать код в верхних страницах, то остаётся проблема второго экрана. Остаётся проблема распределения страниц. Это уже уголок ассемблерщика или кодера демок, но это никак не высокоуровневая задача. При таком подходе много ограничений. Например, ограничен объём загруженных в нижнюю память кодовых модулей. Верхняя память рассматривается как подкачка, в которую вытесняются неактивные модули при недостатке памяти для работы.
Адреса для доступа к данным тоже могут настраиваться.
Сложно, малоэффективно. Как и любые попытки забыть об ассемблере при кодировании под Z80. С этим процессором пора завязывать. Не обижайся.
Спек видится очень слабым полем для таких механизмов, они сами по себе будут занимать довольно приличные ресурсы. Не знаю как насчёт клонов Спектрума, но сам Спек даже со страничной памятью в 256-512 Мб будет смотреться бледновато.
По данным. Модули могут содержать память данных вместе с кодом. При наличии куска данных размером больше 16 кб начинается масса головной боли. Тут и правда надо бить данные на кусочки размером в 256 байт (например?) и иметь все проблемы из-за снижения скорости доступа - из-за блочности и из-за того, что блоки могут быть размещены в нескольких страницах. В самом же простом случае если не брать во внимание динамическую модульность, принять максимальный размер массива в 16 кб и согласиться со всеми другими ограничениями - это работать с данными примерно так:
SetMemPage(Sprites); PutSprite(HeroPhase1);
SetMemPage(Fonts); Text("Lives: "); Int(lives);
где PutSprite уже знает смещение к нужному спрайту в подготовленной вызовом SetMemPage странице. То есть где-то так.