Turbo-Pascal 5.5 (и другие) в браузере — с загрузкой программ по ссылке

«Эх вот в школе я такую программулину написал, на Паскале» — бывают такие мысли, особенно у тех кто учился, скажем, в 90е. И даже находятся порой эти старые программы на старом диске. Но если запустить их ещё в DosBox у себя на машине можно — то как показать-похвастаться другим, в интернете?

Возьмём эмулятор js-dos (им старые игры в браузере запускают) — и поколдуем над ним, чтобы можно было своими паскальными-бейсиковыми программулинами делиться всем на радость.

Пользоваться этой поделкой вы сможете не вникая в подробности! Есть страничка для подготовки ссылок с программами — хоть сразу делитесь в комментариях :) А для тех кому захочется по аналогии и другие компиляторы-интерпретаторы подключить — будет немножко пояснений что и как там сделано — чтобы форкнув код на гитхабе вы могли быстро внести нужные изменения.

Как этим пользоваться?

Нам нужна страничка в которой выполняется, например, старый добрый Quick Basic 1.1 или Turbo Pascal 5.5 (до кучи я добавил Matlab 3.05) — притом такая, чтобы ей можно было каким-то образом передать программу для загрузки и выполнения.

А как именно «передать»? Мы же по ссылке делиться будем — ну значит в параметре ссылки передавать либо целиком программу (если она небольшая) — либо урл откуда её можно взять (главное чтобы cross-origin работал).

Если хотите сразу посмотреть на результат, то вот пожалуйста:

Игра «Escape» по мотивам Ж.Арсака

В открывшемся окне вы увидите довольно знакомый интерфейс редактора Турбо-Паскаля — нажмите Ctrl-F9 (как встарь) или Alt-R и Enter — и программа должна запуститься. Есть и возможность автозапуска — дальше увидим.

Зелёное - выход. Синее - игрок. Красно-жёлтое - падающие фрагменты.

Зелёное — выход. Синее — игрок. Красно-жёлтое — падающие фрагменты.

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

Если хотите попробовать «приготовить» ссылку с собственным кодом — зайдите на центральную страничку https://rodiongork.github.io/dosprog/ — внизу есть поле для ввода программы — скопируйте туда например следующую чепуху (отредактируйте по вкусу):

program demo;

uses crt;

begin
  clrscr;
  gotoxy(33, 13);
  writeln('I am a beautiful message!');
  readln;
end.

После чего убедитесь что выбран нужный язык (паскаль в данном случае) и нажмите кнопку. Под ней появится ссылка — по этой ссылке будет открываться ваша программа, можете попробовать. Если щёлкнете чекбокс «Autorun» — программа не откроется в редакторе, а сразу запустится на выполнение.

Поскольку программа может быть длинной, можно также разместить код программы где-нибудь в интернете (например на github pages как с примером выше) — и вместо самого кода использовать ссылку на него. Кроме возможности таким образом сделать большую программу с довольно короткой ссылкой это ещё и позволит редактировать впоследствии код не меняя самой ссылки.

Теперь когда в общих чертах понятно — давайте посмотрим подробности — они понадобятся если вы захотите аналогичным образом какую-то другую среду разработки запустить.

Создадим страницу с компилятором / интерпретатором

Эта часть очень простая. На сайте js-dos.com мы находим раздел Getting Started — и в нём готовый пример. В двух словах идея такая:

  • js-dos в себе содержит эмулятор (DoxBox) скомпилированный в JavaScript

  • поэтому на странице вы только создаёте зону где отображать «экран» (например, во всю страницу) и вставляете ссылки на таблицу стилей и на js-файл. Их можно либо использовать либо прямо с сайта js-dos (как в примере), либо скачать себе.

  • создаёте «бандл» с приложением — в общем-то zip-архив, в котором лежит кроме того и конфиг для DosBox

Первые два пункта развернутых пояснений не требуют, на третий взглянем чуть подробнее.

Во-первых, как «приготовить» бандл? Если у вас уже есть zip-архив с нужной досовской программой — останется только добавить конфиг.

Если архива пока нет, удобнее всего (по-моему) DosBox запустить на локальной машине, в него скачать все что надо, установить-настроить нужную программулину -, а потом её уже сархивировать в бандл. И опять же добавить конфиг.

Что там за конфиг? Обычный конфиг для DosBox — для начала можно взять тот что в примере (с диггером). В целом всевозможные настройки понятно описаны — самая важная для нас часть — в конце. Там перечислены несколько команд (на манер autoexec) для того чтобы загрузить нужную нам программу после старта.

