Как работал 16-битный компилятор C? - PullRequest
14 голосов
/ 30 сентября 2010
Модель памяти

C с использованием арифметики указателей и всего, похоже, моделирует плоское адресное пространство. 16-битные компьютеры использовали сегментированный доступ к памяти. Как 16-битные компиляторы C справились с этой проблемой и симулировали плоское адресное пространство с точки зрения программиста C? Например, примерно на каком языке ассемблера будет скомпилирован следующий код на 8086?

long arr[65536];  // Assume 32 bit longs.
long i;
for(i = 0; i < 65536; i++) {
    arr[i] = i;
}

Ответы [ 6 ]

13 голосов
/ 30 сентября 2010

Как 16-битные компиляторы C справились с этой проблемой и симулировали плоское адресное пространство с точки зрения программиста C?

Они этого не сделали.Вместо этого они сделали сегментацию видимой для программиста на C, расширив язык с помощью нескольких типов указателей: near, far и huge.Указатель near был только смещением, в то время как указатели far и huge были объединенным сегментом и смещением.Был параметр компилятора для установки модели памяти , которая определяла, был ли тип указателя по умолчанию близким или дальним.

В коде Windows, даже сегодня, вы часто будете видеть typedefs вроде LPCSTR (для const char*).«LP» - это пережиток 16-битных дней;это означает «Длинный (дальний) указатель».

9 голосов
/ 30 сентября 2010
Модель памяти

C ни в коем случае не подразумевает плоское адресное пространство. Этого никогда не было. Фактически, спецификация языка C специально разработана для того, чтобы разрешать не плоские адресные пространства.

В самой простой реализации с сегментированным адресным пространством размер самого большого непрерывного объекта будет ограничен размером сегмента (65536 байтов на 16-битной платформе). Это означает, что size_t в такой реализации будет 16-разрядным, и что ваш код просто не будет компилироваться, поскольку вы пытаетесь объявить объект, размер которого больше допустимого максимума.

Более сложная реализация будет поддерживать так называемую огромную модель памяти. Видите ли, действительно нет проблем с обращением к непрерывным блокам памяти размером любого в модели с сегментированной памятью, это просто требует дополнительных усилий в арифметике указателей. Таким образом, в рамках модели огромной памяти реализация потребует дополнительных усилий, которые сделают код немного медленнее, но в то же время позволят обращаться к объектам практически любого размера. Таким образом, ваш код будет прекрасно скомпилирован.

9 голосов
/ 30 сентября 2010

Истинные 16-битные среды используют 16-битные указатели, которые достигают любого адреса.Примеры включают PDP-11, семейство 6800 (6802, 6809, 68HC11) и 8085. Это чистая и эффективная среда, как и простая 32-разрядная архитектура.

Семейство 80x86, навязанное намгибридное 16-битное / 20-битное адресное пространство в так называемом «реальном режиме» - собственное адресное пространство 8086.Обычный механизм для решения этой проблемы заключался в расширении типов указателей на два основных типа: near (16-разрядный указатель) и far (32-разрядный указатель).Значение по умолчанию для указателей кода и данных может быть установлено массово с помощью «модели памяти»: tiny, small, compact, medium, far и huge (некоторые компиляторы не поддерживают всемодели).

Модель памяти tiny полезна для небольших программ, в которых все пространство (код + данные + стек) меньше 64 КБ.Все указатели имеют (по умолчанию) 16 бит или near;указатель неявно связан со значением сегмента для всей программы.

Модель small предполагает, что стек данных + меньше 64 КБ и находится в том же сегменте;Сегмент кода содержит только код, поэтому может иметь до 64 КБ и максимально использовать 128 КБ памяти.Указатели кода near и неявно связаны с CS (сегмент кода).Указатели данных также near и связаны с DS (сегмент данных).

Модель medium имеет до 64 КБ стека данных + (как маленький), но может иметь любой объем кода.Указатели данных являются 16-битными и неявно связаны с сегментом данных.Кодовые указатели являются 32-битными far указателями и имеют значение сегмента в зависимости от того, как компоновщик установил группы кодов (неприятная бухгалтерия).

Модель compact является дополнением среднего: меньшечем 64 КБ кода, но любой объем данных.Указатели данных - far, а указатели кода - near.

В large или huge модели, подтип по умолчанию для указателей 32-разрядный или far.Основное отличие состоит в том, что огромные указатели всегда автоматически нормализуются, так что их увеличение позволяет избежать проблем с переходом по 64К.См это .

6 голосов
/ 30 сентября 2010

