Кто угодно может пнуть мёртвого льва. Мёртвый лев не рыкнет на наглеца. Мёртвый лев не откусит ему ногу «по самое не хочу», хотя стоило бы. Лев мёртв, и теперь его может пнуть каждый ишак, что конечно же не показывает превосходство ишака над львом. Эта статья будет полна негодования и ненависти. Кровь ещё не закончила кипеть от негодования. Но, разумеется, помимо эмоций будут и сухие объективные факты, немножко исследования и расстановка точек над i. В интернете кто-то не прав... опять...
Существует целый ряд инструментов, технологий и вообще вещей, которым по какой-то непонятной вселенской несправедливости не повезло: нашлась масса непонятных людей, которые по какой-то необъяснимой причине начали распускать про эти инструменты/технологии/вещи разные небылицы, идиотские фейки, слухи и прочий порочащий репутацию «компромат». Можно не переживать, если речь идёт о технологии, которая находится «на пике» — у неё будет большое community и правда восторжествует. Совсем другое дело, когда речь идёт о чём-то, что далеко не на пике, чья минута славы в прошлом (возможно даже давно в прошлом) — здесь мёртвый «лев» не может дать сдачи, и что самое обидное, что в какой-то степени «лев» сейчас отчасти потому и мёртв, что ещё при его жизни началось необоснованное распространение всяких бредовых поверий и мифов про него. И сегодня речь пойдёт об одном из таких случаев.
Всё началось со статьи Что было бы, если BASIC развивался вместо C и Python. 06:30 утра, я едва продрал глаза, беру смартфон, чтобы посмотреть, нет ли важных уведомлений, и тут в новостной ленте попадается эта статья. И я просто никогда не могу пройти мимо подобных статей, потому что есть незыблемое правило: если на Хабре появляется статья про какой-нибудь древний Бейсик, то в комментариях к ней обязательно, гарантированно и непременно вспомнят QB и VB, и появятся странные люди со странной мотивацией,которые будут нести свою шаблонную ахинею про то, что QBasic/QuickBasic и (тут особенно обидно) Visual Basic, дескать, недо-инструменты, потому что не умеют в компилирование, а умеют лишь интерпретировать исходный код.
Тут надо сделать ремарку, что у меня особые отношения с Visual Basic. Ещё примерно с 1998-го года был (и есть по сей день) интернет-ресурс VBStreets, который был одним из самых подробных ресурсов и самых больших сообществ, посвящённых VB/VBA/VBScript/ASP и т.п. В былые времена мы проводили конкурсы совместно с Microsoft, мы издавали бумажные книги совместно с BHV и Ozon. И уже много-много лет я являюсь бессменным администратором этого ресурса. Сейчас в силу положения VB ресурс находится скорее в анабиозе, но речь не об этом. Я не просто администратор этого сайта, я в силу этого обстоятельства потратил какое-то умопомрачительное количество времен на исследование внутреннего устройства VB, на реверс-инжиниринг и тому подобные вещи, так что я знаю внутреннее устройство и внутренний мир VB/VBA как никто другой, и должен вам сказать, этот продукт, эта технология таит массу интересных вещей (если повезёт со свободным временем, я расскажу об этом в отдельных хабра-статьях — рассказы обещают быть очень интересными). И таким образом, досконально зная внутреннее устройство, я не могу спокойно проходить мимо какой-то вопиющей ахинени, которую пишут в частности про VB. На QB я конечно тоже когда то (очень) давно понаписал массу кода, однако QB я никогда досконально не исследовал и его внутренний мир я не знаю так хорошо. Тем не менее, по старой памяти и из ностальгических чувств, когда на QB льют лживые помои, я тоже пройти мимо молча не могу.
Так вот, как вы думаете, обошлось ли в этой статье, ссылку на которую я дал выше, точнее в комментариях к ней без бредовых баек про интерпретаторы? Конечно же нет! Никогда без таких комментариев такие статьи не обходятся.
Вот и сейчас хабра-юзер@Kreyне смог пройти мимо и решил прокомментировать имевшееся в исходной статье утверждение:
>>QuickBASIC/Basic Compiler от Microsoft, который переводил код BASIC в исполняемый .EXE
Ничего он не переводил, а просто упаковывал исходник в виде ресурса и прицеплял его к exe интерпретатора
И вот тут у меня возникает вопрос. Несколько вопросов.
Какая сила или какая мотивация заставляет людей писать подобные комментарии?
Почему когда она приходят с таким утверждением, они не начинают его со слов «Одна бабка сказала» или «Я где-то от каких-то мутных людей слышал, что ...» или «На заборе было написано...»
Если они считают, что эта информация проистекает из достоверного источника, почему не указывают этот достоверный источник, а если у них эта информация в голове на правах предположения или где-то услышанной байки, почему они не перепроверяют вброс, который собираются опубликовать? Вообще-то хорошим тоном считается отвечать за свои слова и проверять достоверность того, что собираешься сказать.
Знаете что я думаю? Я думаю это отголоски холиваров 35-летней давности. Был, допустим, холивар (религиозная война) между сторонниками QuickBasic и Turbo Pascal примерно 35 лет назад. И поскольку это религиозная война, а не аргументированный спор, то ни с одной стороны ни с другой не было знаний и действительной аргументации, а была только слепая вера в превосходство своей любимой игрушки. Нужно было просто сделать вброс, направленный на противную технологию, и чем громче и унизительнее он был звучал, тем лучше. Всё равно никто ничего не будет проверять и исследовать, ведь на то это и религиозная война. «— Я верую, что QuickBasic интерпретируемая какашка, и мне плевать, как там на самом деле!». Холивар давно угас, но отдельные кричалки и посылы словно информационные вирусы путешествуют до сих пор.
Спойлер для самых нетерпеливых
Ничего он [QuickBASIC] не переводил, а просто упаковывал исходник в виде ресурса и прицеплял его к exe интерпретатора
Достоверность этого утверждения — 0%. Это чушь, миф, байка. Дальше будет очень короткий, поверхностный, но действующий способ проверить, что это не правда.
Так вот: QuickBasic действительно умел порождать на выходе EXE-файлы. Которые могли работать отдельно и самостоятельно от IDE. И очень обидно будет/было бы за QuickBasic, если бы он под видом генерации самостоятельного EXE просто порождал бы копию программы-интерпретатора, в ресурсы которого засовывал исходник программы. Кстати, во времена 16-битных EXE-файлов реального режима не было понятие ресурсов, было понятие оверлеев. Это просто выглядит как какой-то обман, надувательство.
И вот что удивительно: я никогда на самом деле не копал внутрь QuickBasic. Я не смотрел и не проверял, что там содержится внутри сгенерированного (скомпилированного) EXE-файла. Вдруг там реально зашит исходник, который просто интерпретируется? Нет! Всегда хотелось считать и, даже стоит сказать иначе, не просто хотелось считать, а было логичным считать, всё всё устроено не так — что внутри EXE-именно машинный код нашей бейсик-программы, а не просто интерпретатор в связке с пришитым к нему исходником.
Давайте включим логику: это сейчас безумные времена, когда в порядке вещей на веб-странице иметь JS-скрипты, которые являются реализацией интерпретатора какого-то другого языка. В те годы интерпретатор был слишком дорогим удовольствием. Он жрёт много памяти. Он требует тактов на своё исполнение. Кто хоть раз писал интерптератор чего либо, знает, что это просто огромное дерево ветвлений и каскады if-ов для проверки всех возможных вариаций синтаксических конструкций на предмет отклонения. Нужно обработать все возможные отклонения от правильного синтаксиса интерпретируемого вами языка и выдать что-то вразумительное в случае ошибки (вы же не будете выдавать Syntax error на всё подряд?)
В таком случае интерпретатор был бы довольно массивным, и, как ни крути, он был бы обязан включать в себя хотя бы «текстовки» ошибок (сообщений об ошибках) для всех возможных вариантов нарушения синтаксиса. И даже за счёт одних только этих текстов сообщений об ошибок он уже получился бы прилично раздутым. А теперь представьте, что кто-то хочет написать 10 абсолютно простых, миниатюрных программок на QB. Тогда получается, что каждый EXE-файл содержал бы вшитую в него логику интерпретирования и ещё пачки строковых последовательностей с сообщениями об ошибках? Выглядит не очень логичным, но умозрительная рациональность или логичность какого-то подхода так себе аргумент «за» или «против» того, насколько такой подход соотносится с реальностью.
Поэтому давайте представим, что я тот самый человек, который хочет сделать вброс и заявить, что скомпилированная QB-программа на самом деле не скомпилированная, а просто склейка заранее заготовленного интерпретатора и исходного кода, который нужно интерпретировать. Или наоборот, я хочу сделать вброс, опровергающий такое общеустоявшееся мнение. Как бы там ни было, прежде чем делать вброс, я бы проверил свои тезисы, чтобы не сесть в лужу.
Я бы запустил QuickBasic и написал простенькую программу в духе Hello world:

Хотя это не важно в данном случае, выглядит результат работы этой программы вот так:
Но нам интересно вовсе не это, нам интересно скомпилировать эту программу в EXE-файл:
Обратите внимание, что здесь нам предлагают выбор: породить на свет EXE-файл, нуждающийся во внешнем BRUN45.EXE, или породить полностью самостоятельный или независимый EXE-файл. Давайте подыграем распространителям фейков и поверим в то, что тот самый BRUN45.EXE и есть интерпретатор, и нам предлагают либо вшить интерпретатор в сам итоговый EXE-файл, либо оставить в выходном EXE-файле маленький кусочек со ссылкой на и подгрузкой внешнего интерпретатор. Выберем вариант с зависимостью от внешнего BRUN45.EXE — в таком случае в нашем EXE-файле должен якобы остаться только исходный код программы и небольшой кусочек машинного кода, подгружающий интерпретатор из внешнего файла.
Компилируем! А теперь берём hex-редактор HIEW и смотрим содержимое только что сгенерированного EXE-файла. Прокрутимся в самый конец, ведь именно там должен быть исходный код, бережливо засунутый туда компилятором для последующей интерпретации в момент запуска:
Упс! Где же чёртов исходный код? Где же так милые сердцу ключевые слова DECLARE, SUB, END, FOR? Где же SOUND и PRINT? Где наш милый исходный код? Кажется, им тут и не пахнет! Может он не в конце, а в начале?

Но и в начале его нет! Ни в каком месте полученного EXE-файла исходного кода на языке QuickBasic не наблюдается и нет вообще.
Его здесь нет: ни целиком. Ни в виде отдельных процедур. Ни в виде отдельный statement-ов. Может хотя бы идентификаторы из нашего кода найдутся? Поищем-ка идентификатор HELLO и идентификатор MyCoolVariableI (именно для этого там в цикле не просто каноническое «i», а переменная со столь длинным именем):

Но никаких следов идентификаторов «HELLO» и «MyCoolVariableI» в содержимом EXE-файла не находится даже близко:

Как же так? Ведь нас уверяют, что в EXE-файл просто тупо вшивается исходник, а при запуске EXE-файла встроенный (или не встроенный, а лежащий рядышком?) интерпретатор начинает его интерпретировать? Но на поверку оказывается, что в EXE-файле не обнаруживается исходный код ни в каком виде. Не то, что даже в виде отдельных строк, а даже отдельно взятые идентификаторы в EXE-файл не попадают.
Но подождите, нам могут сурово возразить и обвинить нас в манипуляции. Ведь изначальный посыл звучал так:
Ничего он не переводил, а просто упаковывал исходник в виде ресурса и прицеплял его к exe интерпретатора
Здесь не сказано, что компилятор просто копировал исходный код в EXE-файл как есть, а сказано, что он упаковывал его. Наверное имеется в виду сжатие каким-нибудь PKZIP или LZW. Ведь это конец 80-х, и нам не на что больше тратить драгоценные такты CPU, кроме как на сжатие и разжатие исходного кода. <sarcasm>Тогда абсолютно логично, почему в содержимом файла мы не видим зашитого исходника — он сжат алгоритмом сжатия!</sarcasm>

