Соответственно при нормальном программировании лимиты: 32кБайт констант и 32кБайт ОЗУ.
* Исполняемый код из code space читается совершенно отдельно и эта картинка к нему не имеет отношения, считать константы из code space можно либо спец. инструкциями (компилятор напрямую не поддерживает), либо кусок code space замапить в окно и читать как данные, как уже показано на картинке.
Если нужно больше констант, то проще написать функции для копирования из памяти констант в обычное ОЗУ кусочками, мапить другую страницу трудно (см. дальше).
Если нужно больше ОЗУ, то потенциально можно ручками воткнуть в окно другую банку вместо констант (ОЗУ или программную память тоже), но нормальная C-программа тут же перестаёт работать (потому, что ей самой для работы нужны константы -- их в обычной программе полно, в т.ч. неявных, нужных самому компилятору). Можно константы на этапе линковки перенести в нижние 32кБайт ОЗУ, тогда заработает, но это слишком расточительно. Либо писать какие-то функции на ассемблере которые умудряются переключать банки и что-то читать из банки замапленной вместо констант. Уже как-то нетривиально. Либо, поскольку на чтение и запись можно замапить разные банки, на чтение оставить константы, а на запись замапить видеопамять -- так ещё терпимо. Почему кстати много памяти как раз у чипов с видеоконтроллером.
Но толком из C работать с памятью в банках -- невозможно. В компиляторе нет, как я помню, понятия far pointer (как на x86) для переменных (для кода есть, см. ниже) и его толком невозможно сделать. Верней там есть "eedata", но обращение к нему -- руками из ассемблера (он чём я выше написал), компилятор код не сгенерирует. Почему невозможно сделать: для нормальной работы C нужно несколько окон или сегментных регистров, один для работы с текущим указателем, один для констант и т.п. Почему сегментных регистров в x86 -- много (cs, ds, es, fs, gs...) С одним нужно постоянно жонглировать этой банкой, как в PIC18. Практически окажется слишком неэффективно, элементарная функция типа memcpy превращается в игольное ушко наподобии DPTR у x51 (для нормальной работы нужно хотя бы два окна/сегмента, для каждого указателя свой).
** В микрочиповской документации far и near означают совсем другое -- память ниже первых 8-и килобайт может адресоваться инструкциями напрямую (полный адрес умещается в код операции), выше 8-и килобайт как на RISC процессоре (вначале в регистр грузим адрес, потом косвенно обращаемся). Соответственно near -- первые 8 кБайт ОЗУ, far -- всё остальное (все 64 килобайта, остальное через банки, поэтому не надо путать "eedata" и "far").
Код тоже выходит за пределы 64 килослов или килобайт (не помню): регистр PC занимает где-то три байта. Но адреса функций 16-битные. Это достигается тем, что если функция не помещается в нижние 64к программной памяти, то линкер прозрачно пoдсовывает в нижнюю часть памяти thunk который делает jump на полноценный адрес функции. С данными, понятно, так не поступишь.