Конфиг укладывается в подкаталог .jsdos с именем dosbox.conf — типичное его окончание может выглядеть так:

[autoexec]
mount c .
c:
c:\tp\turbo.exe

Сам код для запуска бандла в странице выглядит тоже достаточно понятно — имеет смысл только добавить параметр автозапуска (иначе отображается шестерёнка которую нужно кликнуть чтобы DosBox запустился):

    Dos(document.getElementById("dos"), {
        url: "./my_bundle.zip",
        autoStart: true,
    });

Подразумевая что бандл с именем my_bundle.zip лежит в той же директории что и сама страница — и что элемент для отображения рабочего окна программы имеет id="dos".

Если вы это проделаете — и запустите хотя бы локально (нужно использовать хоть простейший локальный сервер, типа busybox — или python3 -m http.server) — то скорее всего с радостью убедитесь что программа запускается (ну, если все сделано правильно). И выдаёт картинку, например как в заголовке статьи. Или вот с матлабом вариант:

в матлабе всё работает только графика в досбоксе почему-то плохо отображается

в матлабе всё работает только графика в досбоксе почему-то плохо отображается

Но как подгрузить текст программы?

Вот здесь и начинаются пляски с бубном.

То что код программы мы в простейшем случае кодируем в base64 и добавляем к ссылке как значение параметра ?p=... — это уже наверное понятно. Написать кусочек javascript чтобы взять урл текущей страницы и вытащить оттуда этот код — несложно:

  let ppos = location.href.indexOf('?p=');
  let data = atob(location.href.substring(ppos+3));

Это конечно упрощённый способ — мы не парсим параметры всерьёз, а просто находим нужный фрагмент с названием параметра (p), считая что параметр только один. Если вам понадобится больше — поправьте эту часть. После этого atob расшифровывает base64 кодировку — и в общем текст нашей программы в переменной data.

Здесь сразу добавим возможность вытащить программу из интернета, если вместо неё оказался урл:

  if (data.substring(0, 5) == 'https') {
      let req = new XMLHttpRequest();
      req.open('GET', data, false);
      req.send(null);
      data = (req.status === 200) ? req.responseText : '{error loading file}';
  }

Здесь мы используем синхронный XMLHttpRequest — это не лучший вариант, но для минимизации примера пусть так останется. Как видите, в случае ошибки мы вместо результата запроса хотим показать что сообщение о неуспешной загрузке.

Важный момент — конечно сервер с которого вы грузите программу должен отдавать её с соответствующим заголовком, позволяющим загрузку в другие страницы, например:
Access-Control-Allow-Origin: *

В частности github-pages так делают, а gist.github.com нет. Если вы захотите подгружать код с гист-гитхаб или с pastebin — скорее всего вам придётся что-то химичить.

Но это лишь пол-дела, даже наверное треть. Надо загрузить программу в наш DosBox.

Тут проблема. Конфигурация js-dos (в джаваскрипте, не в конфиге dosbox-а) позволяет указать например параметр initFs и dosboxConf — первый из них позволяет добавить файлы в файловую систему, второй помогает скорректировать например строку запуска нашей программы. К сожалению оба они не работают если используется «бандл».

На эту тему даже заведён issue в гитхабе js-dos-а. Там есть мелкие технические сложности, но возможно когда-то это будет улучшено. Пока же приходится мудрить.

В параметре onEvent можно указать функцию которая будет вызвана когда эмулятор уже загружен и в неё будет передан параметр — объект CommandInterface — у него в частности есть методы для работы с файловой системой.

К сожалению когда эмулятор уже загружен, вроде бы уже поздно что-то грузить в файловую систему :) Во-первых наша «IDE» уже будет запущена — да и DosBox не видит новых файлов пока не выполнишь команду rescan. Однако давайте есть слона по частям. Здесь оставим кусочек джаваскриптового кода который «забросит» нашу программу в файловую систему -, а в следующем разделе разберемся как её использовать.

Dos(document.getElementById("dos"), {
    url: "./my_bundle.zip",
    autoStart: true,
    onEvent: (event, ci) => {
        if (event === "ci-ready") {
              // здесь подготовим переменную "data" как показано выше
              // и теперь грузим:
              ci.fsWriteFile(dosprog.fname, new TextEncoder().encode(data));
        }
    }
});

BAT-файл для подгрузки программы

Итак, забросить файл в файловую систему эмулятора мы смогли, однако:

  • он её не видит пока не сделаем rescan

  • поскольку всё работает асинхронно, в эмуляторе редактор может запуститься раньше чем программа будет загружена