Но подождите. Если сжать исходный код упаковщиком, пройтись по нему каким-то алгоритмом сжатия, тогда от исходного текста действительно не останется и следа. А я, кажется, вижу в содержимом EXE-файла признаки человеческой речи:

Так что нет, версия, что исходный код при компиляции сжимается, а при запуска готово EXE-файла распаковывается в первозданный вид и передаётся интерпретатору — не оправдывается.
Но подождите вновь! В современном сумасшедшем мире давно есть такая вещь как «минификация»: фронтендеры скармливают свои JS-файлы минификаторам, которые удаляют все пробельные символы, вырезают комментарии, заменяют идентификаторы на однобуквенные (или имеющие минимальное достаточно число букв), но строковые литералы при этом остаются как есть. Может и здесь что-то такое же происходит? Может создатели QB пошли дальше и все ключевые слова и идентификаторы заменили на бинарное представление, а строки остались? Может именно это имелось в виду под упаковкой?
А что если мы пойдём ва-банк? Для этого мы немного изменим наш тестовый код:
Мы модифицируем цикл FOR так, чтобы он пробегал по числам не от 1 до 13 (с дефолтным шагом 1), а от 0xBEEF до 0xCAFE с шагом 0x0123.
Посколько идентификатор «MyCoolVariableI» всё равно в итоговом EXE-файле не обнаруживается, мы заменим его на каноническое «i».
А ещё мы добавим процедуру EatMarker, принимающую 32-битное число и выводящую его:
SUB EatMarker (xxx AS LONG)
PRINT "PASSED MARKER:"; xxx
END SUB
И вызовем её из основного тела программы, передавая примечательное число 0xDEAD4FEE (мёртвый за плату).
В итоге мы получаем вот такую маленькую программку:

Смысл использования примечательных чисел (BEEF, CAFE, 0123, DEAD4FEE) в возможности поискать их в хекс-редакторе и посмотреть, как они вплетены в окружающие их бинарные данные. Компилируем этот код и опять открываем его в hex-редакторе HIEW.
Мы зайдём с конца. С числа 0xDEAD4FEE. Это 32-битное число, а между тем, QB генерировал 16-битный машинный код для 16-битного реального режима работы процессора. Если компилятор генерирует не машинный код, а какое-то промежуточное его представление, которое затем интерпретируется (насколько вообще может быть применён термин интерпретация к сильно переваренному коду, значительно отличающемуся от исходного) — то мы увидим в содержимом EXE-файла это 32-битное число как есть, с той лишь оговоркой, что это будет little-endian представление, то есть байты EE 4F AD DE. Если же я прав, и компилятор компилирует самый обыкновенный машинный код (как делал бы это компилятор C++), то мы увидим упаковку числа 0xDEAD4FEE в стек за два приёма (поскольку режим работы процессора — 16-битный).
Итак, компилируем, открываем в HIEW и пытаемся найти следы 0xDEAD4FEE:

И находим! Но находим не как 4 смежных байта, а сначала младшую часть (0x4FEE => байты EE 4F), а затем спустя 4 байта и старшую часть (0xDEADxxxx => байты AD DE). Давайте-ка не будем тянуть интригу, нажмём F4 (Mode) и выберем режим Disasm.
У машинного кода x86 нет битов самосинхронизации (в отличие от UTF-8, например), поэтому вывод дизассемблера зависит от того, откуда будем начинать дизассемблировать. Вообще-то я ожидал, что там будет использоваться инструкция push, но реальность оказалось чуть иной:

Что же мы тут такое видим? А мы видим, что эти байты EE 4F и AD DE является частью обычного, рядового, простого и привычного машинного кода x86! Никакой это не исходный код. Ни в сыром виде. Ни в упакованном/сжатом виде. Ни в минифицированном виде. Ни в промежуточном представлении.
Это самый настоящий машинный код, исполняемый код x86:
mov word ds:[0dc8], 4FEEh ; помещаем мл. часть числового литерала во вр. перем.
mov word ds:[0dca], DEADh ; помещаем ст. часть числового литерала во вр. перем.
mov ax, 0dc8 ; пушаем адрес временной переменной
push ax ; пушаем адрес временной переменной
call 0000:0109 ; и вызываем процедуру EatMarker
Я чуть-чуть ошибся, ожидая увидеть два push-а, а разгадка проста: на самом деле в QuickBasic аргументы процедур/функций всегда передаются ByRef (по ссылке), а не по значению (ByVal), то есть на физическом уровне передаётся не значение переменной, а адрес этой переменной, указатель на неё. Если же на уровне исходного кода в процедуру передаётся не переменная, а непосредственное значение (числовой литерал), то создаётся временная переменная, куда сохраняется числовой литерал, и указатель на эту временную невидимую переменную передаётся в вызываемую процедуру.
Но может это только для вызова процедур генерируется настоящий машинный код? А для control structures типа ветвлений и циклов используется интерпретация? Вспомним про наш FOR-цикл и вернёмся к нему: у нас там были примечательные числа 0xBEEF, 0xCADE, 0x0123. Поищем-ка их.
И конечно же мы их найдём. Далеко идти не надо, достаточно чуть-чуть прокрутиться наверх:

На этом экране в этом дизасм-листинге виден весь код основного тела нашей QB-программы. Видно, что и для цикла FOR тоже используется машинный код. У нас цикл FOR от значения 0xBEEF до 0xCAFE с шагом 0x0123. И мы здесь видим, что переменная «i» у нас живёт по адресу DS:[0DC6].
В начале цикла FOR инициализируется начальное значение: mov ax, 0BEEF. Сразу же идёт джамп на код проверки условия (продолжать ли цикл) — значение переменной i должно быть не больше 0xCAFE. И действительно, переменная i сравнивается с 0xCAFE (cmp ax, 0CAFE). Если условие выполняется, идёт условный джамп (jle) на тело for-цикла, если нет — выполнение переходит к следующей за циклом FOR инструкции. В теле цикла у нас в исходном коде строка «HELLO i%». Переменная i% передаётся в процедуру HELLO — и в машинном коде мы это видим (вместо значения переменной i% на стек кладётся её адрес), после чего i% инкрементируется на 0x123. За циклом мы видим вызов PRINT "We are done with..., а вслед за ним — вызов процедуры EatMarker, куда передаётся число DEAD4FEE (через временную переменную).
То есть буквально вот этот исходный код:

Превратился в 73 байта машинного кода. Исполняемого кода x86-процессора. Можно прямо сопоставить группы машинных команд строкам исходного кода:

В общем, что мы здесь видим? Исходный код на языке QuckBasic скомпиловался сразу и непосредственно в машинный код, в инструкции x86-процессора для 16-битного реального режима. В такие же инструкции и в такой же код, в каком бы скомпилировался for-цикл, будь он написан на Си. Никакой упаковки исходного кода здесь нет, никакой интерпретации кода здесь нет — интерпретировать эти инструкции будет процессор, его декодер команд.
А теперь вспомним изначальную цитату:
Ничего он не переводил, а просто упаковывал исходник в виде ресурса и прицеплял его к exe интерпретатора
Немая пауза... Мы открыли исполняемый файл и нашли там, чёрт его возьми, исполняемый код! Кто бы мог подумать, что внутри исполняемого файла будет исполняемый код? Никогда такого не было и вот опять... И обратите внимание, что вместо использования какого-нибудь отладчика я специально выбрал простейший hex-редактор HIEW, чтобы ни у кого не было соблазна сказать, что найденный код и найденные инструкции образовались в памяти процесса в результате распаковки/интерпретации/компиляции кода в процессе запуска EXE. Я показываю то, что уже есть в EXE-файле, не допуская никаких самораспаковок.
И это далеко не единственный комментарии такого толка в той статье. Вот «прекрасный» спор между @checkpointи @PerroSalchicha:
Каким бы хорошим не был Basic, это всё равно интерпретируемый язык. Взрослые парни пишут на компилируемых языках. В этом смысле все питонисты - дети. ;)
На что @PerroSalchichaсправедливо возмущается:
Бейсик стал компилируемым ещё в 1980-х, с появлением Турбо Бейсика :)
Но @checkpoint не успокаивается:
AFAIK, он не был компилируемым как таковым, просто среда упаковывала интерпретатор с кодом в один .EXE файл. Нормальных компиляторов с "Васика" я не встречал.
В этот момент воин света @PerroSalchichaпереходит на сторону тьму и тоже начинает писать ахинею:
По-моему, TB был как раз честным компилятором. Исполняющую среду с кодом в один EXE паковал Visual Basic
И @checkpointподытоживает:
Скорее всего Вы правы, мне на глаза попадались только QBASIC и Visual Basic. Оба умели генерировать .EXE, но не настоящий. :)
Изумительно...

