Вся история микрокомпьютеров, расцвет которых пришелся на начало 1980-х, фактически сводится к борьбе программистов и инженеров за каждый свободный байт. Именно оперативная память определяла границы дозволенного. Игры и прикладное ПО усложнялись, каждая новая функция отрезала кусочки от объема ОЗУ, доступного пользователю. Приходилось хитрить, выдумывать нестандартные подходы и всячески изворачиваться, чтобы втиснуть свой код и ресурсы в скромные 48, а то и 16 килобайт оперативной памяти.
Именно об этом мы хотим поговорить под катом. Вспомним самые популярные системы и подглядим, как именно программистам и инженерам удавалось «запихнуть» довольно сложные и объемные проекты в скромное железо того времени.
Зачем нужна ОЗУ, сколько ее можно иметь и при чем тут разрядность процессора
Прежде чем начать разговор о конкретных компьютерах и консолях 1980-х, давайте разберемся, зачем оперативная память в принципе нужна компьютеру. Вы смело можете пропустить этот раздел, если разбираетесь в предмете. Он написан в первую очередь для тех читателей, кто не погружен в тонкости устройства компьютера.
Оперативная память (RAM) — это быстрое рабочее хранилище компьютера, в котором находятся данные и программы, с которыми в данный момент работает процессор.. В отличие от устройств постоянного хранения (ПЗУ, HDD или SSD), оперативная память хранит данные только пока компьютер фактически включен. В современных ПК она позволяет моментально подгружать данные активных программ и игр. Некоторые проекты (например, Baldur’s Gate 3) предлагают специальную опцию: загружать в RAM как можно больше данных с диска, чтобы минимизировать подвисания, которые могут возникать, если файлы игры лежат на «медленном» жестком диске. Что касается ретро-пк, оперативная память, помимо хранения исполняемого кода и данных программ, могла выступать в качестве квази-диска. Пользователь мог загрузить, например, ОС CP/M и полноценно работать с ней без использования жесткого диска. В качестве энергонезависимого накопителя использовались дискеты, а копировать файлы между ними можно было, устраивая временное хранилище в свободных ячейках RAM.
Постоянные же данные (прошивка и набор базовых, необходимых для работы компьютера команд и данных) хранились в ПЗУ (постоянное запоминающее устройство). В большинстве домашних компьютеров использовались микросхемы ПЗУ, содержимое которых нельзя было изменить обычными средствами. В зависимости от типа микросхемы их либо невозможно было перепрограммировать вовсе, либо для этого требовалось специальное оборудование.
После включения содержимое оперативной памяти не имеет практического значения (оно представляет собой случайный набор данных), и она полностью доступна для записи программами. Впрочем, у каждого устройства были свои особенности организации ОЗУ, зависевшие от используемого процессора, количества самой памяти и особенностей архитектуры компьютера. Если вы ранее никогда не общались с ретро-пк и консолями, приготовьтесь: вас ждут немалые удивления.
Пожалуй, здесь стоит упомянуть о том, как разрядность («битность») процессора влияла на количество оперативной памяти и доступ к ней. Мы сознательно упростим это объяснение, чтобы не перегружать читателя специфической информацией и скорее перейти к сути.
У многих 8-битных процессоров адресная шина имела ширину 16 бит, что позволяло адресовать до 64 КБ памяти. Чтобы не перегружать объяснение, будем считать это типичным случаем. Упрощенно оперативную память можно представить как фотопленку. Каждый «кадр» имеет свой порядковый номер (индекс) и при необходимости процессор может затребовать данные из любой ячейки, обратившись к ней по ее индексу. Для этого нужно представить адрес в памяти (фактически смещение от начала памяти, включая ROM, в байтах) в качестве двоичного числа.
Например, число 178 в двоичной системе будет выглядеть как 10110010. Чтобы его записать, нужно ровно 8 бит (единиц или нулей). Чем больше десятичное число, тем больше разрядов может понадобиться для его записи в двоичной системе. Например, для записи числа 55000 понадобится уже вдвое больше, 16 бит: 1101011011011000.
Регистр восьмибитного процессора (можно рассматривать регистры как собственную память процессора, в которую помещаются числа, над которыми нужно совершить операцию, а затем выводится результат) вмещает ровно один байт (8 бит). Максимальное число уникальных комбинаций из восьми единиц и нулей – 256 (28). Во многих 8-битных процессорах регистры можно было использовать попарно, благодаря чему это число возрастает до 65536 (216). Это максимальное значение, которое можно представить в 16-битном адресе.
В случае с большинством 8-битных процессоров одновременно можно было адресовать не более 64 килобайт памяти. Однако часть этого пространства обычно занимало ПЗУ с прошивкой компьютера. Например, если под ПЗУ было отведено 16 килобайт, то для оперативной памяти оставалось только 48 килобайт адресного пространства. На знаменитом ZX Spectrum первый адрес оперативной памяти — 16384. Все адреса ниже относятся к ПЗУ.
Теоретически 64-битная архитектура позволяет адресовать до 16 эксбибайт памяти — настолько огромный объем, что современные компьютеры пока даже близко к нему не подходят. Теперь всё упирается в физические возможности материнских плат.
Но это вовсе не значит, что не существовало 8-битных компьютеров, оснащенных более чем 48 килобайтами памяти. Официальный ZX Spectrum 128 содержал, как нетрудно догадаться, целых 128 килобайт оперативной памяти. Были также совместимые «клоны», поддерживающие память объемом 512, 1024 и даже 4096 килобайт. Без трюков, как вы понимаете, здесь не обошлось.
Как получить больше памяти на 8-битных ЦПУ
С момента появления 8-битных процессоров перед программистами и инженерами стоял вечный экзистенциальный выбор: ограничиться тем, что есть, или попытаться обмануть систему. Главным спасением стал метод переключения банков. Инженеры делили физическую память на несколько блоков одинакового объема, но материнская плата «показывала» процессору только один из них за раз. Как только программе требовались новые текстуры или уровни игры, записью в специальный регистр можно было переключить банк памяти, и на том же самом адресе материализовались совершенно другие данные. Именно этот трюк позволил в конце жизненного цикла консоли создавать для знаменитой NES (Денди) невероятно красивые и технически продвинутые игры. Подобным же образом удалось «нарастить» количество памяти в ZX Spectrum 128.
Однако не все производители компьютерного железа гнались за количеством. Оперативная память была чрезвычайно дорога (и даже нынешние цены на DDR4 покажутся смешными, если изучить прайсы начала 1980-х), поэтому наиболее прагматичные компании отказывались от увеличения объема RAM, чтобы снизить стоимость конечного устройства и продать как можно больше компьютеров. Впрочем, не всё так однозначно.
Философия Клайва Синклера, основателя компании Sinclair Research, создавшей ZX Spectrum, строилась на тотальной экономии: домашний компьютер обязан стоить не дороже хорошей куртки. В 1985 году вслед за младшей моделью дебютировал ZX Spectrum 128. Новинка в одночасье обрушила рынок: никто прежде не предлагал пользователям так много памяти по такой низкой цене. Экономическое чудо держалось на экстремальном инженерном минимализме. Сердцем Спектрума служила кастомная микросхема ULA, которая в одиночку заменяла десятки стандартных логических чипов, отвечая одновременно за вывод картинки, звук и разводку памяти. Снижению цены способствовала и откровенно авантюрная стратегия закупок. По слухам, Sinclair закупала партии микросхем памяти с частично неисправными банками и проектировала платы так, чтобы использовать только гарантированно рабочую часть чипов. Насколько широко применялась эта практика, до сих пор остается предметом споров. При всем уважении, сэр Клайв Синклер не славился заботой о потребителе. Однако бесплатные сервисные центры все же открыл.
Ранние партии Spectrum действительно критиковали за нестабильное качество сборки и сравнительно высокий процент заводского брака. Но цена была настолько привлекательна для небогатого европейского рынка, что компьютеры, несмотря на кучу проблем, пользовались бешеным спросом.
В самой дешевой модели ZX Spectrum 16K под RAM выделили ровно одну «страницу» — от 4000 до 7FFF. Первые 6912 байт этой памяти фактически являлись видеопамятью – особой областью, содержимое которой отображалось на экране. Картинка формировалась прямо из ОЗУ. Поскольку вся память физически была реализована на одной микросхеме, процессору приходилось «делить» время доступа к ней с микросхемой ULA. Из-за этого ЦПУ работал с меньшей эффективностью, чем мог бы.
Spectrum 48K отличался от своего младшего собрата дополнительными 32 килобайта памяти (от 8000 до FFFF). Добавленная «верхняя» память решала проблему, из-за которой процессор вынужденно тормозился: ULA в нее не заглядывала, поэтому самый критичный к скорости код можно было положить туда.
К середине 1980-х лимит нарастающее давление со стороны заокеанских конкурентов стало беспокоить Синклера, и появилась модель ZX Spectrum 128, которая несла на борту уже 128 КБ RAM и 32 КБ ROM, что превышало объем памяти, который процессор мог адресовать одновременно. Чтобы процессор мог дотянуться до всего этого богатства, была внедрена технология переключения банков памяти. Адресное пространство процессора разделили на четыре фиксированных окна по 16 килобайт. Нижнее окно (0000–3FFF) обычно занимал ROM, но теперь там можно было переключать страницы: либо старый BASIC от 48К, либо новое меню-интерфейс 128К с собственным бейсиком. Второе окно (4000–7FFF) намертво привязали к 5-й странице RAM, выполнявшей роль медленной видеопамяти. Третье окно (8000–BFFF) зафиксировали за быстрой 2-й страницей памяти.
А вот самое верхнее окно (C000–FFFF) стало динамическим «шлюзом». Через специальный порт конфигурации (7FFD) программист мог мгновенно подставить в это окно любую из 8 доступных страниц RAM (от 0 до 7). Более того, Spectrum 128 мог использовать в качестве экранной не только пятую, но и седьмую страницу памяти. Программисты могли включить отображение 7-й страницы на экран, пока в фоне незаметно рисовали следующий кадр на 5-й странице, что позволило реализовать идеальный графический скроллинг без мерцания.
Отдельно стоит упомянуть советские и постсоветские клоны популярного компьютера. Когда официальная история Spectrum на Западе подошла к концу, на территории бывшего СССР она только начиналась. Советские инженеры и радиолюбители не просто клонировали британский компьютер, они превратили его в модульного монстра, убрав любые ограничения по памяти. Появились культовые Pentagon 128/512/1024 и Scorpion ZS-256. Используя недокументированные возможности и расширяя логику портов управления памятью, разработчики задействовали свободные биты порта 7FFD и ввели дополнительные порты (например, EFF7 на Пентагоне). Это позволило расширить количество адресуемых страниц памяти с 8 до 32, а затем и до 64. Вершиной этого безумия стали коммерческие клоны середины-конца 90-х и современные реплики, такие как ZX Evolution или Pentagon с 4 мегабайтами RAM.
Вся эта колоссальная по меркам 8-битного процессора память нарезалась на те же 16-килобайтные странички. Впрочем, это уже совсем другая история. Давайте посмотрим: а что в те годы происходило в США?
Если ZX Spectrum был невероятным комбайном из компромиссов и задумывался в первую очередь как самый дешевый учебный компьютер в Европе, то флагманы Commodore на их фоне выглядели как целые космические корабли.
Американские технологические гиганты вроде Commodore и Atari шли несколько другим путем, поскольку более зажиточный рынок США существенно развязывал им руки.
Легендарный Commodore 64 и целое семейство практически одинаковых Atari оснащались специализированными микросхемами, которые аппаратно просчитывали плавное движение объектов на экране и генерировали качественный синтезированный звук. Желание Синклера сэкономить на чипах лишила Spectrum этих преимуществ, из-за чего игры страдали от клешинга (грубого смешения цветов), а графика сильно нагружала центральный процессор. Кроме того, Commodore владела собственной фабрикой по производству микрочипов MOS Technology, что позволяло компании снижать себестоимость за счет колоссальных объемов собственного производства, а не за счет урезания возможностей архитектуры или использования самых дешевых компонентов на рынке.
Впрочем, глава Commodore Джек Трэмиел был известен жестким подходом к бизнесу. Ему принадлежат культовые слова: «Мы должны делать компьютеры для масс, а не для классов». Когда в 1981 году инженеры только проектировали Commodore 64, они колебались между установкой 16 или 64 килобайт памяти, поскольку чипы RAM стоили баснословных денег. Трэмиел лично принял волевое решение ставить максимум — 64 КБ. Его расчет оказался гениальным: благодаря покупке MOS Technology Commodore сама производила микросхемы, а Трэмиел предвидел, что к моменту массового релиза C64 в 1982 году рыночная цена на кремний рухнет в несколько раз. В итоге конкуренты, застрявшие на 16 КБ, просто не смогли тягаться с Commodore по цене и объему памяти, что и сделало C64 самым продаваемым компьютером в истории. Но вернемся к техническим нюансам.
Если Синклер всячески экономил на логике, то инженеры Commodore создали гибкую, но весьма запутанную систему, которая выжимала максимум из 8-битного процессора MOS 6510. Этот чип, как и Z80, имел 16-битную шину адреса и мог «увидеть» только 64 КБ. Однако всё было не так просто. Инженеры умудрились одновременно запихать в компьютер целых 64 КБ оперативной памяти (RAM), 20 КБ постоянной памяти (ROM) со встроенным BASIC и KERNAL, плюс аппаратные регистры графического чипа VIC-II и звукового SID.
Секрет C64 крылся в концепции Memory Mapping, реализовал которую встроенный коммутатор. В отличие от многих других компьютеров, где RAM и ROM жестко делили адресное пространство (в начале — ROM, затем область RAM), почти весь диапазон адресов C64 был занят оперативной памятью. Поверх отдельных ее участков при необходимости отображались прошивки ROM и регистры устройств ввода-вывода. Процессор 6510 имел уникальную особенность: байты 0000 и $0001 являлись встроенным портом ввода-вывода. Меняя значения битов по адресу $0001, программист мог аппаратно «отключать» фрагменты оригинального ROM. Например, можно было убрать встроенный BASIC, освободив дополнительные 8 КБ RAM, или полностью отключить систему KERNAL, обнажив под ней чистую оперативную память. Однако у этой медали была обратная сторона, которая потратило немало нервов разработчикам. Графический чип VIC-II, отвечавший за вывод картинки, был «слеп» к системному коммутатору. Он вообще не видел ROM и всегда «смотрел» напрямую на физическую RAM. Более того, шина адреса VIC-II была всего 14-битной, то есть за раз он мог видеть только 16 КБ памяти. Всю оперативную память C64 пришлось поделить на четыре графических банка по 16 КБ. Переключая биты на микросхеме ввода-вывода CIA, можно было указать чипу VIC-II, в какой именно банк памяти смотреть прямо сейчас.
Поскольку официальная документация Commodore предлагала довольно консервативные методы работы, демосценеры и создатели игр довольно быстро освоили хаки и фокусы, позволявшие добиться максимума от архитектуры компьютера.
Первые 256 байт памяти (адреса $00–$FF), известные как Zero Page, процессор 6510 обрабатывал особенно эффективно. Адреса в этой области занимали всего один байт вместо двух, поэтому многие инструкции получались компактнее и выполнялись быстрее. В играх шла жестокая война за эти байты: кодеры отключали системные прерывания операционной системы, чтобы забрать Zero Page под свои переменные, координаты спрайтов и математические вычисления. Это давало кратное ускорение по сравнению с классическим подходом.
Но это только начало. Высший пилотаж – умудриться синхронизировать выполнение кода с движением растрового луча. VIC-II позволял вызывать прерывание процессора строго в тот момент, когда электронный луч кинескопа долетал до определенной строки на экране. Соответственно, можно было писать код, который подменял цвета, фон или банк памяти прямо на лету, пока кадр на экране только формируется.
Эти и многие другие трюки закрепили за Commodore 64 славу «короля демосцены». До сих пор программистам удается находить ранее неизвестные уловки и хитрости, добиваясь от сорокалетнего компьютера все новых и новых эффектов.

