[Перевод] 640 КБ на самом деле хватит всем

image


Никогда не сдавайтесь


Действительно ли Билл Гейтс произнёс фразу »640 КБ должно хватить всем»? Её история довольно туманна, однако чаще всего её приписывают Биллу, так что, возможно, он действительно такое говорил.

Его довольно часто за это высмеивали. Мысль о общем пространстве памяти размером всего 640 КБ по современным стандартам смехотворна. В этот размер не уместится даже исполняемые файлы большинства программ-установщиков.

Для сравнения: калькулятор в Windows 10 занимает в состоянии простоя 16,2 оперативной памяти — почти в 26 раз больше, чем объём доступной DOS-программам памяти в 1980-х.

Странные дела


Поверите ли вы мне, если я скажу, что до сих пор существует активное сообщество, использующее эту устаревшую платформу и разрабатывающее для неё ПО?

Наверно, вашим первым вопросом будет «Но зачем?» И я хорошо вас понимаю. Давайте рассмотрим некоторые группы, которые до сих пор заинтересованы во вложениях усилий в DOS.

Разработчики legacy-систем

Сегодня по-прежнему существует множество программ, которые выполняются в DOS. Промышленные управляющие программы, системы в точках продаж и научные инструменты — вот только некоторые из примеров областей применения, в которых даже сегодня используется DOS. С течением времени большинство этих программ были портированы на другие системы или полностью переписаны, но часть всё же сохранилась.

Незатратные системы реального времени

DOS в сущности является операционной системой реального времени (ОСРВ). Она не была специально разработана для этой цели, но её минималистичный дизайн позволяет отнести её к другим подобным системам.

ОСРВ характеризуются предсказуемой задержкой программных и аппаратных запросов. Так как DOS имеет минимальный API без внутренней многозадачности, стабильность задержек вызовов операционной системы довольно высока.

Хотя уже существуют более современные примеры систем реального времени с улучшенным дизайном и разработанные специально для этой цели, тончайший интерфейс DOS между приложением и оборудованием даёт ей преимущество в этой области использования. Поскольку такие системы, как FreeDOS (которую я рассмотрю ниже) имеют открытые исходники и распространяются бесплатно, они являются хорошей альтернативой другим ОСРВ.

Ностальгические видеоигры

Современные видеоигры потрясающи. Они требуют огромной мощи 3D, но графика, звук и геймплей иногда настолько реалистичны, что их можно спутать с реальностью. Оборудование виртуальной реальности ещё больше усиливает это ощущение, позволяя игрокам полностью погрузиться в среду игры.

Но несмотря на весь этот прогресс, многие геймеры, бывшие детьми в 80-х и начале 90-х, с любовью вспоминают времена, когда примитивная (по современным стандартам) графика и синтезаторные звуки заставляли их заполнять пробелы собственным воображением.

Несмотря на то, что оборудование оставило ограничения эры DOS далеко позади, по-прежнему существует активная сцена DOS-геймеров. Движимые ностальгией и любовью к программированию, разработчики пишут игры, запускаемые на старых системах, в том числе и на PC, которые изначально поставлялись с DOS.

Если вы считаете, что этот рынок ограничен кучкой пенсионеров, пытающихся вернуться в своё технологическое детство, то вы ошибаетесь. Дэвид Мюррей, больше известный в YouTube как The 8-bit Guy, имеет более полумиллиона подписчиков, которые еженедельно исследуют территорию ностальгического компьютерного «железа». Он даже написал стратегию реального времени для Commodore 64 под названием PlanetX2. Проект имел такой успех, что Дэвид распродал все физические запасы носителей с игрой и планирует создать сиквел для платформы DOS.

Запустите DOS


Существует множество способов запуска DOS. Возможно, у вас даже сохранился компьютер, на котором можно запустить её прямо сейчас. Но поскольку мы хотим плотно работать с этой операционной системой, то воспользуемся способом, в котором применяется бесплатное ПО, работающее почти на любом компьютере.

Для начала немного поговорим о лицензии. По некоторым данным, Microsoft сделала MS-DOS 6.22 общественным достоянием, но мне не удалось найти этому подтверждения. Поскольку ПО можно легко скачать из достаточно безопасных онлайн-источников, я не сторонник установки того, что может нарушать права на копирование. Даже если у вас есть законно приобретённая копия (а я подозреваю, что она есть у многих), её установка на виртуальную машину может быть юридически рискованным поступком.