С тем, что QuickBasic всё-таки умеет генерировать .EXE и при этом это настоящий .EXE, самый настоящий, более настоящего не придумать — мы вроде бы разобрались. Но QuickBasic никогда меня особо не интересовал. Совсем другое дело — Visual Basic — внутреннюю кухню которого я изучил вдоль и поперёк.
Эти дурацкие байки,я клянусь, всплывают в абсолютно любой статье на Хабре, где хоть как-то затрагиваются всевозможные бейсикоподобные языки. Эта паста постоянно пережёвывается кем-нибудь и передаётся из уст в уста.
Мне хочется воззвать хотя бы к логике людей, которые тиражируют подобные байки. Если люди в Microsoft в 80-х годах сумели сделать и осилили такую вещь как генерацию настоящих полноценных EXE-файлов с машинным кодом в таком в общем-то несерьёзном и игрушечном продукте, как QuickBasic, неужели вы хоть на минуту допускаете, что в таком монструозном продукте как Visual Basic кто-то сделал бы такую дичь как засовывание в EXE-файл исходника в склейке с интерпретатором? Разве хоть немного правдоподобным это выглядит?
Чтобы понимать, что за зверь такой VB, нужно понимать, как он родился и из каких проектов у него растут ноги. Здесь всё не так просто. Есть два пути разобраться в этом вопросе: короткий и более углубленный. Предполагая, что у вас не так много времени, расскажу об этом коротко, а затем, для тех, кто хочет узнать поглубже, дам несколько ссылок на свои переводы и статьи, проливающие свет на этот вопрос. Так вот, если говорить коротко:
На рубеже десятилетий (80-е/90-е) в Microsoft существовало такое подразделение, как DABU — Data Applications Business Unit. В ведении DABU тогда находилась разработка СУБД под название Omega. Кроме того, именно DABU занималась разработкой и поддержкой QuickBasic. В составе СУБД Omega должен был быть бейсикоподобный язык, и он тогда назывался EB, что расшифровывалось как Embedded Basic. Речь не идёт о бейсике для электроники типа Raspberry Pi (если бы оно в то время существовало), речь идёт о возможности встраивать что-то бейсикоподобное в своим приложения. Не трудно догадаться, что проект СУБД Omega со своим EB развился потом в MS Access/JET/MS SQL. Помимо этого, подразделению DABU было поручена разработка инновационной среды для объектно-ориентированной разработки приложений под Windows с бейсико-подобным языком программирования под кодовым названием Silver.
В то же время небезызвестный Joel Sploslky в одной из своих статей рассказывал, как в своё время Билл Гейтс решил, что в Excel должен был встроенный язык программирование, и что это должно быть нечто бейсикоподобное. Именно Джоэлю было поручено написать спецификацию для будущего языка программирования, который на начальном этапе тоже назывался EB — только команда расшифровывала это как Excel Basic, то есть бейсик для Excel. Джоэль, в частности, гордится, что принёс в спецификацию 4 инновационных момента: новый тип переменных Variant, который мог бы хранить что угодно (потому что ячейка Excel может хранить что угодно), поддержку позднего связывания наряду с ранним связыванием в ООП-вызовах, конструкцию For Each, позаимствованную из csh и конструкцию With...End With, позаимствованную из Паскаля.
Одновременно с этим в те же года некто Алан Купер — IT-предприниматель — разработал концепцию инструмента под названием Ruby (никакого отношения это не имеет к языку программирования Ruby). Ruby у купера — концепция (и не только концепция, но и рабочий прототип) менеджера рабочего стола, где пользователю даровали возможность самому себе в режиме конструктора делать то окружение, которое ему будет удобно.В Ruby была концепция так называемых «штуковин» (gizmos), эти штуковины можно было легко рисовать в любом месте на desk-ах, связывать друг с другом, привязывать к разным событиям и свойствам штуковин различные действия, например, при щелчке на пункт в списке могла запускаться какая-нибудь команда ОС/программа. В Ruby не было никакого своего языка программирования: ни бейсикоподобного, ни Си-подобного, ни какого либо вообще. Алану Куперу удалось выгодно продать эту концепцию и свой рабочий прототип Биллу Гейтсу в Microsoft.
Таким образом, сначала EB из проекта Omega попал и начал свою жизнь в рамках проекта Silver (IDE для Windows с ООП и бейсикоподобным языком). Потом проект Silver стал частью проекта Excel Basic (снова EB). Далее этот EB развился в то, что теперь известно как VBA.
Не отказываясь от первоначальной идеи проекта Silver (бейсикоподобная IDE с ООП для разработки Windows-приложений), Microsoft решило подружить проект EB с проектом Ruby — результат слияния и сращения двух изначально независимых продуктов сперва назывался Thunder, но позже отдел маркетинга решил иначе: EB стал называться Visual Basic for Applications, а Thunder, который представлял собой результат слияния EB и Ruby, стал называться просто Visual Basic.
Для тех, кто хочет детальнее изучить эти аспекты истории становления продуктов Microsoft, информация под спойлером:
Расширенная информация
Я давно написал статью о слиянии EB и Ruby, но прежде чем приступать к этой статье, рекомендуется подготовить свой мозг и прочитать две других, которые являются переводами статьей, написанных непосредственно теми людьми, кто работал в Microsoft и работал над Silver/Omega/EB/VBA/Ruby/VB. Читать рекомендую именно в таком порядке:
Почему меня называются «отцом Visual Basic'а — перевод одноимённой статьи Алана Купера, придумавшего систему Ruby.
Thunder... рождение Visual Basic — перевод статьи Скотта Фергюсона, человека из DABU, кто имел отношение к разработке Silver/Thunder/EB. Это буквально взгляд на VB с другой стороны тоннеля, потому что к уже имеющемуся ядру бейсикоподобного языка поставили задачу присоединить чужеродную систему — Ruby.
Ruby + EB = Visual Basic — моя статья, полагающаяся на материалы предыдущих двух, а также статьи Joel Spolsky и собственную аналитику и результаты глубоких исследований внутреннего устройства VB/VBA.
В итоге получается, что генеалогическое дерево VB очень сложное и переплетённое, и вообще, скорее даже не дерево, а просто граф — VB является побочным продуктом слияния VBA (EB) с другой независимой и купленной ранее у Алана Купера концепцией/идеей/технологией — Ruby.
А теперь важные вещи, касающиеся VBA/EB и VB в плане генерации кода:
EB/VBA никогда исторически не был интерпретируемым. Собственно, даже QuickBasic не интерпретирует исходный код в момент запуска программы под отладчиком — код интерпретируется в момент его вводы в редакторе кода и в дальнейшем внутри QB представлен не как код, а как нечто более абстрактное и предобработанное (но не как AST). EB унаследовал эту концепцию, и тоже не интерпретировал код (как это делал, скажем VBScript).
Поскольку EB должен был быть кроссплатформенным, поскольку, Excel, например, поставлялся так же и под Mac, а под Mac была своя аппаратная архитектура, то EB исторически никогда не компилировался в машинный код. Вместо этого авторы EB разработали свою виртуальную машину (почти как в Java) со своей собственной системой высокоуровневых команд. Байт-код для этой виртуальной машиной назывался P-код. Весь код, написанный на EB/VBA, в конечном счёте компилировался в P-код и в рабочем режиме (а также в режиме отладки) этот P-код исполнялся собственной P-кодной виртуальной машиной. Это был единственный и основной режим компиляции EB/VBA, что, в общем-то и логично. Реализация же самой виртуальной машины на разных аппаратных архитектурах и под разными ОС могла быть совершенно разной, а вот система команд была одной и той же, что теоретически означало бы, что единожды скомпилированный VB-код в P-код мог выполняться без перекомпиляции под сильно разными платформами. Отдельно стоит сказать, что EB/VBA не перекомпилирует P-код процедур и методов классов (и других объектных сущностей) всякий раз при запуске. Код компилируется по принципу JIT (если каких-то процедур не коснулись — они вообще не компилируется), и один раз скомпилировавшись, продолжает существовать между запусками, если процедуры не модифицировались. Более того, этот подход с P-кодом позволяет VBA давать разработчику уникальную возможность: поставив выполнение кода на паузу и словив его на паузу брекпоинтом или выполняя пошаговое выполнение, разработчик может очень существенно по живому переписывать код, менять строки кода местами, удалять что-то добавлять, дописывать, редактировать выражения. И это всё не требует перекомпиляции проекта, перезапуска, за исключением редких случаев (когда, например, был объявлен массив одного типа, а после правки он остался массивом, но уже другого типа, либо поменялась размерность).
Однако то, что было хорошо для EB/VBA, для Thunder/VB могло оказаться не самым оптимальным. Поэтому в VB, который умел (в отличие от VBA) порождать самостоятельные независимые EXE-файлы (включим сюда и DLL/OCX и т.п.), сделали целых два режима компиляции кода проекта.
Первый механизм, используемый VB — это компиляция кода во всё тот же P-код, как в VBA. Все процедуры компилируются в P-код и помещаются в EXE-файл вместе во вспомогательными структурами данных, чем-то похожими на RTTI в C++. Виртуальная же машина, исполняющая этот P-код, жила в отдельном файле, например, для Visual Basic 6 этот файл назывался MSVBVM60.DLL, Весь этот принцип исполнения P-кода на виртуальной машине был устроен так, что в любой момент из P-кода можно было вызвать Native-код процедуры (например системное API или какие-то функции из сторонних прикладных библиотек), а с другой стороны из любой native-кодной среды (например сишного кода) можно было вызвать процедуру, реализованную как P-код — для этого EB генерирует для такой процедуры крохотный переходничок, который передаёт управление виртуальной машине, заставляя её выполнять P-код процедуре, а затем выполнить возврат обратно в переходничок и в вызывающую сторону.
Второй механизм это то, чем не может похвастаться VBA (EB), но VBA ( = Thunder = EB+Ruby) похвастаться может — это генерация из VB-кода полноценного EXE-файла с трансляцией кода в машинный код x86, исполняемый непосредственно процессором. Без каких-либо интерпретации даже при очень поверхностном взгляде.
В обоих случаях никакой интерпретацией не пахнет. В одном случае виртуальная машина исполняет свой проприетарный байт-код, ровно так же, как это делает Java. В другом случае вообще генерируется машинный код, который исполняется процессором, как будто скомпилировали программу на Си или Си++.
Выбор, какой режим компиляции использовать, лежал на программисте. Оба варианта имели свои преимущества и недостатки. По умолчанию действовал вариант с генерацией машинного кода. Вариант с генерацией P-кода давал очень компактный код, потому что P-код инструкции были весьма высокоуровневыми: одна P-code инструкция могла делать то, что делает 50 машинных инструкций процессора. Зачастую P-кодный вариант был медленнее, чем исполняемый файл, сгенерированный в Native-код. Но не всегда: если куски машинного кода, составляющие реализацию виртуальной машины, удачно попадают в кеш инструкций процессора, выполнение P-code варианта могло (и может) наоборот обогнать Native-код.
Ну и по аналогии аналогии с QuickBasic, давайте напишем какой-нибудь бессмысленный в реальной жизни код на Visual Basic, скомпилируем и посмотрим под дизаессемблером, что же там генерируется. Но на этот раз мы не просто будем смотреть на результат компилции VB-кода, а рядом напишем аналогичный код на C++ и сравним оба варианта.
В качестве демонстрации напишем две процедуру/функции:
Первая — GetMinMax — принимает два числа (a и b) и возвращает минимальное из них и максимальное из них. При этом, если так оказалось, что a>b — вызывается WinAPI-функция MessageBeep с параметром MB_OK.
Вторая — Fact — принимает число n и подсчитывает факториал (n!) самым наивным образом, не заботясь о переполнениях при больших n.
Использовать будем два продукта одного года выпуска: Visual Basic 6 и Visual C++ 6 (более нового VB просто не существует):

Компилируем VB-код. Оставляем активной опцию «Compile to Native Code», которая и так выбрана по умолчанию для всех новых проектов. Compile to Native Code означает компиляцию в машинный код x86, который выполняется на виртуальной машиной, а непосредственно процессором. Ставим галочку Create Symbolic Debug Info — чтобы потом легко можно было найти наши процедуры в дизасм-листинге. Компилируем и получаем EXE-файл.

Теперь компилируемый сишный код:сишный код компилируем bat-ником со следующим содержимым:
cl test.cpp /O2 /link /dll /noentry /debugtype:coff user32.lib
pause
Здесь мы передаёт ключ /O2 компилятору, потому что это соответствует VB-шной оптимизации «Optimize for Fast Code» (см. скриншот выше), линкеру же мы передаём ключи /dll и /noentry (чтобы не писать функцию main ни в каком виде, ибо она в данном эксперименте не нужна), а также /debugtype:coff, чтобы нужные функции были как-то подписаны в дизассемблере.
И теперь сравниваем результат дизассемблирования обоих бинарных файлов:

Что мы здесь видим? Главным образом мы видим то, что внутри EXE-файла, порождённого на свет силами VB, содержится, чёрт его возьми, машинный код. Не исходный код, как говорят нам бредовые байки, не какое-то там промежуточное представление, а нормальный 32-битный машинный код.
Во вторую очередь мы видим, что на уровне машинного кода результат компиляции GetMinAndMax вообще абсолютно идентичен для VB6 и MSVC++6. И это в то же самое время, когда кто-то пишет глупость в духе:
Исполняющую среду с кодом в один EXE паковал Visual Basic
...
Оба умели генерировать .EXE, но не настоящий. :)
Я просто ума не приложу, чем EXE-файл, генерируемый силами VB, не является настоящим, если он, зараза, байт-в-байт, инструкция-в-инструкцию идентичен результату компиляции эквивалентного кода, написанного на C++? Но, конечно, я знаю ответ: авторам не интересно докапываться до истины или хотя бы проверять правомочность своих слов. Куда прикольнее просто пнуть тушу мёртвого льва, ведь лев уже не даст сдачи.
В случае с функцией Fact() варианты, которые выдали компиляторы VB и C++ отличаются: VB-шный выхлоп более многословен. Но это ни в коем случае не означает, что VB-шный компилятор в каком-то смысле менее полноценный или оптимальный.
Просто VB — это не Си (и не C++), а C++ — не VB. В Си язык никогда не будет сам заботиться о том, что у вас может произойти переполнение при выполнении целочисленной арифметики. VB — совсем другое дело. VB гарантирует вам, что случайное целочисленное переполнение не останется назамеченным: на каждую операцию, потенциально грозящую переполнением, VB сгенерирует код, выполняющий проверку, не произошло ли оно, и выбрасывающий ошибку (путём выкидывания SEH-исключения), позволяющую программисту отреагировать каким-либо образом.
Таким образом, в случае функции Fact() выхлоп от VB получился длиннее просто потому, что VB предполагает дополнительные телодвижения и автоматически делает их. Но вы, если вам вдруг это не надо и вы точно уверены, что переполнения не будет (или вам всё равно на результат, если оно всё-таки случится), можете деактивировать это поведение:

После этого (я также дал компилятору обещание, что в коде нет aliasing-а — ситуации, когда на одно местоположение в памяти ссылаются две различные переменные), если перекомпилировать код, то и для функции Fact() выхлоп на выходе VB-компилятора станет таким же, как у компилятора C++:

Машинный код идентичен инструкция-в-инструкцию, байт-в-байт, бит-в-бит.
Но мы с вами помним — VB просто вшивает исходный код в EXE-шник, наряду с интерпретатором. VB умеет делать EXE, но эти EXE априори ненастоящие, неполноценные, недоделанные какие-то. Ну, по крайней мере, так искренне считают люди, которые распространяют эти бредовые байки и поверия.
Вообще же, сравнение результата работы компилятора VB, работающего в режиме «Compile to Native Code», и компилятора C/C++ от Microsoft — тупейшее и бессмысленнейшее занятие. И дело здесь в следующем. (Речь, разумеется, идёт о компиляторах одной эпохии, одного поколения).
Нужно знать, как устроен компилятор (транслятор) C/C++, являющийся частью Microsoft Visual C++ 6.0 (для более ранних версий это актуально в той же степени — не только для шестой). Компилятор (транслятор) оформлен в виде исполняемого файла CL.EXE. На самом деле, внутри CL.EXE почти нет ничего интересного. Архитектура компилятора (транслятор) C/C++ предполагает двухкомпонентный подход: фронтенд и бэкенд (рассматривайте эти термины просто в контексте двухкомпонентной архитектуры, забудьте о веб-разработке и дополнительных коннотациях, связанных с веб-разработкой). CL.EXE разбивает работу по компиляции (трансляции) исходного файла на два этапа:
Первый этап выполняет фронтенд, который зависит от языка исходного кода. У Си свой фронтенд (C1.DLL), у Си++ — свой (C1XX.DLL). Задача фронтенда — выполнить первые, языко-специфичные шаги трансляции. Это обработка директив препроцессора, токенизация, построение AST-деревьев, построение таблиц имён, объектов, олицетворяющих процедуры, генерация графов хода выполнения, разворачивание циклов и тому подобное.
Второй этап выполняет бэкенд, который получает от фронтенда частично транслированную программу в максимально абстрагированном от конкретики ЯП формате — IL (Intermediate Language, ничего общего не имеет с MSIL в .NET). Задача бэкенда абстрактное представление программы низвести до уровня машинных команд, а точнее не только машинных команд, а объектного файла с его специями кода, данных и т.п. В случае компилятора CL.EXE из MSVC++6 бэкенд один — C2.DLL — которому абсолютно безразлично, компилировался ли сейчас исходник на C или на C++.
Таким образом, CL.EXE просто создаёт конвейер/пайплайн между фронтендом (выбирая его в зависимости от языка) и бэкендом (выбирая его в зависимости от аппаратной архитектуры, правда в случае с MSVC6 выбора нет — поддерживается только x86, 32-битный режим).
В случае же Visual Basic для генерации EXE-файлов в режиме «Compile into Native Code» Microsoft позаимствовали бэкенд (C2) компилятора C/C++ у команды Visual C++ и включили его в состав продукта VB. С единственной оговоркой: что теперь это не C2.DLL, а C2.EXE, который в процессе компиляции вызывается средой (VB IDE) и выполняет всю грязную работу.
Значительная часть оптимизаций уровня того, какие оптимальные инструкции выбрать, какой порядок чередования инструкций, какой план использования регистров и т.п. — всё это удел бэкенда C2. И абсолютно глупо сравнивать или задаваться целью сравнить, какой компилятор сгенерирует более хороший, либо быстрый, либо компактный машинный код для одной и той же задачи — VB или C/C++ — просто потому, что генерацией машинного кода и в том и в другом случае занимается один и тот же компилятор (а точнее его половинка) — C2.
Но вопрос не в том, что оптимальнее и насколько оптимальнее. Вопрос в том, что генерация машинного кода в случае с VB не уступает таковой у C++ (того же поколения, версии) просто по той причине, что делается силами и средствами того же самого механизма кодогенерации. И на фоне этого в комментариях циркулируют байки про интерпретацию или какие-то неполноценные EXE-файлы... Потому что мёртвый лев не даст сдачи; и про него можно писать любые гадости: достоверность никто не пойдёт проверять (не интересно же, когда есть модные молодёжные языки а-ля Rust или Zig), зато с радостью кто-нибудь перескажет вашу дурацкую небылицу.
Компиляция в P-code же — совсем другая история. Да, в этом случае код пользовательских процедур не превратится в машинный код. Он превратится в P-код и будет исполнен виртуальноый машиной VB. Но это ни коим образом не делает эти EXE каким-то неполноценными и не даёт право называть их интерпретируемыми, так же как выполнение Java-приложения на JVM не делает Java-код интерпретируемым.
Виртуальная машина VB — стековая. Все манипуляции операции делаются на собственном стеке ВМ. Подход чем-то похож на работы инструкциями FPU и регистрами FPU,которе организованы как стек.
Код процедуры GetMinAndMax:
Public Sub GetMinAndMax(ByVal a As Long, _
ByVal b As Long, _
ByRef Min As Long, _
ByRef Max As Long)
If a > b Then
Min = b
Max = a
MessageBeep MB_OK
Else
Min = a
Max = b
End If
End Sub
Например, превращается в такой P-код:
loc_401AF8: ILdI4 arg_C ' Поместить на стек VM аргумент #1 (он же [a])
loc_401AFB: ILdI4 arg_10 ' Поместить на стек VM аргумент #2 (он же [b])
loc_401AFE: GtI4 ' Сравнить два лежащих на стеке I4-числа (Long)
loc_401AFF: BranchF loc_401B1B ' Перейти туда-то, если первое не было больше второго
loc_401B02: ILdI4 arg_10 ' Поместить на стек VM аргумент #2 (он же [b])
loc_401B05: IStRf arg_14 ' Убрать его из стека в память в аргумент #3 (он же [Min])
loc_401B08: ILdI4 arg_C ' Поместить на стек VM аргумент #1 (он же [a])
loc_401B0B: IStRf arg_18 ' Убрать его из стека в память в аргумент #4 (он же [Max])
loc_401B0E: LitI4 0 ' Помустить на стек VM число-литерал 0 в формате I4 (Long)
loc_401B13: ImpAdCallFPR4 MessageBeep() ' Вызвать импортируемую функцию MessageBeep
loc_401B18: Branch loc_401B27 ' Безусловный переход на эпилог процедуры (чтобы перепрыгнуть Else-ветку)
loc_401B1B: ' else-часть условия
loc_401B1B: ILdI4 arg_C ' Загрузить (push) в стек значение аргумента #1 (он же [a])
loc_401B1E: IStRf arg_14 ' Извлечь (pop)) его из стека и поместить в аргумент #3 (он же Min)
loc_401B21: ILdI4 arg_10 ' Загрузить в стек значение аргумента #2 (он же [b])
loc_401B24: IStRf arg_18 ' Извлечь его из стека и поместить в аргумент #4 (он же Max)
loc_401B27: ExitProc ' Выход из процедуры
Теперь, когда вы встретите людей, либо утверждающих, что QuickBasic является интерпретируемым языком (особенно, если они говорят, что EXE-файл скомпилированной программы на поверку оказывается интерпретатором, с пришитым к нему исходным кодом), либо утверждающих, что Visual Basic является интерпретируемым языком (с той же чушью относительного неполноценности EXE-файлов), вы знаете, что делать. Позорьте их, бросайте в них соответствующими тряпками, минусуйте им карму. Вежливо и конструктивно объясните им, что они мягко говоря заблуждаются. Пришлите им ссылку на эту статью. И обязательно скажите, что делать громкие, но непроверенные заявления нужно обязательно с припиской «одна бабка сказала».
Фух, а теперь можно выдохнуть...
Комментарии (84)

Krey
05.12.2025 16:07Я видел сам неоднократно бейсиковые блобы в бинарнике в школьные времена, до того как перейти на паскаль и буквально месяц два назад натыкался на каком-то ретро канале на ютубе на тоже самое. Будет время, проверю в досбоксе.
Единственное в чем может быть заквоздка, это в том что я имел ввиду qbasic из состава DOS, возможно это другой продукт

firehacker Автор
05.12.2025 16:07QBasic вообще не делал EXE-файлы.
QuickBasic делал, и это были настоящие полноценные полноправные EXE, как показано в статье.
Krey
05.12.2025 16:07Да, только что проверил. Ну то что у меня было - делало и именно так, как я описал. Что-то наверное из школы притащил. Уж извините, мне сейчас 45, тогда было 17, имею право что-то забыть. Попадётся ещё на просторах ютуба, допишу.

NickDoom
05.12.2025 16:07Я тоже отчётливо помню фейковые «ЕХЕ» с явно видимым исходным васичным кодом внутри. Причём они работали с точно такой же скоростью, что и просто васик.
Смутно помню, что при каких-то обстоятельствах получил код, летающий птичкой. Что-то ещё в голове промелькнуло, типа «о, начали реально компилировать, а не просто цеплять в хвост интерпретатору бейсичный код». Не был уверен в том, что правильно запомнил — но тут автор, похоже, подтвердил в своей статье то, что мне это не померещилось и память не переврала.
Ну, теперь подожду подтверждения первой части воспоминаний с Вашей стороны :) Будем искать, в какой момент произошла эта разница :)

artptr86
05.12.2025 16:07Может быть потому что Quick Basic мог сохранять исходники в своём бинарном формате?

Диалог сохранения файла

Oangai
05.12.2025 16:07в начале девяностых как-то попалась в руки демо версия довольно крутого по тем временам SPICE симулятора электронных схем, написанная на каком-то тогдашнем компилируещем бейсике. Все модели, символы и формулы были в комплекте, а демонстрационность реализовывалась тем что они вырезали логику ввода от пользователя и заменили на массив записанных комманд симуляции - движения мышью, нажатия клавиш итд. Помнится, я наверное пару недель пытался сломать этот код и переделать в более-менее употребимое состояние, но так и не смог, что-то он там слишком сложное для понимания накомпилировал, так обидно было. И 640кб памяти катастрофически не хватало, не влазило это всё туда вместе с дебаггером, но это всетаки удалось решить дополнительной EGA карточкой, подключал её память в дос как расширенние, всетаки влезло, хотя и не помогло в итоге. Своеобразные впечатления...

Krey
05.12.2025 16:07В те годы интерпретатор был слишком дорогим удовольствием.
Да, включите логику и объясните каким образом это дорогое удовольствие было в каждой 8ми битке, например в ZX80 с 4КБ памяти и сколько там, 16КБ ПЗУ ?!

firehacker Автор
05.12.2025 16:07Не жалко занять всю имеющуюся память интерпретатором, если по изначальной задумке это единственное, чем должно быть занято ПЗУ вашего изделия.

Krey
05.12.2025 16:07Пзу занимается много чем, речь ведь идёт о компьютере. Там куча системных функций зашита, особенно в случае ZX, в котором не было ни одного спец контроллера. Системный шрифт и прочие ресурсы.

rivo
05.12.2025 16:07Не жалко занять всю имеющуюся память интерпретатором
Еще один миф, интерпретатор копактнее нативного кода. Есть техника token threading или еще "p-code" называется. Инструкции могут 4 бита занимать, вызов функции 8 или 12 бит. Нативный вызов минимум 8 на иснтрукцию + 8 адрес.

firehacker Автор
05.12.2025 16:07Я не понимаю, с чем вы вспорите? Если вы предлагаете рассмотреть гипотетический вариант, где исполняемый файл содержит в себе прикладной код программы, представляемый в виде 4-битных элементов, полученных путём предварительной обработки исходного кода в момент компиляции, а также исполняющий системы, которая бежит по этой цепочке 4-битных элементов, то это не по адресу, потому что я не про такой подход говорил, что он дорогой до ресурсов. Нет смысла оспаривать то, чего я не говорил.
Я говорил, что что интерпрератор, который в момент выполнения прикладной программы будет интерпретировать исходный код прикладной программы с нуля, с сырого человеко-читаемого представления — вот он будет и медленный, и объямный по памяти.
Опять, когда вы говорите про 4-бита, про 8/12 на функцию — вы говорите о компактности представления пользовательского кода, а я говорю о [не-]компактности machinery, которая всё это делает.