В DOS 16 бит, я не помню, чтобы я мог это сделать. У вас может быть несколько вещей, каждая из которых имеет размер 64 КБ (потому что сегмент может быть скорректирован, а смещение обнулено), но не помните, можно ли пересечь границу одним массивом. Простое пространство памяти, в котором вы могли бы умышленно выделять все, что вы хотели, и вбирать в массив столько, сколько захотите, не происходило, пока мы не смогли скомпилировать 32-битные программы DOS (на 386 или 486 процессорах). Возможно, другие операционные системы и компиляторы, кроме Microsoft и Borland, могут генерировать плоские массивы размером более 64 КБ. Win16 Я не помню эту свободу до тех пор, пока не победил win32, возможно, моя память стала ржавой ... Вам все-таки повезло или вы имели богатый мегабайт памяти, машина размером 256 или 512 Кбайт не была неслыханной. В конечном итоге ваш дисковод имел долю от мегабайта до 1,44 мегабайта, а на жестком диске - дюжину или несколько мегабайт, так что вы просто не рассчитывали такое большое количество раз.

Я помню конкретную проблему, которую я узнал о DNS, когда вы могли загрузить всю базу данных DNS всех зарегистрированных доменных имен на планете, фактически вам пришлось установить собственный DNS-сервер, который почти требовался в то время, чтобы есть веб-сайт. Этот файл был 35 мегабайт, а мой жесткий диск был 100 мегабайт, плюс DOS и Windows, пережевывая некоторые из этого. Вероятно, имел 1 или 2 мегабайта памяти, мог бы в то время выполнять 32-битные программы. Часть, если бы я хотел проанализировать файл ascii, который я сделал за несколько проходов, но каждый проход вывод должен был идти в другой файл, и мне пришлось удалить предыдущий файл, чтобы иметь место на диске для следующего файла. Два дисковых контроллера на стандартной материнской плате, один для жесткого диска и один для дисковода компакт-дисков, и здесь этот материал был не дешевым, не было много свободных слотов isa, если бы вы могли позволить себе другой жесткий диск и плату контроллера диска.

Была даже проблема чтения 64 Кбайт с помощью C, который вы передали fread с количеством байтов, которые вы хотели прочитать в 16-битном int, что означало от 0 до 65535, а не 65536 байт, и производительность резко упала, если вы не читали в четном размере секторов, так что вы просто читаете 32 Кбайт за раз, чтобы максимизировать производительность, 64 Кб не доходили до тех пор, пока не прошли дни dos32, когда вы окончательно убедились, что значение, передаваемое в fread, теперь стало 32-битным числом, и компилятор не собирался отрубать верхние 16 биты и использовать только младшие 16 бит (что часто случается, если вы использовали достаточно компиляторов / версий). В настоящее время мы сталкиваемся с аналогичными проблемами при переходе с 32-разрядного на 64-разрядный режим, как и при переходе с 16-разрядного на 32-разрядный. Что наиболее интересно, так это код от таких людей, как я, который узнал, что переход с 16 на 32 бита int изменил размер, но беззнаковые char и unsigned long этого не сделали, поэтому вы адаптировали и редко использовали int, чтобы ваши программы компилировались и работали для и 16 и 32 бит. (Код от людей этого поколения выделяется среди других людей, которые тоже пережили его и использовали тот же трюк). Но для перехода с 32 на 64 все наоборот, и код, не подвергнутый рефакторингу для использования объявлений типов uint32, страдает.

Читая ответ Уоллика, который только что пришел, огромная вещь-указатель, обернутая вокруг, звонит в колокольчик, также не всегда имея возможность компилировать для огромных. маленькой была плоская модель памяти, с которой нам сегодня комфортно, а с сегодняшним днем ​​это было легко, потому что вам не приходилось беспокоиться о сегментах. Так что было желательно собрать для малого, когда вы могли. У вас все еще не было много памяти, дискового или дискового пространства, поэтому вы обычно не имели дело с такими большими данными.

И, согласившись с другим ответом, смещение сегмента составляло 8088/8086 Intel. Intel еще не доминировала во всем мире, поэтому существовали и другие платформы, которые имели ограниченное пространство памяти или использовали другие хитрости, возможно, в аппаратном обеспечении (вне процессора) для решения проблемы. Из-за сегмента / смещения Intel смогла ездить на 16-битной вещи дольше, чем она, вероятно, должна была бы. Сегмент / смещение имели некоторые интересные и интересные вещи, которые вы могли бы сделать с ним, но это было так же больно, как и все остальное. Вы либо упростили свою жизнь и жили в плоском пространстве памяти, либо постоянно беспокоились о границах сегментов.

4 голосов
/ 30 сентября 2010

Действительно определить размер адреса на старых x86 довольно сложно. Вы можете сказать, что это 16 бит, потому что арифметика, которую вы можете выполнить с адресом, должна помещаться в 16 битный регистр. Можно также сказать, что он 32-битный, потому что фактические адреса вычисляются по 16-битному регистру общего назначения и 16-битному сегментному регистру (все 32 бита значимы). Вы также можете просто сказать, что это 20 бит, потому что регистры сегментов сдвинуты на 4 бита влево и добавлены в регистры gp для аппаратной адресации.

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

2 голосов
/ 30 сентября 2010

Проверьте эту запись в Википедии. О дальних указателях. По сути, можно указать сегмент и смещение, что позволяет перейти к другому сегменту.

...