Чтобы упростить установку, мы воспользуемся FreeDOS. FreeDOS — это клон DOS с открытым исходным кодом, написанный Джимом Холлом. Джим начал разработку в июне 1994 года, когда Microsoft заявила о том, что больше не будет продавать и поддерживать MS-DOS. Через несколько недель к проекту присоединились Пэт Виллэни и Тим Норман. Всего через несколько месяцев появилась версия 0.01. Сегодня мы пользуемся версией 1.2, выпущенной на Рождество 2016 года.

Сначала мы зайдём на веб-сайт FreeDOS и скачаем ISO-образ стандартного установщика на CD-ROM. Также можно скачать USB-версию, но с учётом различий в «железе», BIOS и т.д. мы не будем рассматривать здесь этот способ. Вместо этого мы воспользуемся виртуализацией.

Я протестировал FreeDOS и с VirtualBox Oracle, и с VMware Workstation / Player. Оба продукта работают хорошо, однако VMware обеспечивает немного более качественную эмуляцию BIOS и имулирует PC speaker, используемый во многих играх для DOS. В примерах ниже я буду использовать VMware Workstation, но в принципе подойдёт любая система.

По умолчанию вариантами установки для FreeDOS являются VirtualBox и VMware. Вас может удивить, насколько мало выделяется ОЗУ и дискового места. Обычно я создаю для DOS двухгигабайтный диск, но и выбранных по умолчанию 512 МБ будет вполне достаточно.

Установка FreeDOS проста, достаточно загрузиться в виртуальной машине с ISO-образа (запрос файла ISO будет выдан при запуске или при создании виртуальной машины) и следовать экранным подсказкам. После того, как FreeDOS разобьёт диск и перезагрузится, снова выберите «Install to Hard Disk». Выберите «Full Installation» или «Full Installation with Sources», если вы любите видеть, как всё работает. Вы читаете эту статью, поэтому скорее всего так и есть, тогда почему бы не установить и исходники?

После установки необходимо будет перезагрузиться. На этот раз вместо выбора «Install to Hard Disk» в загрузочном меню CD выберите «Boot from System Hard Disk». Вариант по умолчанию Jemmex (with no EMS) вполне подходит для стандартной загрузки, особенно если вы хотите заняться разработкой.

Технически система FreeDOS готова к работе, только пока ещё полностью «голая». В таком состоянии удобно запускать игры и программы DOS, но оно не совсем подходит для разработки, потому что у нас нет важных инструментов.

Милый FDimples


Теперь настало время установить в FreeDOS немного программного обеспечения. ОС содержит в себе замечательную систему правления пакетами FDimples. Чтобы воспользоваться ею, введите:

fdimples


На этом экране можно установить широкий спектр ПО. Здесь у нас есть два варианта — установить все инструменты разработки, или просто установить всё. Второй вариант может показаться перебором, но помните, что по современным стандартам все эти программы займут очень мало места, поэтому на диске останется ещё много пространства.

101f55b37b2f08bc882ee298acba1262.png


Потрясающий пакетный менеджер с милым названием.

Для перемещения между категориями и пакетами можно использовать клавиши со стрелками. Пробелом выбираются отдельные пакеты, ENTER — целые категории. Какой бы вариант вы ни выбрали, обязательно отметьте категорию Development. Для запуска установки нажимайте TAB, пока не будет выбрана кнопка OK, затем нажмите ENTER.

От C++ не спрятаться


Но вы и не стремитесь, не так ли? C++ замечателен, и благодаря Ди Джею Делори производительность и возможности GNU Compiler Collection теперь доступны и в DOS.

Более того, с помощью поставляемого в комплекте расширителя DOS CWSDPMI программы могут использовать защищённый режим и компилироваться в 32-битные исполняемые файлы без малейших проблем. Вы сможете преодолеть барьер 640 КБ с большим запасом. Выкусите, мистер Гейтс!

Чтобы проверить работоспособность среды разработки, давайте создадим новую папку и тестовый проект:

mkdir C:\src
mkdir c:\src\hello
rhide c:\src\hello\hello.cpp


При этом запустится среда разработки RHIDE. Она не дотягивает до уровня Visual Studio, но для наших нужд разработки вполне достаточна. Мне она напоминает среду Borland Turbo-C, с которой связано множество приятных воспоминаний.

5f5f4305f9ce806cf0f680a910279815.png


От этого интерфейса по телу разливается тепло и пробегают мурашки.

Вот обязательный тестовый код:

#include 
using namespace std;

int main() {
  cout << "Hello, World!\n”;
  return 0;
}