artptr86
05.12.2025 16:07Кстати в случае с ZX Spectrum, в нём исходник фактически уже был частично токенизирован: в памяти вместо ключевых слов лежали однобайтные коды этих слов.
Поэтому, например, вместо
FOR a=1 TO 10 STEP 2в памяти лежалоEB 61 3D 31 CC 31 30 CD 32— интерпретатору требовалось распарсить только имена переменных и литералы. Это сильно упрощало интерпретацию и требовало существенно меньше памяти, чем иные промежуточные структуры.
yappari
05.12.2025 16:07Если быть точным, то рядом со всеми этими 31, 31 30 лежали ещё 0Е aa bb cc dd ee, то бишь дополнительно к текстовому представлению числа хранилось ещё и внутреннее численное, что память не экономило. Но слегка, видимо, ускоряло (но вряд ли сильно, математический движок или "калькулятор" там был очень тормознутым).

mlnw
05.12.2025 16:07в ZX80 с 4КБ памяти и сколько там, 16КБ ПЗУ ?!
Не 4, а 48 кб ОЗУ и 16 кб ПЗУ - самая распространенная версия

VelocidadAbsurda
05.12.2025 16:07Речь не о Spectrum. В ZX80 (он вышел за 2 года до Spectrum) памяти и того меньше, чем написали выше, 1к ОЗУ и 4к ПЗУ

NickDoom
05.12.2025 16:07…и таки срама, потому что драму к z80 цеплять ЕМНИП всё-таки требовало дополнительных усилий. Небесплатных.

checkpoint
05.12.2025 16:07Спасибо, что включили мой username в свою статью. Прочтя Вашу статью я сильно озадачился - не выжил ли я из ума под старость лет, ведь в начале 90-х я имел дело QBasic и отчетливо помню, что никакой он не компилятор, но в Вашей статье приводятся ясные доказательства обратного. Чтож, надо разобраться. Я пошел гуглить и выяснил следующее:
Сущетсвуют целых три версии BASIC-а с похожими названиями от компании Microsoft: GW-Basic, QBasic и QuickBasic.
В состав MS-DOS сначала входил GW-Basic, с версии 5.0 его заменили на QBasic.
QuickBasic продвигался как отдельный продукт и действительно является компилирующим: содержит среду, компилятор, линкер и отладчик.
Ни GW-Basic, ни QBasic не являются компилятором.
Ниже цитата с сайте Wikipedia: https://en.wikipedia.org/wiki/QuickBASIC
A subset of QuickBASIC 4.5, named QBasic, was included with MS-DOS 5 and later versions, replacing the GW-BASIC included with previous versions of MS-DOS. Compared to QuickBASIC, QBasic is limited to an interpreter only, lacks a few functions, can only handle programs of a limited size, and lacks support for separate program modules. Since it lacks a compiler, it cannot be used to produce executable files, although its program source code can still be compiled by a QuickBASIC 4.5, PDS 7.x or VBDOS 1.0 compiler, if available.
Моя ошибка в том, что QBasic вообще не мог генерировать .EXE файл.

NickDoom
05.12.2025 16:07Страшная мысль: а вдруг это была неофициальная, но очень популярная тулза? И она нам всем запомнилась как вот это вот «ха-ха, оно не компилирует, просто цепляет васик в хвост файла»?
Надо б древние диски глянуть, вдруг прочитаются…

checkpoint
05.12.2025 16:07Это какой-то
массовый психозглюк в пространственно-временном континууме. :)
artptr86
05.12.2025 16:07Эффект Манделы

NickDoom
05.12.2025 16:07Возможно. Но версия сторонней тулзы мне кажется более вероятной. BC она называлась, кажется. BC.EXE, не .COM.

artptr86
05.12.2025 16:07Для BC.EXE гуглится, что так, собственно, назывался компилятор бейсика в версиях MS Basic PDS и MS Visual Basic for DOS: https://ftp.zx.net.nz/pub/archive/ftp.microsoft.com/MISC/KB/en-us/84/469.HTM И судя по этой документации, это именно что полноценный компилятор, а не склеивалка исходника с интерпретатором.

fujikiriku
05.12.2025 16:07В состав MS-DOS сначала входил GW-Basic, с версии 5.0 его заменили на QBasic.
Если мне не изменяет память, в MS-DOS до версии 2.1 входил интерпретатор BASICA, который не был GW-Basic, и его функции зависели от версии MS-DOS

PerroSalchicha
05.12.2025 16:07Если мне не изменяет память, в MS-DOS до версии 2.1 входил интерпретатор BASICA,
Не совсем, BASICA, это IBM'овский Бейсик, он в MS-DOS не входил, поставлялся только с ранними IBM PC DOS, и его основная часть была прошита в ПЗУ IBM PC, на других машинах он даже и не работал.

victor_1212
05.12.2025 16:07Моя ошибка в том ...
Ваш ответ напомнил мне высказывание Л.Н.Королева типа "своими ошибками надо помогать своим ученикам", он принимал у меня несколько экзаменов в свое время, случалось и плавать :)

checkpoint
05.12.2025 16:07Был еще такой MS Visual Basic for MS-DOS (VBDOS), я скачал его инсталляшку и запустил в DosBox.

Microsoft, Visual Basic for DOS / 1992 В меню есть опция для генерации .EXE:

Я написал тестовую погу и заглянул в результирующий EXE. Если генерировать по второму варианту "EXE Requiring Run-Time Module", то получается коротенький файл похожий на "шитый код" в заголовке которого находится код подгрузки
интерпретаторавиртуальной машны из рядом лежащей библиотеки. Если выбрать первую опцию "Stand-Alone", то содержимое этой библиотеки включается в результирующий EXE, за которым следует все тот же "шитый код".

Siemargl
05.12.2025 16:07Как бы 3 этапа - разбор исходного текста - генерация промежуточного кода (p-code) - исполнение.
Можно p-code :
Не писать в файл и вообще не генерировать по AST в явном виде - 100% интерпретатор. Пример - классический бейсик.
Записать, и интерпретировать поочерёдно. Будет быстрее, т.к синтаксический разбор вычислительно непрост, но все равно это пошаговая интерпретация. Пример Forth - машина.
Записать и компилировать к исполнению в наивный только нужные куски - это JIT - компиляция, как Java или ранний .Net
-
Записать и компилировать полностью. Это компилятор. Например LLVM.
P-code соответственно называется немного по-разному - MSIL, Java byte code, LLVM IR, шитый код.

firehacker Автор
05.12.2025 16:07Как бы 3 этапа - разбор исходного текста - генерация промежуточного кода (p-code) - исполнение.
Это теоретизированный подход в духе «Как заработать миллиарды?»:
Придумать хитрый план
Начать работать по нему
???
Заработать миллиарды.
В случае VB/VBA промежуточных этапов и форм представления кода сильно больше, чем 3. Другой интересный факт состоит в том, что машинный код x86, когда он генерируется, не генерируется и P-кода, который при этом генерируется для работы проекта в режиме отладки (человек не произошёл от обезьяны, а человек и обезьяна происходят от общего предка)

Siemargl
05.12.2025 16:07Подробнее могу посоветовать только книжки по разработке компиляторов, я в три абзаца не уложился =)

firehacker Автор
05.12.2025 16:07Сам уже могу писать такие книжки ;-)
Ещё в школьные годы я разработал программный продукт, одной из фич которого было наличие встроенного ЯП и IDE (у меня он назывался XCode); это не было центральной фишкой той софтины, но это был как приятный бонус, как VBA в Excel — обычно вам должно хватить штатных возможностей Excel, но если вдруг не хватает, вы можете написать что-то нестандартное. За эту работу была получена золотая медаль Intel ISEF.

Siemargl
05.12.2025 16:07Проверить компилятор или интерпретатор проще по размеру бинарника или скорости исполнения. За базу по скорости можно взять аналогичный Паскаль или С код без оптимизации. Если драйстоун медленнее более чем в 10 раз, то это интерпретатор

morgot
05.12.2025 16:07В то, что visual basic настолько хорош, я узнал только от нашего общего знакомого лет 8 назад. До этого тоже был убежден, что там ни разу не натив. Хотя сам пишу на Си,Асм. Почему так - вероятно, общие дурацкие стереотипы, из каких-то статей для начинающих или холиваров, черт его знает..
Интересно другое:
Почему VB6 все же умер для M$,в плане, не получил развития далее? Фанатичная ставка на .NET?
Что думаете о проекте TwinBasic?

Siemargl
05.12.2025 16:07VB6 все же умер для M$
Ставка на dotNET
Сильная завязка на неудобные и сложные в разработке VBX, OCX
Так, для интереса, есть современный проект Xojo

firehacker Автор
05.12.2025 16:07VBX и OCX писать в одном месте вот так через запятую — это 5.
Это как сравнивать прерывания BIOS/DOS и WinAPI. VBX это устаревшая и узкопециализированная вещь. OCX — гениальная и крутая. Писать OCX на VB не неудобно и не сложно. Если вы собрались писать OCX на Си, это другой разговор, но в таком случае и реализовать дотнетовский элемент управления на Си будет ещё на пару порядков сложнее.

Siemargl
05.12.2025 16:07VBX и OCX писать в одном месте вот так через запятую — это 5.
И то и то является пользовательскими контролами для VB. OCX конечно новее и универсальнее.
Я знаю, как это разрабатывать. Долго и неудобно. Потому говорю и загнулось.

firehacker Автор
05.12.2025 16:07И то и то является пользовательскими контролами для VB
Только первое является пользовательскими контролами именно для VB. Второе является пользовательскими контролами для чего угодно на свете (если границами «белого света» считать мир Windows).

firehacker Автор
05.12.2025 16:07Думаю, что всё-таки внутри Microsoft всегда была предвзятость к VB как к побочному продукту существования VBA. Если смотреть на это через призму такого подхода, то для Microsoft продукт не умер, не прекратил развитие в 1998 — он продолжил развитие в виде всё новых и новых версий VBA. Просто к нему больше не прикручивали куперовский Ruby для получения побочного продукту/ответвления. А нишу этого ответвления решили закрыть дотнетом — Microsoft всегда хотела сделать свою Джаву, и вот наконец после Microsoft J++ получилась более удачная попытка.
В общем, на 85% это маркетинг, на 15% — технические сложности.Ничего хорошо (если рассматривать его как альтернативу или замену VB). Если просто рассматривать к какой-то ещё один бейсик — ради бога, больше бейсиков богу бейсиков.

Siemargl
05.12.2025 16:07он продолжил развитие в виде всё новых и новых версий VBA
Что? Vba6 вышла где то в 98, потом была встроенная в приложения Vba 7.1 с минимумом изменений. И всё.

firehacker Автор
05.12.2025 16:07С минимумом изменений? Они сделали 64-битную версию VBA, добавили тип данных LongLong и TDC для него. Это не минимум изменений, это очень сложная переботка, учитывая что виртуальная машина целиком и полностью была написана ассемблере и помимо неё в коде EB было очень много фрагментов, пришедших ещё с 16-битной эпохи.
Или претензия в чём? Вы не смотрите на 7.1, вы смотрите по билд-номерам библиотеки EB/VBA. Или по релизам офиса, в которых попадала VBA. Насколько таким образом VBA по дате своего последнего релиза пережила VB6 (98-й год)?
Кстати, и для VB6 последний релиз был вовсе не в 98-м году, потому что выпускались многочисленные SP к нему.

jobless
05.12.2025 16:07https://en.wikipedia.org/wiki/Dartmouth_Time-Sharing_System
Нужно смотреть на истоки. И там однозначно интерактивной была прородительница IDE, которая работала на одной системе а код для КОМПИЛЯЦИИ и выполнения отдавала другой.
Я давно живу и давно говорю, что так называемый "персональный компьютер" притормозил развитие индустрии программирования где то на четверть века. В 1985 в одном из моих проектов использовались асинхронный ввод вывод и многозадачность. А в 1995 нужно было придумывать всевозможные ухищрения над примитивной DOS
p.s. я это к тому, что BASIC родился не под dos и даже не под cp/m