Чтобы не дожидаться пока какую-то фичу для этого запилят в js-dos (а может и есть какой-то ход до которого я не додумался) — можно просто написать старый-добрый досовский BAT-файл (аналог юниксовых shell-скриптов, только проще).

Мы будем запускать этот файл из autoexec-секции в конце dosbox.conf -, а что же будет в самом файле?

Будем делать rescan, проверять, появилась ли программа на диске, и если появилась то запускаем IDE -, а иначе повторяем операцию в цикле.

Один нюанс — нам нужна и возможность загружаться «начисто» — если никакой программы не передано. Ну что ж, будем в этом случае сигнализировать «добрасывая» в файловую систему пустой файл с именем, например, nofile.pas — в общем, BAT-файл получится такой:

:repeat
rescan
if exist prg.pas goto withfile
if exist nofile.pas goto nofile
goto repeat

:withfile
\tp\turbo.exe prg.pas
goto done

:nofile
\tp\turbo.exe

:done

Как видите, циклы, goto и переходы по меткам — логика простая. Если появился файл prg.pas — выскакиваем из цикла и переходим к запуску с файлом. Если появился файл nofile.pas — тоже выскакиваем и переходим к запуску без файла.

В javascript есть отдельная строчка чтобы этот nofile передать -, но не будем на этом сейчас останавливаться, просто посмотрите в коде по необходимости.

Автозапуск

В качестве приятной фичи желательно добавить возможность автозапуска. В случае с бейсиком это осуществляется добавлением ключа /run в командную строку. С турбо-паскалём я такого ключа не припомню, поэтому надо запустить компилятор tp.exe, а потом запустить скомпилированную программу. В случае матлаба файл и так запускается интерпретатором (отдельный вопрос — может это неудобно).

В общем всё это легко добавить в BAT-файл. Осталось только придумать как передать что мы хотим «автозапустить» программу. Я не придумал ничего лучше кроме как использовать ещё одно отдельное имя файла программы (например auto.pas) и по нему выбирать какой именно режим запуска требуется.

Ну, а чтобы на стороне джаваскрипта выбрать нужен ли автозапуск, условимся что это сигнализируется именем параметра в ссылке. Если это буква P большая — значит нужен. Если p маленькая — значит нет. Конечно это вы можете переделать на свой вкус.

На упомянутой «главной» страничке форма для создания ссылок имеет чекбокс чтобы отметить автозапуск.

Заключение

Код выложен на гитхаб — можно пользоваться как есть, можно склонировать себе и добавить любимые компиляторы и т.п.

Для удобства общий джаваскриптовый код отвечающий за запуск вынесен в файл dosprog.js — так что если захотите добавить например Turbo C 2.0, то вам нужны следующие шаги:

  • форкаем или клонируем репозиторий

  • копируем файл tp55.html например в tc20.html — и в нём редактируем заголовок, названия файлов (вместо prg.pas например prg.c и так далее) -, а также имя бандла

  • создаём бандл с таким именем как задали выше, в него положим кроме Turbo C также поправленный конфиг dosbox.conf и модифицированный BAT-файл (в общем-то тоже меняются имена файлов и все).

Можно было бы сделать удобнее если использовать серверную часть для хранения пользовательских программ (собственный «pastebin»), сделать пользовательские аккаунты и так далее. Но неясно насколько эта смешная поделка актуальна — кроме того тогда уже не будет так легко добавить собственные компиляторы и т.п.

Хорошо бы иметь возможность экспортировать отредактированный файл. Ведь из досбокса с экрана ничего не скопируешь. Такая возможность добавлена — если открыть консоль разработчика (Developer Console) в браузере и выполнить во вкладке console функцию в духе:

getFile("PRG.PAS")

то в консоль же будет выдана наша программа, уже закодированная в base64. Можно было бы сделать какой-то всплывающий диалог -, но пока не придумал куда прилепить кнопку для его вызова. Главное — заметьте что здесь регистр имени файла различается -, а в досе нет. Чаще всего файлы будут созданы с большими буквами в имени.

Переводы строк. Это больная тема — по умолчанию нередко может оказаться что они не «досовские» — и приложения типа турбо-паскаля отобразят все в одну строку. Для этого в код загрузки добавлена небольшая «автоподстановка»:

data = data.replace(/(?

Не пугайтесь её -, но если будут какие-то проблемы с переводами строк — вы теперь знаете где можно поправить.

Вот и всё! Можете пробовать и делиться своими историческими чудо-программами в комментариях :)

P.S. данную статью не следует рассматривать как руководство по JavaScript — автор мало знаком с ним и с фронтенд-разработкой — так что тут наверняка есть что улучшать!

© Habrahabr.ru