Теперь можно запустить код с помощью меню «Run» (или нажав ALT+R, а затем R или CTRL+F9). Вы заметите, что можно пользоваться мышью и горячими клавишами. Поначалу мыши достаточно, но стоит запомнить горячие клавиши, которые указаны рядом с различными элементами меню. Проведя какое-то время в RHIDE, вы запомните самые важные из них и ваша продуктивность значительно увеличится!

Более интересный пример


Давайте сделаем что-нибудь более интересное. Для начала давайте создадим рабочее пространство:

mkdir c:\src\example
rhide c:\src\example\example.cpp


В этом примере мы покажем, как задаются видеорежимы, указываются пиксели и выполняется управление цветом в режиме VGA 320×200x256.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define COLOR_WHITE 15
#define MAX_WIDTH 320
#define MAX_HEIGHT 200

using namespace std;

void set_video_mode(int mode) {
  // Создаём структуру для регистров
  __dpmi_regs r;
 
  // Задаём AX значение видеорежима
  r.x.ax = mode;
 
  // Вызываем int 10 (установка видеорежима BIOS)
  __dpmi_int(0x10, &r);
}

void put_pixel(int x, int y, int c) {
  // Видеопамять VGA находится по адресу A000, поэтому давайте выполнить запись в него.
  _farpokeb(_dos_ds, 0xA0000+y*320+x, c);
}

void clear_pixel(int x, int y) {
  // Мы просто записываем пиксель 0, чтобы очистить точку
  _farpokeb(_dos_ds, 0xA0000+y*320+x, 0);
}

void draw_effect() {
  // Текущие координаты x и y
  int x = 0;
  int y = 0;

  // Для каждого до y_max записываем пиксель
  for (y = 0; y < MAX_HEIGHT, y++) {
    for (x=0; x < MAX_WIDTH, x++) {
      put_pixel(x, y, rand()%(15+1));
    }
  }
}

int main() {
  // Устанавливаем видеорежим 13h (320x200x256)
  set_video_mode(0x13);

  // Отрисовываем эффект
  draw_effect();

  // Ожидаем нажатия клавиши ENTER
  cin.ignore();

  // Возвращаемся в текстовый режим
  set_video_mode(3);

  return 0;
}


Пример с движением


В этом примере мы на основе предыдущего примера добавим управляемое пользователем движение. Мы будем использовать клавиши со стрелками, чтобы перемещать пиксель по экрану. Это станет фундаментом того, что может превратиться в видеоигру.

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define COLOR_WHITE 15
#define MAX_WIDTH 320
#define MAX_HEIGHT 200

#define KEY_ESC 283
#define KEY_UP 18432
#define KEY_DOWN 20480
#define KEY_LEFT 19200
#define KEY_RIGHT 19712

using namespace std;

void set_video_mode(int mode) {
  // Создаём структуру для регистровs
  __dpmi_regs r;
 
  // Задаём AX значение видеорежима
  r.x.ax = mode;
 
  // Вызываем int 10 (установка видеорежима BIOS)
  __dpmi_int(0x10, &r);
}

void put_pixel(int x, int y, int c) {
  // Видеопамять VGA находится по адресу A000, поэтому давайте выполнить запись в него.
  _farpokeb(_dos_ds, 0xA0000+y*320+x, c);
}

void clear_pixel(int x, int y) {
  // Мы просто записываем пиксель 0, чтобы очистить точку
  _farpokeb(_dos_ds, 0xA0000+y*320+x, 0);
}

int get_key() {
  // Создаём структуру для регистров
  __dpmi_regs r;
  // Присваиваем AH значение 00h для вызова BIOS
  r.x.ax = 0x0000;
  // Вызываем int 16 (обработчик клавиатуры BIOS)
  __dpmi_int(0x16, &r);
  // Возвращаем значение AX (полный скан-код)
  return r.x.ax;
}

int main() {

  // Устанавливаем видеорежим 13h (320x200x256)
  set_video_mode(0x13);

  // Основной цикл
  int running = 1;
  int curkey;
  // Начинаем с середины экрана
  int char_x = MAX_WIDTH / 2;
  int char_y = MAX_WIDTH / 2;
  while (running) {
    // Если нажата клавиша
    curkey = get_key();
    // Что бы ни случилось, мы всё равно должны очищать пиксель
    clear_pixel(char_x, char_y);
    switch (curkey) {
      case KEY_ESC:
        // Нажата клавиша ESC, поэтому выполняем выход
        running = 0;
        break;
      case KEY_UP:
        char_y--;
        break;
      case KEY_DOWN:
        char_y++;
        break;
      case KEY_LEFT:
        char_x--;
        break;
      case KEY_RIGHT:
        char_x++;
        break;
    }
    // Отрисовываем "персонажа" на экране
    put_pixel(char_x, char_y, COLOR_WHITE);
  }
  
  // Возвращаемся в текстовый режим
  set_video_mode(3);
  return 0;
}