Но не компьютерами едиными! А что же было под капотом у знаменитой NES/Famicom/Денди?
Архитектура памяти у нее кардинально отличалась от домашних компьютеров. В то время как Commodore или Spectrum стремились быть универсальными машинами, в которых всё строилось вокруг центрального процессора, NES изначально создавалась как игровой автомат. Её память была разделена на полностью изолированные друг от друга части, связанные между собой через картридж, который, в свою очередь, был продолжением внутренней архитектуры приставки. Внутри оригинальной приставки NES трудились два главных чипа: центральный процессор Ricoh 2A03 (клон легендарного 6502) и графический сопроцессор PPU (Picture Processing Unit). У каждого из них была своя собственная адресная шина и своя, полностью изолированная оперативная память. Они физически не могли заглянуть в чужие «владения».
Процессор консоли имел смехотворный по меркам компьютеров объем оперативной памяти — всего 2 килобайта. Она располагалась в самом начале адресного пространства процессора. В эти 2 КБ программистам нужно было упаковать все переменные игры: координаты игрока, здоровье врагов, счетчики очков и текущую логику уровней. Всё, что шло в адресном пространстве выше 2 КБ, предназначалось для портов ввода-вывода (звук, джойстики) и, самое главное, для подключения банков памяти картриджа.
Графический чип PPU имел собственные 2 килобайта памяти, которые использовались исключительно для хранения таблиц экранов. Этой памяти физически хватало, чтобы хранить два полных экрана игры. Если игра скроллилась по горизонтали (как Super Mario Bros.), эти два экрана выстраивались в ряд, и пока игрок бежал по первому, процессор незаметно перерисовывал второй. Внутри PPU также была распаяна отдельная сверхбыстрая память объемом 256 байт. Она предназначалась строго для хранения характеристик 64 экранных спрайтов (каждый спрайт описывался 4 байтами: координата X, координата Y, номер картинки и атрибуты цвета).
Поскольку внутри приставки оперативной памяти было решительно мало, все ресурсы игры находились на картридже. Картридж вставлялся прямо в системные шины процессора и видеочипа, добавляясь к ним как внешняя постоянная память. Причем эта память тоже была жестко разделена на PRG-ROM (Program ROM) и CHR-ROM (Character ROM). Первый фрагмент содержал код игры и подключался напрямую к процессору. CHR-ROM содержал игровую графику и подключался, соответственно, напрямую к графическому чипу PPU. Если на картридже стояла CHR-ROM, видеочип брал графику оттуда. Если вместо CHR-ROM использовалась CHR-RAM, процессор загружал графику в CHR-RAM через регистры PPU. Зато подменять ее затем можно было прямо на ходу (так, например, делалось в Metroid).
Такая жесткая и ограниченная архитектура заставляла разработчиков идти на невероятные ухищрения, чтобы игры не выглядели примитивно и неказисто. Это породило целую кучу интересных тркюков с памятью и подменой данных на лету. Расскажем о парочке из них.
Sprite DMA
Поскольку процессор NES был весьма медленным, постоянный ручной перенос графики в память видеочипа непременно привел бы к лагам в игре. Инженеры Nintendo встроили в процессор аппаратный механизм прямого доступа к памяти (DMA). Программист создавал зеркальную копию всех 64 спрайтов в системной RAM (занимая под это 256 байт из 2048 доступных), а затем давал специальную команду процессору. Процессор «замирал» на 513 тактов, а DMA в это время одним мощным залпом перекидывал весь блок данных из системной RAM прямиком в память видеочипа. Этот трюк выполнялся строго во время VBlank, пока телевизионный луч возвращался в начало экрана.
Чипы-мапперы
Процессор NES физически мог адресовать только 32 КБ игрового кода с картриджа, а видеочип одновременно видел лишь 8 КБ графических данных картриджа. Когда игры стали сложнее, этого перестало хватать. Тогда разработчики начали встраивать в сами картриджи специальные микросхемы — мапперы (MMC). Маппер перехватывал сигналы от процессора и позволял незаметно подменять целые куски памяти картриджа. Процессор думал, что обращается к одному и тому же адресу, но маппер «подставлял» туда другую страницу памяти. Благодаря этому игры выросли со скромных 40 КБ (Super Mario Bros.) до гигантских мегабайтных масштабов (Kirby’s Adventure). Это привело к тому, что за один присест огромные игры стало проходить невозможно, и на борту картриджа стали размещать дополнительную SRAM с батарейкой специально для сохранений (The Legend of Zelda).
Эволюция оперативной памяти в 8-битных микрокомпьютерах и консолях доказала, что жесткие технические рамки — лучший катализатор для инженерного гения. Ограничения процессорных архитектур и дороговизна кремния не остановили прогресс, а заставили создателей железа и софта прыгнуть выше головы. И не важно, в какую сторону совершался этот «прыжок»: будь то авантюрная экономия Клайва Синклера, слоеный пирог архитектуры Commodore 64 под управлением Джека Трэмиела или NES с хитрыми мапперами — каждая платформа решала в первую очередь одну единственную задачу: выжать максимум из каждого доступного байта.
Партизанские методы обхода аппаратных лимитов навсегда изменили индустрию. Рожденные в муках компромиссов концепции сформировали фундамент, на котором работают операционные системы наших современных ПК и смартфонов. К тому же не стоит забывать, скольких талантливых программистов и инженеров воспитала постоянная борьба с «любимым» железом и вечное желание выйти за пределы его технических ограничений.
ссылка на оригинал статьи https://habr.com/ru/articles/1066142/