checkpoint
05.12.2025 16:07p.s. я это к тому, что BASIC родился не под dos и даже не под cp/m
BASIC был создан из академического интереса, на больших ЭВМ, а применение нашел годы спустя на 8-ми битных и 16-ти битных персоналках с очень крохотной память и ограниченными аппаратными возможностями. На больших ЭВМ того времени BASIC был из разряда "не пришей рукав", он не мог конкурировать ни с FORTRAN-ом, ни с PL/I, ни с COBOL-ом ни даже с "Cи" на Мини.
BASIC использовался и как язык программирования и как операционная среда! Разработчики ПЭВМ использовали интерпретатор BASIC-а как замену операционной системы, которую, по большому счету, создать для таких ПЭВМ было невозможно из-за отсутствия внешних накопителей и малого обьема памяти. Это уже потом на 8-мибитках появятся привод гибких и жестких дисков, будет портирована CP/M и прочее DOS на её основе (или по мотивам), но в начале на этих машинках был только интерпретатор языка BASIC в различных плохосовместимых диалектах. И это был прогресс, потому как позволял использовать, пусть и убогую, ЭВМ простому Джону или Ивану у себя дома.

jobless
05.12.2025 16:07Очень они были большие
The DATANET-30 used magnetic-core memory with a cycle time of 6.94 μs. The word size was 18 bits and memory was available in sizes of 4K, 8K, or 16K words. The system could attach up to 128 asynchronous terminals, nominally at speeds of up to "3000 bits per second" (bit/s), but usually limited to the 300 bit/s supported by standard common-carrier facilities of the time, such as the Bell 103 modem.

NickDoom
05.12.2025 16:07Зависит от плотности кода. Если там что-то типа ЕС, у которой на все случаи математических операций отдельные команды и режимы (разбирали тут вроде недавно) — это слишком большая память, чтобы её экономить «методом Васика».
А если мы подключили 16 килослов по 16 (18 по понятной причине не поддерживаются) к 6502-му — есть вероятность, что Вася ещё может ограниченно приносить пользу. Но не так радикально, конечно, как в случае одной кильки рамы. Там или жизнь вся через васю-в-роме, или жизни в принципе нет :)

PerroSalchicha
05.12.2025 16:07Я давно живу и давно говорю, что так называемый "персональный компьютер" притормозил развитие индустрии программирования где то на четверть века.
Асинхронный ввод/вывод у персональных компьютеров был и в первом IBM PC, а многозадачность появилась физически в 1984-м, программную же поддержку получила в конце 1980-х. То, что у вас в 1995-м был проект без всего этого, это не проблема персональных компьютеров в целом, это проблема вашего проекта в частности.

SIISII
05.12.2025 16:07И вообще, не стоит смешивать наличие поддержки в железе и наличие поддержки в ОС. В Унихе, в частности, ввод-вывод синхронный (поэтому в ПОСИХе предусмотрели специальные расширения, поскольку без асинхронщины... тяжко временами), но это не из-за отсутствия железной поддержки на PDP-11 -- та же RSX-11 асинхронная насквозь. То же самое касается и ПК: и полноценная вытесняющая многозадачность, и асинхронный ввод-вывод технически были возможны и на оригинальном IBM PC, ну а что МС ДОС -- исключительно убогая никуда не годная система, это уже не вина железа (которое временами весьма кривое, но, тем не менее, достаточно функциональное).

victor_1212
05.12.2025 16:07по слухам то на чем собирали UNIX имело порядка 16КВ памяти, и вообще сначала это были части MULTICS min необходимые для одного пользователя, так что в swapping смысла не было, позже переписали на С и т.д., интересно что на q7 для sage swapping был (примерно 1958)

SIISII
05.12.2025 16:07Ну, Уних, как его обычно понимают, требовал полноценной ПДП-11 с полноценным ММУ, причём настолько полноценным, что у большинства ПДПшек его не было (большинство имело упрощённый вариант, не позволяющий реализовать совсем полноценную виртуальную память: требовалось держать все 64 Кбайта задачи в памяти целиком, а не отдельными страницами, поскольку в случае прерывания из-за отсутствия страницы не было возможности выполнить "откат" содержимого регистров после автоинкремента/автодекремента; в полноценном ММУ был специальный регистр, запоминающий изменения в регистрах, что позволяло ОС сделать откат для последующего повторения команды, вызвавшей прерывание).

victor_1212
05.12.2025 16:07посмотрел воспоминания иx начальника Sam Morgan, получается на раннем этапе они использовали GE635->PDP7->PDP11/20->PDP11/45, PDP7 это 9-144KB, типа слухам 16КВ не противоречит, интересно что первые 1-2 года руководство довольно прохладно относилось, машины использовали других отделов, также интересно, что с самого начала прицел был на использование языка высокого уровня, что более-менее понятно, т.к. люди пришли из группы MULTICS, где использовали в основном диалект PL1, также знали, что MCP для B5000 написана на диалекте ALGOL

Oangai
05.12.2025 16:07что особо интересно, так это тот факт что микрософт уже к 80му году портировала под 8086 и потом еще под несколько тогдашних архитектур популярный тогда AT&T юникс, назвала его Xenix и несколько лет неплохо продавала, тем кто мог себе позволить отвалить за профессиональную систему штуку баксов, в первой половине 80х это был вообще самый популярный юникс на рынке. Тоесть, технический бэкграунд и понимание того как должна выглядеть операционка у микрософта был хорошо до всей эпопеи с дос-ом, якобы написанном в самолёте перед презентацией. Поэтому вообще удивительно, как им совесть позволяла эту убогость столько лет всему миру впаривать.

PerroSalchicha
05.12.2025 16:07Поэтому вообще удивительно, как им совесть позволяла эту убогость столько лет всему миру впаривать.
А в чём убогость DOS была хоть? Это вполне себе обычная однопользовательская ОС на момент появления, и она вполне себе соответствовала своему назначению где-то до конца 1980-х. А там уже Майки и в полуось вкладывались, и в винду, и в NT.

SIISII
05.12.2025 16:07Подобная система уместна на 8-разрядном компутере с его жутко ограниченными ресурсами, поэтому я не бурчу против, скажем, CP/M. Но даже у самого первого IBM PC оператива могла иметь объём до 256 Кбайт (а технически -- и все 640, просто в варианте 1981 года не предусматривалось использование микросхем ОЗУ по 64 Кбита, а 16-Кбитных требовалось бы слишком много), а у его следующего варианта, как и всех последующих машин перешли на микросхемы в 64 Кбита и получили до тех самых пресловутых 640 Кбайт (у PC/XT 256 Кбайт был уже минимальный объём).
Для сравнения: моя любимая RSX-11 в полноценном варианте успешно работала на машине с ОЗУ в 248 Кбайт, причём под себя жрала порядка половины этого объёма -- и при этом была многозадачной многопользовательской системой, а не однопользовательским ублюдством, как МС ДОС, которая при этом жрала вполне сравнимый объём памяти (при этом пользуясь услугами ввода-вывода из БИОС, в то время как RSX-11 все драйверы включала в себя: в ПЗУ у PDP-11 был только начальный загрузчик). В общем, функционала у МС ДОС мало, а потребностей в памяти -- много; единственное, что у неё лучше, и то лишь отчасти -- это древовидная файловая система ("отчасти" по той причине, что у неё не было никаких прав доступа к файлам, в отличие от файловой системы RSX-11). Впрочем, написать драйвер файловой системы, подобной FAT12/16/32, для RSX-11 можно без особых проблем: система легко расширялась в любую сторону (драйверы файловых систем -- это драйверы режима пользователя, поэтому они не имели жёстких ограничений по размеру, в отличие от драйверов режима ядра).

PerroSalchicha
05.12.2025 16:07Но даже у самого первого IBM PC оператива могла иметь объём до 256 Кбайт
64К, если точнее. 256 - это уже у второго :)
Для сравнения: моя любимая RSX-11 в полноценном варианте успешно работала на машине с ОЗУ в 248 Кбайт, причём под себя жрала порядка половины этого объёма
Ну а DOS потребляла килобайт 60 в самых последних версиях, если в UMB её не прятать. И при запуске программ ещё и командный процессор из памяти выгружала. А более ранние версии DOS вообще в 30 кб укладывались, при этом уже имя поддержку жестких дисков и FAT16. Функции ПЗУ? Ну, ещё несколько килобайт, это ни о чём. Я не скажу по памяти, сколько там обработчики прерываний в оригинальной IBM PC занимают, но ПЗУ "Поиска" с поддержкой FDD и HDD, это 12К, из них 2К - это знакогенератор, плюс там же ещё начальная инициализация и POST, монитор и утилита форматирования жесткого диска. При этом ещё и код PDP-11 более компактный, чем х86. Поэтому вы сравниваете несравнимое - систему куда более легковесную и простую для персонального компьютера, с навороченной для компьютера многопользовательского. Причём с аппаратной поддержкой переключения задач.

SIISII
05.12.2025 16:0764К, если точнее. 256 - это уже у второго
Нет, именно 256 Кбайт у первого варианта, поскольку, помимо микросхем памяти на системной плате, сразу предлагались и дополнительные платы расширения.
Ну а DOS потребляла килобайт 60 в самых последних версиях, если в UMB её не прятать. И при запуске программ ещё и командный процессор из памяти выгружала.
Эти "килобайт 60" -- как раз при выгруженном командном процессоре, и его я не считаю (в RSX-11 его аналог, MCR -- обычная привилегированная задача, а соответственно, тоже может системой загружаться/выгружаться по мере необходимости). Верхнюю память не рассматриваем, так как это эксплуатация багофичи 80286, которой у 8086/88, понятное дело, не было (а на 80286 правильней было бы делать уже защищённый режим, если бы в Интеле сидели вменяемые архитекторы и обеспечили бы совместимость -- но делать нормально, как уже 10-15 лет делали другие, им религия не позволяла что тогда, что сейчас).
Кстати говоря, RSX-11 вполне можно было урезать до килобайт 30, причём совершенно штатными средствами -- хотя она всё равно оставалась многозадачной многопользовательской (но уже без защиты памяти, поскольку такой кастрированный вариант предназначался для машин без MMU). Ну а однозадачный монитор SJ в RT-11 вообще требует... не помню, но что-то вроде 5-6 килобайт под себя, его даже на БКшку с её 16 Кбайтами ОЗУ портировали.
Так что МС ДОС -- это исключительно малофункциональное поделие, занимающее для своего функционала крайне много места в ОЗУ.
Функции ПЗУ? Ну, ещё несколько килобайт, это ни о чём.
Это вполне "о чём", когда речь идёт о системах, занимающих 50-100 килобайт памяти, поскольку функции БИОСа вполне тянут на 10-15% от общего объёма кода.
При этом ещё и код PDP-11 более компактный, чем х86.
Неверно, код для 8086/88 зачастую более компактный, чем для PDP-11, если писать на ассемблере и достаточно качественно.
Поэтому вы сравниваете несравнимое - систему куда более легковесную и простую для персонального компьютера, с навороченной для компьютера многопользовательского. Причём с аппаратной поддержкой переключения задач.
Во-первых, я сравниваю сравнимое -- ОС для компьютеров с более-менее одинаковым объёмом памяти. "Персональность" вообще ни разу роли не играет, мне на персоналках под ДОСом было крайне некомфортно из-за того, что я лишён возможности запускать несколько задач параллельно, хотя ресурсы машины это позволяют.
Во-вторых, никакой аппаратной поддержки переключения задач в PDP-11 никогда не было. Она есть в 80286 и IA-32 (80386 и последующие в защищённом 16- или 32-разрядном режиме). И, более того, такая поддержка попросту не нужна, недаром та же Винда этой возможностью не пользуется и переключает задачи чисто программно, "ручками" сохраняя и восстанавливая контекст (и поэтому в 64-разрядном режиме эту аппаратную поддержку выпилили вместе с сегментацией и рядом других вещей).

PerroSalchicha
05.12.2025 16:07Нет, именно 256 Кбайт у первого варианта, поскольку, помимо микросхем памяти на системной плате, сразу предлагались и дополнительные платы расширения.
В то время это было физически невозможно при всём желании. Самая объемная и дорогая микросхема памяти - 4116, 16 килобит. Восемь микросхем на 16К, в плату расширения влезало четыре линейки, 64К. В IBM PC всего пять слотов расширения, из них три
Эти "килобайт 60" -- как раз при выгруженном командном процессоре