Надеюсь, этот пример дал вам базовое понимание того, как обрабатывать ввод пользователя для перемещения персонажа по экрану. Некоторые части кода несовершенны.

Например, для получения нажатий клавиатуры мы используем INT 16. Это работает хорошо, но приостанавливает работу программы, пока она ждёт ввода. В более сложном примере мы бы использовали INT 16 AH = 01 для проверки состояния клавиатуры и возвращались бы к выполнению программы, если ввод с клавиатуры отсутствует. Это даст нам время на отрисовку другой графики. В нашем примере это работает нормально, но для более динамичной игры нужно будет писать обработчик событий клавиатуры с этими функциями.

Кроме того, put_pixel и clear_pixel работают достаточно хорошо, но оба они не так эффективны, как создание буфера кадра и запись в него. Буфер памяти можно с любой нужной скоростью копировать в видеопамять с помощью memcpy (). Пример его использования может выглядеть как-то так:

#include 

char buffer[320x200];
int i;
 
// Изменяем массив буфера, как любой другой массив
 
// Периодически копируем буфер в видеопамять
dosmemput(buffer, 320*200, 0xA0000);


На этом мы экономим множество изменений защищённого/реального режима, что значительно ускоряет операции записи. Кроме того, можно выполнять запись на экран во время выбора, что позволяет производить в фоне другие операции — воспроизведение музыки, запись на диск и загрузку графики.

Лучшее из обоих миров


Даже несмотря на то, что C++ — высокоуровневый язык, вы можете заметить, что в этом примере мне всё равно пришлось использовать псевдоассемблер. Такова природа всей системы. На ранних этапах программирования в DOS без BASIC код часто писался на языке ассемблера, и существовало несколько стандартных библиотек.

Однако в таком подходе есть своя красота. Вместо того, чтобы писать целиком на ассемблере, я могу воспользоваться мощью вызовов прерываний языка ассемблера в сочетании с удобством C++, не говоря уже о мощной системе управления памятью в защищённом режиме, доступной благодаря DJGPP.

Можно сказать, что используя эту систему, я жульничаю, и если относиться к этому строго, то так и есть. До появления расширителей DOS разработчикам приходилось мучиться, пытаясь уместить всё в 640 КБ. И если смотреть реалистично, у них редко было даже 640 КБ. На большинстве DOS-машин после загрузки драйверов и TSR (резидентных программ) едва находилось 600 КБ ОЗУ.

Этого было достаточно для простых программ, но когда требовались более сложные структуры данных, приходилось использовать расширители DOS или EMS (расширенную память). Код может быть и умещался в 600 КБ, но пространства данных в сложных программах и играх требовали места для роста, а барьер в 1 МБ был невероятно мал. Расширители DOS наподобие CWSDPMI были настоящими спасителями!

Подводим итог


Надеюсь, вам понравилось наше путешествие в Страну ностальгии. Программирование под DOS — это очень интересная область. В любой другой операционной системе сложно так близко подобраться к голому «железу», а без использования DJGPP трудно добиться равновесия между функциональностью и гибкостью.

Хочется, чтобы у нас было больше времени на более сложные примеры программ. С этим комплектом разработки наши возможности поистине безграничны. Мы получаем молниеносность программирования под DOS, графический потенциал системы VGA (которая, несмотря на свою древнюю природу, по-прежнему может создавать интересную игровую графику) и мощь стандартной библиотеки C.

Благодаря C++ мы можем создать класс персонажа для героя видеоигры, назначить ему переменные (например, текущее положение на экране, здоровье и силу) и управлять им с помощью клавиш со стрелками. Поскольку у нас есть до 4 ГБ адресуемого пространства (по крайней мере, теоретически), мы можем считывать с диска сложную тайловую графику или даже целые JPG пререндеренной графики. С помощью этой системы вполне возможно создать почти современную видеоигру.

Но у неё будет одна особенность, которой нет сегодня ни у одной игры. Её можно будет запускать на компьютере, который вам подарили на Рождество в двенадцать лет.

Приступайте к работе — ваш старый компьютер ждёт вас. Настало время снова с ним познакомиться.

© Habrahabr.ru