Вот чистая DOS 6.22 без всяких приблуд. Сколько вам там памяти оставит RSX-11? Так это, минуточку, самая последняя версия, вышедшая, когда память измерялась уже в мегабайтах, и уже используемая больше для поддержки винды, чем для автономной работы.
Во-первых, я сравниваю сравнимое -- ОС для компьютеров с более-менее одинаковым объёмом памяти. "Персональность" вообще ни разу роли не играет
Назначение компьютера - это его определяющая функция, а не объем памяти. Такой же объем памяти вы можете найти и у промышленного контроллера, и у сигнального процессора, но вы же не будете сравнивать их софт между собой. PDP-11, это архитектура, изначально создававшаяся под многопользовательскую работу, с поддержкой нескольких терминалов, и с узеньким каналом между ними и собственно компьютером. РС - это сугубо однопользовательская машина, в которой она сам себе терминал.
Во-вторых, никакой аппаратной поддержки переключения задач в PDP-11 никогда не было.
А разве MMU там не умел сохранять контекст регистров между переключением задач?

SIISII
05.12.2025 16:07В то время это было физически невозможно при всём желании. Самая объемная и дорогая микросхема памяти - 4116, 16 килобит. Восемь микросхем на 16К, в плату расширения влезало четыре линейки, 64К. В IBM PC всего пять слотов расширения, из них три
Читайте техописание на IBM PC от августа 1981 года, где английским по белому написано:


Таким образом, имеем до 64 Кбайт на системной плате и по 64 Кбайта на каждой плате расширения, что даёт 256 Кбайт при трёх платах (ещё два разъёма расширения уходят на один или два видеоадаптера или видеоадаптер и ещё что-нибудь -- контроллер флопов, как правило).
И, кстати, микросхемы по 64 Кбита уже производились -- просто цена одного бита была выше, чем в 16-килобитных, поэтому для удешевления использовали последние. Но уже в варианте 1983 года перешли на 64-килобитные.
Вот чистая DOS 6.22 без всяких приблуд. Сколько вам там памяти оставит RSX-11?
Так это зависит от исходного объёма памяти. Система в полном варианте под себя и драйверы сожрёт 100-120 килобайт, остальное свободно (т.е. свободным будет порядка 120 килобайт на наиболее распространённых машинах с 248 Кбайтами ОЗУ, но почти 2 Мбайта на старших моделях с 2 Мбайтами ОЗУ). И снова повторюсь: у МС ДОС почти никаких функций нет, по большому счёту, она обеспечивает лишь работу с файлами. А RSX-11 -- многозадачная многопользовательская даже в минимальном варианте, с поддержкой асинхронного ввода-вывода, таймерных событий, обмена данными (сообщениями) между задачами и т.д. и т.п. Так что МС ДОС по набору функционала уместней сравнивать с RT-11, однозадачный монитор которой (SJ) занимал только 4 Кбайта и мог использоваться на машинах, начиная с ОЗУ в 16 Кбайт, а двухзадачный (FB) -- 8 Кбайт. Вот и сравните эффективность реализации этих систем.
Назначение компьютера - это его определяющая функция, а не объем памяти.
И IBM PC, и PDP-11 -- универсальные вычислительные машины, степень их "персональности" не играет вообще никакой роли. Более того, в линейке PDP-11/LSI-11 полно компьютеров, которые сейчас назвали бы персональными, включая самую первую модель -- PDP-11/20, появившуюся в 1969 году (или, например, наши ДВК). Ресурсы же, и, в первую очередь, объём оперативной памяти, определяют набор функций, которые способна потянуть данная машина. IBM PC тянуть могла больше, чем большинство PDPшек, а PC/AT -- чем любые PDPшки и ряд 32-разрядных VAX-11, однако они оставались крайне убогими в силу ужасающей убогости их программного обеспечения. Ещё раз повторюсь: система уровня MS DOS -- это удел 8-разрядных компьютеров, поскольку MS DOS не имеет принципиальных отличий от CP/M по своему функционалу, но её сделали системой для намного более мощных машин.
А разве MMU там не умел сохранять контекст регистров между переключением задач?
MMU делало то, что должно делать, -- отображало виртуальные адреса на физические, и ничего сверх этого. При переключении задач ОС должна была перепрограммировать регистры отображения MMU, чтобы обеспечить переход к другому адресному пространству. Регистры самого процессора всегда сохранялись-восстанавливались ручками. Плюс, MMU отсутствовало на всех LSI-11 и на изрядной части PDP-11, что не мешало гонять на них многозадачные системы при наличии достаточно большого объёма ОЗУ (32-56 Кбайт в зависимости от ОС).

PerroSalchicha
05.12.2025 16:07Таким образом, имеем до 64 Кбайт на системной плате и по 64 Кбайта на каждой плате расширения, что даёт 256 Кбайт при трёх платах
Ну ок, но у вас там нет трех свободных разъемов :) У вас там видеокарта, контроллер флопов и контроллер портов ввода-вывода стоят. Три свободных слота, это только в конфигурации с кассетным магнитофоном. И опять же таки, DOS разрабатывалась таким образом, чтобы вы могли работать на компьютере с 64К, не покупая за штуку баксов ещё и плату расширения памяти.
снова повторюсь: у МС ДОС почти никаких функций нет, по большому счёту, она обеспечивает лишь работу с файлами.
Ну здрасьте :) Кроме собственно обвеса файловой системы - управление потоками ввода-вывода, IOCTL, поддержка оверлеев, поддержка резидентов, консольный ввод-вывод, управление кодовыми страницами, управление памятью, поддержку сетевых драйверов. Она не такая уж простая, как вам кажется. Просто у вас не было необходимости в неё погружаться.
IBM PC тянуть могла больше, чем большинство PDPшек, а PC/AT -- чем любые PDPшки и ряд 32-разрядных VAX-11
Так она и тянула реально больше. Только в других задачах. Много ли на PDP-11 было штук вроде Lotus 1-2-3, P-CAD, AutoCAD, Microsoft Word, dBase III и т.д.?

SIISII
05.12.2025 16:07Ну ок, но у вас там нет трех свободных разъемов :) У вас там видеокарта, контроллер флопов и контроллер портов ввода-вывода стоят. Три свободных слота, это только в конфигурации с кассетным магнитофоном. И опять же таки, DOS разрабатывалась таким образом, чтобы вы могли работать на компьютере с 64К, не покупая за штуку баксов ещё и плату расширения памяти.
У оригинальной IBM PC в сумме пять разъёмов расширения, так что при необходимости впихнуть 256 Кбайт было возможно уже в первый вариант, о чём прямо говорит техописание (и показывает необходимые для этого положения переключателей).
Ну и, кроме того, брать 16-разрядный компьютер чисто чтоб дома поиграть, особого смысла тогда (в 1981-82 годах) не было -- для этого существовали очень неплохие 8-разрядные компы, которые ещё долго оставались вполне себе конкурентоспособными в этой области. 16-разрядный же, да ещё от IBM, -- это, в первую очередь, для бизнеса, где вопрос цены так остро не стоит; для домашнего применения -- это уже вторично (предусмотрели подключение к телевизору -- но с потерей качества изображения, чуть позже сделали обрубок PCjr, который не взлетел).
И да, PDPшки ж тоже начинались не с 2 Мбайт ОЗУ, а с, кажется, 16 Кбайт, а из всей периферии изначально были только телетайп и перфолента -- поэтому ДЕК и выкатила за примерно пять лет целый ворох ОСей, начиная с перфоленточной и кончая как раз RSX-11. Так что, если говорить об ОС, то для ранних IBM PC с малым объёмом памяти (например, 64 Кбайта только на системной плате) MS DOS, как мы её знаем, была бы разумным вариантом (как CP/M была нормальной на 8-разрядных компьютерах с аналогичным объёмом памяти), но вот на немного более поздних, где 256 и больше килобайт стали нормой, для эффективной работы явно требовалась более мощная и удобная система -- а её не было ещё лет 10 (по большому счёту, до появления более-менее пригодной для использования Винды 3.1, если говорить о потребительском секторе, ну а первой по-настоящему нормальной системой из мелкомягких стала лишь WinNT 4 -- а это уже 32-разрядные компы).
Ну здрасьте :) Кроме собственно обвеса файловой системы - управление потоками ввода-вывода, IOCTL, поддержка оверлеев, поддержка резидентов, консольный ввод-вывод, управление кодовыми страницами, управление памятью, поддержку сетевых драйверов. Она не такая уж простая, как вам кажется. Просто у вас не было необходимости в неё погружаться.
Управления памятью и резидентами там, по большому счёту, нет -- в силу однозадачности системы (а возможность откусить часть памяти в верхних адресах имеется и без всякой системной поддержки). Это вам не возможность создавать/уничтожать разделы памяти, загружать/выгружать задачи и совместно используемые библиотеки и т.д. и т.п., что было в RSX-11.
Насчёт поддержки оверлеев именно в МС ДОС (в самой системе) я, честно говоря, не помню, хотя мне кажется, что поддерживала их не система, а сами программы собственными силами; но вполне может быть, что за давностью лет ошибаюсь. В RSX-11 она точно была на уровне собственно ОС (и её штатного компоновщика, который и собирал оверлейные задачи под чутким руководством скрипта, описывающего оверлеи) и использовалась интенсивнейшим образом для "тяжёлых" задач; компилятор Кобола так ващще из одних оверлеев состоял :) В частности, для систем с малым объёмом ОЗУ можно было драйвер файловой системы (задача FOCACP) собрать как жутко оверлейный, но, есно, и жутко тормозной, а вот на системе, где памяти достаточно много, собрать его вообще без оверлеев. На системах с MMU и большим свободным объёмом ОЗУ (скажем, на PDP-11/70 или нашей СМ-1420, где типичным был объём физической памяти 2 Мбайта, хотя встречались и варианты с 248 Кбайтами) система умела использовать "оверлеи в памяти": обычные оверлеи грузить сразу в физическую память, а переключаться между ними по запросам задачи (по сути, по запросам кода, сформированного компоновщиком в необходимых местах задачи) с помощью регистров MMU без дополнительных обращений к диску, что, естественно, в 100500 раз быстрей.
Всё остальное, что Вы упомянули, -- это ввод-вывод, причём, в общем и целом, куда более примитивный, чем в RSX-11 (попробуй без грязных хаков запустить параллельно несколько операций ввода-вывода и продолжать при этом свою работу, отвлекаясь для обработки завершения одной операции и запуска следующей). Разве что с поддержкой сетей, наверное, в МС ДОС было лучше, но это уже следствие разницы эпох: в 1970-е, когда RSX-11 была создана и развивалась, сети только-только начинали появляться и оставались ещё чисто полем для экспериментов. Ну, ещё кодовые страницы, коих в RSX-11 не было -- но в те годы и чисто в Америке в этом нужды не было, а о других странах особенно не думали (а у нас, украв системы, сделали кучу извращённых и не совместимых друг с другом кодировок, из-за чего и пошли всякие IНЖАЛИД ДЕЖИЦЕ -- но это уже вопрос к нашим "гениям", а не к ДЕКовским системам).

victor_1212
05.12.2025 16:07overlay кода для DOS были, типа специальный linker от независимых компаний за разумную цену, плюс текстовый файл конфигурации, и работает более-менее автоматически, по памяти довольно сложные конфигурации приходилось видеть с десятками областей overlay

SIISII
05.12.2025 16:07Нет, что оверлейные программы в МС ДОС были, я знаю -- просто мне кажется, что система это никак не поддерживала, и они реализовались полностью сторонними средствами (в отличие от RSX-11 или там OS/360, где это было на уровне штатных средств самой ОС и её штатного компоновщика).

PerroSalchicha
05.12.2025 16:07Нет, что оверлейные программы в МС ДОС были, я знаю -- просто мне кажется, что система это никак не поддерживала, и они реализовались полностью сторонними средствами
В структуре EXE-файла в описании сегмента есть его оверлейный идентификатор, а в DOS API есть функция Load Overlay. Другое дело, что DOS никак не запрещает использовать другие оверлейные менеджеры.

PerroSalchicha
05.12.2025 16:07У оригинальной IBM PC в сумме пять разъёмов расширения, так что при необходимости впихнуть 256 Кбайт было возможно уже в первый вариант
Ну только система будет не функциональной. Или без видео, или без дисков, или без портов ввода-вывода.
И да, PDPшки ж тоже начинались не с 2 Мбайт ОЗУ, а с, кажется, 16 Кбайт
Само собой. Но и RSX-11 у них в такой конфигурации ни в каком виде быть не могло. А вот PC DOS даже на 16К вполне себе запускалась, хотя смысла в этом особого не было.
но вот на немного более поздних, где 256 и больше килобайт стали нормой, для эффективной работы явно требовалась более мощная и удобная система
Неа, в большинстве случаев совсем наоборот. Память - самый ценный ресурс. Все эти трюки MS DOS вроде упрятывания её в верхние блоки, это всё ради экономии килобайт и килобайт памяти для прикладного софта. Вы же поймите, мир PDP-11 был совсем другим. У вас там софтины - это текстовые утилитки, часто с одним входом и выходом, обычно укладывающиеся в 64К. Они простые, зато их много. Мир PC - это одиночные, но зато крупные интерактивные и многофункциональные приложения. Им нужно как можно меньше ОС, но зато как можно больше памяти.
Там, где 256К и DOS, вы можете и писать софт в Турбо Паскале, и проектировать простые схемы в PCad, и детали в Автокаде. Там, где у вас была бы чудесная возможность переключаться между Автокадом и чем-то ещё (а чём, кстати? Не браузером же, и не плейером?), у вас бы не работал Автокад.

Siemargl
05.12.2025 16:07В мсдос своей сети не было. появилась такая поддержка только в win 3.11
Требовались отдельные внешние программные пакеты типа msnet или lantastic

Oangai
05.12.2025 16:07есть определённая разница между стартапом с двумя разрабами выкатывающим фичи на пределе своих возможностей и способностей, и уже устоявшейся компанией с продаваемым за хорошие деньги серьёзным продуктом, пусть даже они его не сами разработали а только портировали - опыт был, масса готового кода и инфраструктура, неплохие машинки VAX/11 для сборки у них уже в компании использовались.
Аргументы, которые приводились почему Xenix не годится для PC, может быть и имели смысл в самом начале эпопеи, когда IBM сам не знал что хочет, если расчитывали продавать машинки для бизнеса с 16-64кб оперативы. Но ведь очень скоро, буквально через год-два, они вполне доросли до запросов Xenix - 256кб и жесткий диск, и с того момента не было больше никакой причины кроме чисто маркетинговой, почему бы например в поставку DOS не включить редактор vi и менеджер терминалов типа screen, что бы сразу сделало систему значительно удобнее для пользователей, и практически без затрат со стороны разработчика, потому что весь код у них уже был. Но это бы повредило имиджу дорогого продукта.
В результате, сами MS во все времена присутствовали в экосистеме юникса и были в курсе любого прогресса, однако для пользователей DOSа создали свою экосистему и старательно их от мира юникса изолировали. Ибо нефиг нищебродам делать удобно, а то элита посмотрит и дорогой продукт покупать перестанет.

PerroSalchicha
05.12.2025 16:07почему бы например в поставку DOS не включить редактор vi и менеджер терминалов типа screen
Ответ тут лежит на поверхности. В ранних версиях DOS железо тогдашних PC редактор vi вряд ли бы осилило. Его и железо PDP-11 не осиливало уже в 1980-е. А куцый малофункциональный редактор похожей парадигмы в DOS и так был. В поздних версиях DOS появился уже нормальный обычный редактор Edit.
Что касается менеджера терминалов, у меня вообще загадка: для каких задач эта штука могла бы пригодиться пользователю компьютера, на котором работа не предполагает повседневного использования вороха консольных утилит?

victor_1212
05.12.2025 16:07MS к 80 году портировала популярный AT&T UNIX ...
точнее MS купила лицензию на старый код у AT&T c условием выплаты royalty fee с каждой проданной копии, и рассчитывала вместе с SCO и др. хорошо заработать на porting и перепродаже UNIX, но AT&T их всех слегка кинула выпустив новую версию UNIX System III и позднее System V чтобы продавать напрямую, MS пыталась конкурировать выпустив Xenix System V, но решила бросить это дело и заняться OS/2 вместе с IBM, права на Xenix были переданы SCO которая фактически делала большую часть работы по porting на 286, она продолжала работы по Xenix дальше, с 1985 MS стала работать с IBM по OS/2, чем это закончилось здесь уже обсуждалось

VBDUnit
05.12.2025 16:07Что интересно — в VB6 то, что скомпилировано в псевдокод (P‑Code) на современных компах работает гораздо быстрее, чем то, что скомпилировано в Native Code. Чуть ли не на порядок. По крайней мере так было на всех компах, где я тестил (Win7/8/8.1/11).


artptr86
05.12.2025 16:07ChatGPT считает, что это потому, что P-код компактнее, а потому попадает в L1 и L2-кеши, современные процессоры оптимизированы под исполнение инструкций виртуальной машины и её предсказуемого цикла работы, сама виртуальная машина MSVBVM60.dll — хорошо оптимизирована внутри, цикл диспатча укладывается в L1/L2. В то же времчя нативный код, который порождал компилятор VB6, оптимизирован под Pentium 1/2, а не под современные процессоры, распыляет логику по разным веткам, неэффективно использует регистры процессора и т.д. В итоге нативность кода тонет в промахах кеша, пайплайна и предсказания ветвлений.

PerroSalchicha
05.12.2025 16:07В итоге нативность кода тонет в промахах кеша, пайплайна и предсказания ветвлений.
Я не думаю, что можно в разы ухудшить работу нативного кода по сравнению с виртуальной машиной путём его какой-то там оптимизации под полтора прямолинейных конвейера первопня. И если оно всё влезло в кеш вместе с исполняющей средой, то нативно уж точно влезет. Тут больше вопрос в том, что этот код там такого делает?

firehacker Автор
05.12.2025 16:07ChatGPT не далёк от истины. Каждой P-code-ной инструкции соответствует кусочек машинного кода виртуальной машины, имплементирующий функциональность этой инструкции. Эти кусочки очень компактны, состоят всего из нескольких машинных инструкций, они написаны на MASM-е, то есть это даже не процедуры/функции — у них нет прологов, у них нет эпилогов, то есть нет сохранения регистров (которые соглашение о вызове предписывает оставлять нетронутыми), нет восстановления регистров в конце, нет резрвирования места в стеке под локальные переменные. В них есть только сама суть, так как они написаны на ассемблере. Поэтому они очень компактны.
И поскольку они очень компактны (а также из-за того, что для реально одинаковых поддействий из таких кусочков всё-таки вызываются классические процеды), реализация самых «популярных» P-кодных инструкций очень быстро попадает в кеш процессора. P-кодый код, если там выполняюся одни и те же процедуры VB-проекта, или много циклов, попадают в кеш данных процессора.
Таким образом, для процессора выполнение большей части VB-кода превращается в исполнение уже очень хорошо знакомых (с точки зрения кеша) макро-блоков.

Siemargl
05.12.2025 16:07Зачем здесь нужен твой ответ, если есть ЧатГПТ?
Тем более, как и обычно у ИИ, это наполовину чушь.

PerroSalchicha
Спасибо за исследование, но я всё-таки был прав. Компиляция в нативный код в Visual Basic появилась только в VB5, до этого он EXE собирал из исполняющей среды и исходников, точнее, не исходников, а байт-кода, в который он их преобразовывал.
DancingOnWater
Тогда любой современный компилятор можно назвать интерпретатором.
А если вспомнить, что в любом x86 современном процессоре встроен преобразователь cisc команд во внутреннее risc, то можно дойти до того что ни один язык не является компилируемым.
На самом деле у компилятора и интерпретатора есть коренное различие. У второго ошибка синтаксиса языка - ошибка времени исполнения программы. При компиляции анализ на корректность синтаксиса происходит на этапе сборки программы.
firehacker Автор
Обычно говорят «точнее», когда хотят уточнить и без того достаточно точное первое утверждение. Например «она была зелёного цвета, точнее фисташкового». Его странно видеть в предложениях типа «он был чёрным, точнее белым» и т.п.
Исходники и байт-код — на двух диаметрально противоположенных концах жизненного цикла программного продукта. Это руда и экскаваторы. Мука и булочки.
Если вы понимаете, что там не исходники, а байт-код, то почему называете это «интерпретируемым языком»? Точно так же работает дотнет, точно так же работает Java. Никто не говорит, что Java интерпретируемая, что C# интерпретируемый, что VB.NET интерпретируемый.
Python, PHP и современный JS тоже генерирует байт-код и выполняют его на своей виртуальной машине. Но они это делают в момент запуска и на конечном устройстве. В случае VB это было делалось один раз, при компиляции и происходило на машине разработчика.
Даже если бы до появления VB5 там реально вшивался бы исходный код (что not the case, как мы выяснили, исходный код не вшивался никогда), почему-то никто никогда не делает эту ремарку с указанием версии, хотя, грубо говоря, 10 лет генерации нативного машинного кода не было, после чего 20 лет эта генерация была — то есть она была бОльшую часть времени, но этого факта как будто бы не существует, зато положение дел, которое существовало меньшую часть времени, акцентируется и извращается до непомерных масштабов (готовый оптимизированный байт-код для виртуальной машины выдаётся за исходный код).
PerroSalchicha
Нет, вообще нет. Байт-код, это нечто посередине между исходниками и машинным кодом. Промежуточный набор инструкций, который получился после того, как отработал парсер синтаксиса, но ещё не пригодный для исполнения на целевой машине. Чтобы он исполнился, нужен либо
Собственно компилятор, который соберёт пригодный для исполнения машинный код
Среда исполнения с JIT-компилятором, которая будет собирать пригодный для исполнения машинный код прямо перед выполнением конкретных подпрограмм.
Интерпретирующая среда исполнения, которая будет читать байт-код и медленно и уныло исполнять это по шагам.
Так вот, классический VB - это как раз третье.
Первая версия VB вышла в 1991, генерация появилась в 1997, последняя версия вышла в 1998. Он не так долго успел побыть зрелым инструментом разработки, чтобы это вошло в историю.
firehacker Автор
Медленно и уныло — это как раз второй вариант.
С какой стати 3-й вариант медленный и унылый? Пруфы? Доводы?
Я что-то не понял: когда в 1998 вышла последняя версия, возможность компилировать в Native-код убрали? Да? Нет. В 1998 ничего не изменилось. Поэтому на временной шкале если и отмечать точки переломных моментов, то 1998 год как точка явно вообще не должен был отмечен.
PerroSalchicha
Конкретно в 1998 ничего не изменилось, но я нигде и не писал, что что-то в 1998 изменилось. Изменилось чуть позже, в 2001-м, когда вышла новая платформа, более не совместимая со старой, а Майкрософт дала понять, что VB - продукт теперь устаревший, и пора мигрировать.
SIISII
Изначальная Жаба, насколько помню, была двухстадийной: первая стадия -- это компилятор из исходного текста в байт-код, а вторая стадия -- это интерпретация байт-кода (именно интерпретация). Более поздние реализации -- тоже двухстадийные, но второй стадией является уже компиляция байт-кода в машинный код, но только тогда, когда это понадобилось (компиляция прямо во время выполнения по мере необходимости). Но, естественно, принципиально нет проблем компилировать жабу сразу в машинный код -- но тогда не получится распространять жаба-программы в типа готовом виде, а только в виде исходников.
Что же до Бейсика, то, хотя классические (именно классические, 70-х годов) диалекты сего языка почти всегда интерпретировались, технически они могли и компилироваться. И компиляторы таки были уже тогда; скажем, под RSX-11 на PDP-11 был интерпретатор одного диалекта Бейсика и компилятор совершенно другого диалекта, не совместимого с первым (ну, принт хелловорлд и там, и там будет работать одинаково, но вот практически всё остальное было очень разным).