Какие утверждения верны для компактной модели памяти
Обновлено: 22.11.2024
Каждая единица проектирования VHDL содержит объявление "сущности" и одну или несколько "архитектур". Каждая архитектура определяет различную реализацию или модель данной единицы проекта. Определение объекта определяет входные и выходные данные модуля, а также любые «общие» параметры, используемые различными реализациями модуля.
Формат объявления объекта – Вернуться к началу
Режим порта определяет направление сигналов на этом порту и может быть одним из следующих: вход, выход, буфер или вход.
Пример
Обобщения позволяют передавать статическую информацию блоку из его окружения для всех архитектур единицы разработки. К ним относятся сведения о времени (установка, удержание, время задержки), размеры деталей и другие параметры.
Пример
Архитектура — Вернуться к началу
- Поведенческая модель: не подразумевается никакой структуры или технологии. Обычно пишется в последовательном процедурном стиле.
- Модель потока данных: показаны все пути данных, а также все управляющие сигналы.
- Структурная модель: взаимосвязь компонентов.
Пакет VHDL содержит подпрограммы, определения констант и/или определения типов, которые должны использоваться в одной или нескольких единицах проекта. Каждый пакет содержит «раздел объявлений», в котором объявлены доступные (т. е. экспортируемые) подпрограммы, константы и типы, и «тело пакета», в котором определены реализации подпрограмм вместе с любыми внутренними константами и типами. . Раздел объявления представляет часть пакета, которая «видима» пользователю этого пакета. Фактические реализации подпрограмм в пакете обычно не представляют интереса для пользователей этих подпрограмм.
Формат объявления пакета:
Пример:
Формат тела пакета:
Пример:
Видимость пакета
Чтобы сделать все элементы пакета "видимыми" для проектной единицы, добавьте перед желаемой проектной единицей оператор "использовать":
Пример:
Инструкция «use» может предшествовать объявлению любого объекта или архитектуры, которая должна использовать элементы из пакета. Если оператор «use» предшествует объявлению сущности, пакет также виден архитектуре.
Пакеты, разработанные пользователями
Скомпилируйте пользовательские пакеты в вашей текущей рабочей библиотеке. Чтобы сделать его видимым: Примечание: «std» и «work» (ваша текущая рабочая библиотека) — это две библиотеки по умолчанию. Оператор VHDL 'library' необходим, чтобы сделать библиотеку ieee и/или дополнительные библиотеки видимыми.
Пример
Стандартные пакеты VHDL
-
STANDARD - объявления базовых типов (всегда видны по умолчанию)
TEXTIO - типы данных ввода/вывода ASCII и подпрограммы
Пакет стандарта IEEE 1164
Этот пакет, содержащийся в библиотеке ieee, поддерживает многозначные логические сигналы с объявлениями типов и функциями. Чтобы сделать видимым:
Специальные 12-значные типы данных/функции для взаимодействия с QuickSim II и принципиальными схемами.
ИДЕНТИФИКАТОРЫ VHDL, ЦИФРЫ, СТРОКИ И ВЫРАЖЕНИЯ – Вернуться к началу
Идентификаторы
Идентификаторы в VHDL должны начинаться с буквы и могут содержать любую комбинацию букв, цифр и знаков подчеркивания. Обратите внимание, что VHDL внутренне преобразует все символы в ВЕРХНИЙ РЕГИСТР.
Примеры
Числовые константы
Числовые константы могут быть определены и могут иметь любую основу (по умолчанию десятичная). Числа могут включать встроенные символы подчеркивания для улучшения читабельности.
Примеры
Битовые строковые литералы
Примеры
Арифметические и логические выражения
- Логический: и, или, nand, ни, xor, не (для логических или битовых операций)
- Относительный: =, /=, , >=
- Арифметика: +, -, *, /, mod, rem, **, abs
(mod b принимает знак b, rem b принимает знак a) - Объединить: &
(например, a и b составляют один массив)
Примеры
ТИПЫ ДАННЫХ VHDL — Вернуться к началу
Каждый объект VHDL должен классифицироваться как относящийся к определенному типу данных. VHDL включает ряд предопределенных типов данных и позволяет пользователям определять собственные типы данных по мере необходимости.
Предопределенные скалярные типы данных (отдельные объекты)
Стандарт VHDL:
- битовые значения: "0", "1"
- логические значения: ИСТИНА, ЛОЖЬ
- целочисленные значения: от -(231) до +(231-1)
- натуральные значения: от 0 до целого числа (подтип целого числа)
- положительные значения: от 1 до целого числа (подтип целого числа)
- значения символов: символы ASCII (например, "A")
- значения времени включают единицы измерения (например, 10 нс, 20 мкс)
Стандарт IEEE 1164 (пакет ieee.std_logic_1164.all)
-
значения std_ulogic: «U», «X», «1», «0», «Z», «W», «H», «L», «-»
- битовый векторный массив (естественный диапазон <>) бит
- массив строк (естественный диапазон <>) символов
- текстовый файл "string"
- массив std_ulogic_vector (естественный диапазон <>) std_ulogic
- массив std_logic_vector (естественный диапазон <>) std_logic
- Ограниченный массив: указаны верхний и нижний индексы.
- Инерционная задержка: добавление в очередь событий события, запланированного на время T, автоматически отменяет все события в очереди, которые должны произойти до времени T, т. е. любое событие короче, чем время задержки, подавляется.
- Задержка передачи: каждое новое событие просто вставляется в очередь событий, т. е. поведение соответствует линии задержки. Ключевое слово транспорт используется для обозначения задержек транспорта.
- А А
- с выражением выберите порт A".
- Передача сообщений и
- Общая память
- Следовательно, вариант А является правильным выбором, поскольку он также относится к вышеуказанной концепции. В то время как другое неверно, потому что--
- В варианте Б указано, что передача сообщений происходит быстрее, чем в общей памяти, что неверно. \u00a0
- Вариант C указывает, что для больших данных используется передача сообщений, но для больших данных используется общая память.
- Вариант D указывает, что разделяемая память недоступна в каком-то процессоре, что неверно. ">,
- Следовательно, вариант А является правильным выбором, поскольку он также относится к вышеуказанной концепции. В то время как другое неверно, потому что--
- В варианте Б указано, что передача сообщений происходит быстрее, чем в общей памяти, что неверно.
- Вариант C указывает, что для больших данных используется передача сообщений, но для больших данных используется общая память.
- Вариант D указывает, что разделяемая память недоступна в каком-то процессоре, что неверно.
- В модели с малой памятью один сегмент использовался для всего кода, а другой — для всех данных. Все указатели по умолчанию располагались рядом с указателями.
- Модель Compact содержала не более 64 КБ кода, поэтому все переходы и вызовы могли выполняться около , но она могла обрабатывать более 64 КБ данных. В частности, он может дать стеку свой собственный сегмент и снизить риск переполнения стека. Переходы, вызовы и указатели функций в языках, в которых они были, были рядом, но указатели на данные по умолчанию были далеко. Вероятно, это была наиболее часто используемая модель в MS-DOS.
- В модели Medium было не более 64 КБ данных и более 64 КБ, но менее 640 КБ кода. (MS-DOS не смогла загрузить код выше адреса 0xA0000 , по крайней мере, обычным способом, потому что IBM решила разместить видеопамять на исходном ПК именно там.)
- В модели Large по умолчанию использовались дальние указатели, и она могла поддерживать более 64 КБ кода и данных.
- Borland Turbo C и несколько других компиляторов поддерживают модель памяти Tiny, в которой весь код и данные помещаются в один сегмент размером 64 КБ.
- В модели Huge для кода и данных использовались дальние указатели, но данные обрабатывались как плоское адресное пространство. Разница между Large и Huge заключалась в том, что если арифметика указателя на дальнем указателе данных переполняла нижние 16 бит, они циклически повторялись. Если арифметика указателя на огромном указателе данных переполняет нижние 16 бит, сегмент увеличивается.
-
'U' = неинициализированный
'X' = неизвестный
'W' = слабый 'X'
'Z' = плавающий
'H'/'L' = слабый '1 '/'0'
'-' = все равно
Предопределенные типы сводных данных VHDL
Агрегированные типы данных стандарта IEEE 1164
Примеры
Определяемые пользователем типы перечисления
Пример
Другие определяемые пользователем типы
Пример
Примеры
Примеры
Псевдонимы
Псевдоним" определяет альтернативное имя для сигнала или части сигнала. Псевдонимы часто используются для ссылки на выбранные фрагменты битового_вектора.
Пример
ОБЪЕКТЫ VHDL: КОНСТАНТЫ, ПЕРЕМЕННЫЕ И СИГНАЛЫ — Вернуться к началу
Константы
Примеры
Переменные
Примеры
Сигналы
Сигнал – это объект с историей значений (связанных с временем "события", т. е. временем, когда значение сигнала изменяется).
Сигналы объявляются с помощью операторов объявления сигналов или определений портов объектов и могут иметь любой тип данных. Синтаксис объявления:
Примеры
Каждый сигнал имеет один или несколько драйверов, которые определяют значение и время изменения сигнала. Каждый драйвер представляет собой очередь событий, которые указывают, когда и на какое значение должен быть изменен сигнал. Каждое назначение сигнала приводит к изменению соответствующей очереди событий для планирования нового события.
ПРИМЕЧАНИЕ. Если задержка не указана, сигнальное событие запланировано на одну бесконечно малую "дельту" задержки от текущего времени. Изменение сигнала произойдет в следующем цикле моделирования.
Примеры
Примеры
При наличии нескольких драйверов для одного сигнала должна быть предусмотрена «функция разрешения», чтобы определить значение, которое будет присвоено сигналу, из значений, предоставленных несколькими драйверами. Это позволяет моделировать шины с несколькими источниками/драйверами.
ПРИМЕЧАНИЕ. Типы std_logic и std_logic_vector из библиотеки ieee имеют предопределенные функции разрешения:
Пример
Разрешенное значение равно "1", так как "1" переопределяет значение "Z" (плавающее). Если бы эти два значения были "1" и "0", разрешенное значение было бы "X", что указывает на неизвестный результат.
СОВПАДАЮЩИЕ ЗАЯВЛЕНИЯ — Вернуться к началу
Параллельные операторы включены в определения архитектуры и в "блочные" операторы, представляющие параллельное поведение в смоделированной единице проектирования. Эти операторы выполняются асинхронно, без определенного порядка, моделируя поведение независимых аппаратных элементов в системе.
Параллельное назначение сигналов
Пример
(В этом формате порядок сигналов не важен)
Пример:
Параллельное утверждение — Вернуться к началу
одновременный оператор утверждения проверяет условие (возникновение события) и выдает отчет, если условие не соответствует действительности. Это можно использовать для проверки нарушений синхронизации, недопустимых условий и т. д. Можно сообщить о дополнительном уровне серьезности, указывающем характер обнаруженного состояния.
Синтаксис:
Создать выписку — Вернуться к началу
оператор генерации — это итеративная или условная обработка части описания. Это обеспечивает компактный способ представления того, что обычно представляет собой группу операторов.
Пример
ПОСЛЕДОВАТЕЛЬНЫЕ ЗАЯВЛЕНИЯ — Вернуться к началу
Последовательные операторы используются для определения алгоритмов, выражающих поведение элемента дизайна. Эти операторы появляются в операторах процесса и в подпрограммах (процедурах и функциях).
Ожидание – Вернуться к началу
- приостанавливает выполнение процесса/подпрограммы до тех пор, пока сигнал не изменится, условие не станет истинным или не истечет определенный период времени. Также можно использовать их комбинации.
Синтаксис:
Пример
Заявление о присвоении сигнала – Вернуться к началу
Пример
Оператор присваивания переменной — Вернуться к началу
Обновите переменную процесса/процедуры/функции с помощью выражения. Обновление вступает в силу немедленно.
Пример
Вызов процедуры – Вернуться к началу
Условные операторы — Вернуться к началу
Стандарт если..Конструкции then и case можно использовать для выборочных операций. ПРИМЕЧАНИЕ. Предложения elsif и else являются необязательными. ПРИМЕЧАНИЕ. Варианты case могут быть выражениями или диапазонами.
Операции цикла — Вернуться к началу
Последовательности операторов могут повторяться несколько раз под управлением конструкций while или for. ПРИМЕЧАНИЕ: метка необязательна.
Операции завершения цикла — позволяют завершить одну итерацию, цикл или процедуру.
следующий [при условии]; -- завершить итерацию текущего цикла
выйти [при условии]; -- полностью выйти из внутреннего цикла
выражение возврата; -- выход из подпрограммы
ПРИМЕЧАНИЯ: 1. Условие следующего/выходного условия является необязательным.
<р>2. Выражение возврата используется для функций.ПРОЦЕДУРЫ – Вернуться к началу
Пример
ФУНКЦИИ – Вернуться к началу
Функция — это подпрограмма, которой передаются параметры и которая возвращает одно значение. В отличие от процедур, функции в основном используются в выражениях.
Пример
Вызовы функций:
-
To_bit(sul) - от std_ulogic к биту
To_bitvector(sulv) - от std_ulogic_vector/std_ulogic_vector
To_StdULogic(b) - от бита к std_ulogic
To_StdLogicVector(bv) - от bit_vector или std_ulogic_vector
To_StdLogicVector(bv) - от bit_vector или std_ulogic_vector
To_StdULogicVector(bv) — из bit_vector или std_logic_vector
To_X01(v) — из бита, std_ulogic или std_logic в X01
To_X01Z(v) — из бита, std_ulogic или std_logic в X01Z
>To_UX01(v) — от бита, std_ulogic или std_logic до UX01
Другие функции "ieee.std_logic_1164" - Вернуться к началу
-
Rising_edge(s) — true, если передний фронт сигнала s (std_ulogic)
falling_edge(s) — true, если задний фронт сигнала s (std_ulogic)
-
библиотека mgc_portable;
используйте mgc_portable.qsim_logic.ALL;
а + б, а - б, а * б, а/б, мод б, а рем б
Логические операции между всеми типами сигналов и векторами типов сигналов в библиотеке "ieee".
Вариант А — правильный ответ на приведенный выше вопрос.
Верно утверждение, что общая память обычно быстрее, чем передача сообщений.
Почему общая память работает быстрее?
В компьютерных науках общеизвестно, что общая память – это тип памяти, которую многие программы могут видеть или использовать одновременно с целью обеспечения связи между ними.
Известно, что общая память является жизненно важным методом или средством передачи данных между программами, поскольку не нужно копировать данные, чтобы поделиться ими с другим процессом, и поэтому общая память работает быстрее.
Подробнее об общей памяти
Верно утверждение, что общая память обычно быстрее, чем передача сообщений.
Почему общая память работает быстрее?
В компьютерных науках известно, что общая память – это тип памяти, которую многие программы могут видеть или использовать одновременно с целью обеспечения связи между ними.
Известно, что общая память является жизненно важным методом или средством передачи данных между программами, поскольку не нужно копировать данные, чтобы поделиться ими с другим процессом, и поэтому общая память работает быстрее.
Подробнее об общей памяти
Ответ:
Вариант А — правильный ответ на приведенный выше вопрос.
Пояснение:
Компьютерная система нуждается во взаимодействии, которое осуществляется внутри процессора для обработки задачи, поставленной пользователем. Существует два типа моделей, используемых для взаимодействия:
Разница между ними заключается в том, что при передаче сообщения сообщение передается в двух точках за одну единицу времени, тогда как совместно используемая память одновременно распределяет несколько сообщений. Вот почему общая память быстрее, чем передача сообщений.
Новые вопросы по компьютерам и технологиям
Можете ли вы определить ошибку и исправить код?Вы можете попробовать запустить код на Python, чтобы проверить, правы ли вы! Если вы это сделаете, вам нужно будет добавить … оператор ввода для некоторых из них, так как иначе код не запустится. = a if response = "пип" : print("цикл завершен") [1]
Верите ли вы, что онлайн-активизм приводит к реальным изменениям, или вы думаете, что это то, что люди делают, чтобы выглядеть лучше/чувствовать себя лучше, чем они сами? Найдите несколько примеров того и другого в недавней истории, чтобы подтвердить свою точку зрения. Отразите свои выводы в посте для обсуждения объемом 350 слов. Обязательно дайте ссылки на несколько онлайн-источников и добавьте визуальные и другие мультимедийные ресурсы, которые помогут вам донести свою точку зрения. Убедитесь, что вы указали свои источники через обратную ссылку. Вы рассматриваете идеи других, поэтому они должны быть отмечены как таковые.
Чтобы защитить ценную информацию, организации должны O внимательно следить за всеми перемещениями сотрудников O интегрировать физические и цифровые меры безопасности O в … блокировать шпионское ПО на всех компьютерных устройствах сотрудников O вести учет всех паролей, используемых в системе
ЭЛЕМЕНТЫ БЛОК-СХЕМЫ A. НАПРАВЛЕНИЯ. Используйте подсказки, чтобы заполнить слова. Слова могут поперек или вниз. Буквы разделяются, когда слова пересекаются. ПОПЕРЕЧНО … 2. Линии представляют ____ последовательности и направления процесса. 5. Процесс, который может ответить на решение «да» или «нет», требует блока принятия решения. 8. представлен небольшим прямоугольником с изогнутыми углами. 10. представлен маленьким кружком или соединительной коробкой и обозначен буквами. ВНИЗ 1. представлен прямоугольником. Это относится к действию в бизнес-процессе. 3. Этот символ будет содержать букву внутри. Это указывает на то, что поток продолжается на совпадающем символе, содержащем ту же букву, в другом месте на той же странице. 4. Нарисовано в одном направлении, предпочтительно сверху вниз, держите блок-схему четкой. 6. представлен прямоугольником с двойными линиями на каждой стороне. 7. Он представляет информацию, входящую или выходящую из системы. 9. Представляет собой распечатку, например документ или отчет
Если бы вы создавали мультфильм, что бы вы предпочли: "прямо вперед" или "поза за позой"? Поясните свой ответ. - - Ваш ответ должен состоять не менее чем из 5-7 предложений
Когда геймдизайнеры начинают деконструировать игру, они начинают лучше понимать процесс разработки. Что из нижеперечисленного не является частью… процесса деконструкции игры? Варианты ответа на вопрос 1: Оценка того, как аудитория взаимодействует с игрой. Экспериментирование с игрой, предварительно протестировав ее. Выяснив, какие элементы присутствуют в игре.
Я слышал фразу "модель памяти", используемую в отношении программирования MS-DOS (и ранних версий Windows), с такими терминами, как "маленький" и "компактный".
Но каковы были фактические определения этих моделей памяти?
Обратите внимание: мне стало известно, что эти модели памяти обсуждаются в ответе на другой вопрос, но это это совсем другой вопрос, касающийся только один из аспектов крошечной модели (которую он называет "плоской моделью в реальном режиме").
Из-за этих различий я не нашел эту информацию при первоначальном поиске RC. Вопрос (который дважды был предложен в качестве причины для закрытия) можно найти в первом комментарии к этому вопросу, если вы хотите проверить его и принять собственное решение.
Поскольку целью правила "закрыть как дубликат" является предотвращение дублирования вопросов, я считаю, что этот вопрос по-прежнему актуален. Я не верю, что кто-то здесь подумает, что искатели должны просматривать все ответы на косвенно связанные вопросы, чтобы найти нужную им информацию :-) Это сделало бы RC гораздо менее полезным сайтом. на мой взгляд.
Информация есть, но ее трудно найти. Я бы сказал, что имеет смысл задать вопрос, который явно касается моделей памяти в целом.
Именно поэтому я задал вопрос. Близкая причина обмана - "вопрос уже задавался раньше и на него есть ответ". Предложенный обман, хотя и содержал ответ, содержащий эту информацию внизу, на самом деле не спрашивал ни о чем, кроме крошечной модели (то, что он называл плоской моделью в реальном режиме).
Можно уточнить? Это не сама DOS имеет какие-либо модели памяти, это, например, компилятор C, который предоставляет вам различные модели памяти в зависимости от того, насколько большую программу вы хотите создать. В конце концов, DOS просто загружает ваш исполняемый файл для запуска, и он работает на процессоре x86, ваша программа может делать с памятью все, что захочет.
@Stephen, конечно, в вопросе должно быть некоторое сходство. Если ответ на вопрос Есть ли у Java неопределенное поведение? утверждает, что увеличение знакового целого числа в C является одним из примеров UB, это не должно препятствовать тому, чтобы кто-то спрашивал, почему моя переменная C получает странные значения, когда становится слишком большой? .Я не думаю, что кто-то подумает, что первый вопрос связан со вторым, поэтому они даже не будут смотреть ответы. Но, в любом случае, спасибо, что нашли мета-вопрос, я перенесу дальнейшие комментарии туда.
3 ответа 3
Модель памяти зависела от того, сколько кода и/или данных использовала ваша программа. Сначала немного предыстории.
8086 (1) был основан на более ранних чипах Intel, где их адресное пространство составляло строго 64 КБ, и у вас был доступ ко всему этому как для кода, так и для данных, используя 16-битный адрес.
Однако с 8086, позволяющим увеличить объем памяти, они использовали довольно остроумное решение, когда специальные сегментные регистры выбирали базу памяти, которую вам было разрешено использовать, и затем вы могли адресовать 64 КБ в этой точке и за ее пределами. Эта база может быть другим значением для кода и данных (и, если уж на то пошло, стека).
Преобразование для преобразования сегментного регистра SR и адреса AD в физический адрес PA было следующим:
Таким образом, сегмент мог начинаться с любого физического адреса, кратного шестнадцати, и это обеспечивало большую гибкость при размещении кода в зависимости от того, сколько места ему требовалось. В памяти могло одновременно находиться несколько программ, но поскольку защиты памяти не было, нужно быть осторожным.
Регистры сегментов CS и DS определяли, где в физическом адресном пространстве находились адреса кода и данных ( SS предназначался для стека, а ES был регистром дополнительного сегмента).
В этих более ранних чипах Intel можно было бы представить, что все эти сегментные регистры (если они действительно имели сегментные регистры, которых у них нет) установлены на ноль, что означает код, данные и стек. находятся в первых 64 КБ.
Таким образом, старые программы, которые должны были содержать все данные и код в одном блоке размером 64 КБ, можно было бы легко перевести, а затем, просто установив для CS и DS одно и то же значение при их запуске, они будут работать нормально. У них будет доступ только к их 64-килобайтному пространству, поскольку они не знают, что могут изменять регистры сегментов.
Однако новые программы могут воспользоваться этим знанием, чтобы разрешить более 64 КБ кода и/или данных, просто изменяя сегментные регистры по желанию.
Вы могли получить доступ к более чем 64 КБ данных, повозившись с DS или используя специальные инструкции, которые вместо этого использовали ES. Вы можете перейти к коду за пределами вашего текущего сегмента CS, используя дальний вызов, а не обычную (короткую) инструкцию вызова.
Кроме того, эта схема просуществовала и в эпоху защищенного режима, даже после того, как простое вычисление физического адреса было заменено селекторами, которые использовали таблицы для определения физических адресов, а также допустимые ограничения на объем данных, к которым вы можете получить доступ, начиная с этого адреса (например, возможно, меньше, чем 64 КБ).
Сказав все это, обратимся теперь к моделям памяти. Я не уверен, что все это были «официальные» модели памяти (от Intel или MS-DOS), но они использовались в различных продуктах.
Tiny: это было фактически то же самое, что и схема до 8086, допускающая 64 КБ для кода, данных и стека. Все CS , DS и SS были установлены на одно и то же значение. Это была модель памяти, которую использовали COM-файлы (при запуске они, конечно, могли изменить сегментные регистры после, если бы захотели). EXE-файлы также могли использовать крошечные модели, но для них были разрешены следующие модели.
Маленький: аналогичен крошечному в том смысле, что CS и DS никогда не изменятся, но они будут иметь разные значения. Это позволяло использовать код размером 64 КБ и отдельные данные размером 64 КБ. В этом случае (и других ниже) у вас может быть SS такой же или отличный от DS, в зависимости от ваших потребностей.
Компактность: по-прежнему использовался один неизменяемый CS, но DS разрешалось изменять. Таким образом, размер кода по-прежнему ограничивался 64 КБ, но объем данных мог быть значительно больше.
Средний. В отличие от компактного, используются дальние вызовы, так что CS может меняться, а DS остается на одном уровне. Разрешено более 64 КБ кода, но ограничено данными до 64 КБ.
Большой: используются дальние вызовы и несколько значений DS, что позволяет хранить более 64 КБ кода и данных.
Огромный. Большой, но с небольшими отклонениями. Несмотря на то, что большая модель давала вам более 64 КБ данных, каждый отдельный элемент данных, как правило, ограничивался 64 КБ. Огромная модель добавила возможность для одного элемента данных превысить 64 КБ с помощью некоторой формы «обмана», реализованной в программном обеспечении.
Теперь вы можете комбинировать модели памяти, если, например, вы хотите, чтобы в основном была маленькая модель, но нужна была одна функция с удаленным вызовом. В этом случае вам нужно было бы каким-то образом уведомить свой компилятор о том, что эта функция является функцией дальнего вызова, например, используя FAR в своем коде, чтобы пометить ее так (затем он скорректирует вызовы к ней, чтобы они были дальними вызовами).
Вы также должны быть очень осторожны, так как библиотеки времени выполнения, добавленные в ваш код, были выбраны для конкретной модели памяти. Таким образом, небольшая программа-модель не очень удобна для вызова библиотечных функций там, где CS отличается от ее собственной.
Проблема заключается не столько в вызове, поскольку ваш код с удаленным вызовом может выполнять удаленный вызов в библиотеку. Но сама библиотека вернётся близко, а не далеко. Добром это не кончится :-)
Это хороший момент, @ecm: я уточнил, что именно так они были запущены, чтобы не было путаницы, что они были заперты в этом состоянии. Спасибо. Я полагаю, кстати, вы сказали «около 64 КБ», потому что они были загружены по адресу 0x0100, да? Следовательно, файл может быть только 64K-256?
В случае небольшого приложения модели памяти, вызывающего большую библиотеку моделей памяти, все было в порядке, если предположить, что заголовочные файлы были настроены для правильного объявления функций и указателей данных. Похоже, ваш пример пытается показать, что приложение с большой моделью памяти не может использовать маленькую библиотеку модели памяти, но я не думаю, что это оптимально доносит суть.
Для больших и малых (или больших и компактных) вам нужно поставить ближний вызов XXXX, дальний ret где-то в целевом сегменте, а затем дальний вызов для этого. Дальний вызов приведет вас в правильный сегмент, чтобы ближний вызов (и вернувшийся ближний) сработал, а затем дальний рет вернет вас в другой сегмент кода. Или я что-то пропустил?
Модели памяти были определены компиляторами для языков высокого уровня и были достаточно стандартными для Microsoft, Borland и Watcom. Модели Small, Medium, Compact и Large, по-видимому, созданы с помощью компилятора Intel 1980 года.
Во-первых, краткое объяснение того, как работает архитектура 8086. Это был 16-битный ЦП, который мог адресовать память только порциями, называемыми сегментами. Размер каждого сегмента составлял 65 536 байт, потому что это было число байтов, которое могли адресовать 16 бит. Программа может одновременно использовать четыре сегментных регистра: SS (стек), CS (код), DS (данные) и ES (дополнительно). 16-битный указатель в одном из этих сегментов был ближним указателем. Первоначально эти сегменты могли начинаться с любого 16-байтового «абзаца» одномегабайтной «обычной памяти», поэтому дальнему указателю требовалось 32 бита для хранения 20-битного адреса. Более поздние машины добавили возможность переключения между сегментами «расширенной» или «расширенной» памяти, чтобы защитить память как недоступную для записи или неисполняемую, а также добавили еще два сегментных регистра, FS (не означает) и GS (что угодно). ).
Некоторое время назад я написал подробный ответ о причинах, по которым Intel сделала такой выбор. В то время это имело смысл, но только потому, что инженеры верили, что когда-нибудь Intel сможет нарушить обратную совместимость с ним и двигаться дальше.
Модели памяти определяли, будет ли программа считать, что весь ее код находится в одном сегменте, все ее данные, и то, и другое. Это определяло, может ли программа предположить, что любая произвольная функция, которую она вызывает, находится в том же сегменте кода, или любые данные, к которым она обращается, уже находятся в ее сегменте данных, и, следовательно, нужна ли ей дополнительная память для хранения сегмента и дополнительный код для обновления сегментного регистра. . Язык ассемблера на самом деле не нуждался в формальной модели памяти, поскольку программист всегда мог решить, писать ли ближнюю или дальнюю инструкцию. Языки высокого уровня, однако, должны были найти компромисс между использованием меньших ближних указателей, которые были более эффективными, и более широкими дальними указателями, которые могли поддерживать более 64 КБ кода или данных. (Точно так же современные программисты иногда пишут 32-битный код на 64-битной машине, потому что 32-битные указатели используют меньше памяти.) Стандартной стала следующая терминология:
Важно отметить, что хотя модели Compact и Large поддерживали более 64 КБ данных, ни один отдельный массив, структура или объект не мог превышать 64 КБ. Каждый такой объект должен был вписаться в один сегмент.(Вот почему C и C++ по-прежнему не позволяют сравнивать или вычитать два указателя из отдельных объектов. Это нарушит архитектуру, использующую сегменты.)
Программа, использующая более крупную модель, по-прежнему может локально использовать ближние указатели или помещать семейство функций в одну и ту же группу сегментов, где они могут вызывать друг друга с помощью ближних вызовов. В модели с меньшей моделью памяти может быть несколько дальних функций за пределами основного сегмента кода или только несколько фрагментов дальних данных, а остальные умещаются в пределах 64 КБ.
Было и несколько других моделей памяти.
Позже компиляторы добавили шестую модель.
Это имело незначительное преимущество, заключавшееся в том, что два указателя данных были псевдонимами друг друга тогда и только тогда, когда они были закодированы одними и теми же битами, и гораздо более важным преимуществом, заключающимся в том, что массивы и структуры теперь могли иметь размер более 64 КБ.
Наконец, некоторые программы MS-DOS (большинство из них, но не все, игры) в 90-х годах начали использовать расширители DOS. Многие из них использовали недокументированные приемы, позволяющие программе DOS использовать плоское 32-битное пространство памяти. К концу существования DOS они стали стандартизированы как DPMI и другие интерфейсы.
Поскольку основная цель этой главы — научить вас, как использовать низкоуровневые машинные инструкции для реализации решений, циклов и других управляющих конструкций, было бы разумно показать вам, как имитировать эти высокоуровневые операторы, используя " чистый" язык ассемблера. Эта информация представлена в следующих разделах.
2.8 Введение в решения
В своей простейшей форме решение – это своего рода ветвь кода, которая переключается между двумя возможными путями выполнения в зависимости от некоторого условия. Обычно (хотя и не всегда) последовательности условных инструкций реализуются с помощью инструкций условного перехода. Условные инструкции соответствуют оператору IF..THEN..ENDIF в HLA:
Язык ассемблера, как обычно, предлагает гораздо большую гибкость при работе с условными операторами. Рассмотрим следующий оператор C/C++:
Подход "грубой силы" к преобразованию этого оператора в язык ассемблера может привести к следующему:
Как видите, требуется значительное количество условных операторов. операторы только для обработки выражения в приведенном выше примере. Это примерно соответствует (эквивалентным) операторам C/C++:
Теперь сравните это со следующим «улучшенным» кодом:
Из приведенных выше кодовых последовательностей должны быть очевидны две вещи: во-первых , один условный оператор в C/C++ (или каком-то другом HLL) может потребовать несколько условных переходов на ассемблере; во-вторых, организация сложных выражений в условную последовательность может повлиять на эффективность кода. Поэтому следует проявлять осторожность при работе с условными последовательностями на языке ассемблера.
Условные операторы можно разделить на три основные категории: операторы IF, операторы SWITCH/CASE и непрямые переходы. В следующих разделах описываются эти программные структуры, как их использовать и как писать на языке ассемблера.
2.8.1 Последовательности IF..THEN..ELSE
Самым распространенным условным оператором является оператор IF..THEN или IF..THEN..ELSE. Эти два оператора имеют форму, показанную на рисунке 2.1:
Рисунок 2.1 IF..THEN..ELSE..ENDIF и IF..ENDIF Поток операторов
Инструкция IF..ENDIF является частным случаем инструкции IF..ELSE..ENDIF (с пустым блоком ELSE). Поэтому мы рассмотрим только более общую форму IF..ELSE..ENDIF. Базовая реализация оператора IF..THEN..ELSE на языке ассемблера 80x86 выглядит примерно так:
Примечание: J cc представляет некоторую инструкцию условного перехода.
Например, to преобразовать оператор C/C++:
в язык ассемблера, вы можете использовать следующий код 80x86:
Для простых выражений, таких как "( a == b )", генерируется правильный код для оператор IF..ELSE..ENDIF почти тривиален. Если выражение становится более сложным, сложность связанного с ним кода на языке ассемблера также увеличивается.Рассмотрим следующий оператор IF C/C++, представленный ранее:
При обработке сложных операторов IF, таких как этот, вы обнаружите, что задача преобразования упрощается, если вы разбиваете этот оператор IF на последовательность из трех разных операторов IF. следующим образом:
Это преобразование происходит из следующих эквивалентностей C/C++:
эквивалентно
и
эквивалентно< /p>
В языке ассемблера прежний оператор IF становится следующим:
Как вы, вероятно, понимаете, код, необходимый для проверки условия, может легко стать более сложным, чем операторы, появляющиеся в операторах ELSE и THEN. блоки. Хотя кажется несколько парадоксальным, что для проверки условия может потребоваться больше усилий, чем для того, чтобы действовать в соответствии с результатами этого состояния, это происходит постоянно. Поэтому вы должны быть готовы к этой ситуации.
Возможно, самая большая проблема с реализацией сложных условных операторов на языке ассемблера заключается в попытке выяснить, что вы сделали после того, как написали код. Вероятно, самым большим преимуществом языков высокого уровня по сравнению с языком ассемблера является то, что выражения на языке высокого уровня намного легче читать и понимать. Это одна из основных причин, по которой HLA поддерживает высокоуровневые структуры управления языком. Версия на языке высокого уровня является самодокументируемой, тогда как язык ассемблера имеет тенденцию скрывать истинную природу кода. Таким образом, хорошо написанные комментарии являются важным компонентом реализации операторов if..then..else на языке ассемблера. Элегантная реализация приведенного выше примера:
Следует признать, что для такого простого примера это выходит за рамки. Вероятно, будет достаточно следующего:
Однако по мере того, как ваши операторы ЕСЛИ становятся сложными, плотность (и качество) ваших комментариев становится все более и более важной.
2.8.2 Преобразование операторов HLA IF в чистый язык ассемблера
Перевести операторы HLA IF на чистый язык ассемблера очень просто. Булевы выражения, поддерживаемые HLA IF, были специально выбраны для преобразования в несколько простых машинных инструкций. В следующих абзацах обсуждается преобразование каждого поддерживаемого логического выражения в чистый машинный код.
if(flag_specification) then > endif;
Эта форма, возможно, является самой простой оператором IF HLA для конвертировать. Чтобы выполнить код сразу после ключевого слова THEN, если определенный флаг установлен (или снят), все, что вам нужно сделать, это пропустить код, если флаг снят (установлен). Для реализации требуется только одна инструкция условного перехода, как демонстрируют следующие примеры:
if(register) then > endif;
Эта форма оператора IF использует инструкцию TEST для проверки указанный регистр для нуля. Если регистр содержит ноль (ложь), то программа перепрыгивает операторы после предложения THEN с инструкцией JZ. Преобразование этого оператора в язык ассемблера требует инструкции TEST и инструкции JZ, как показано в следующих примерах:
if( ! register ) then > endif;
Эта форма оператора IF использует инструкция TEST для проверки указанного регистра на наличие нуля. Если регистр не равен нулю (истина), то программа перепрыгивает операторы после предложения THEN с инструкцией JNZ. Преобразование этого оператора в язык ассемблера требует инструкции TEST и инструкции JNZ способом, идентичным предыдущим примерам.
if(boolean_variable) then > endif;
Эта форма IF оператор сравнивает логическую переменную с нулем (false) и выполняет разветвления вокруг операторов, если переменная содержит false. HLA реализует этот оператор, используя инструкцию CMP для сравнения логической переменной с нулем, а затем использует инструкцию JZ (JE) для обхода операторов, если переменная ложна. В следующем примере показано преобразование:
if( ! boolean_variable ) then > endif;
Эта форма оператора IF сравнивает логическую переменную с нулем (false) и выполняет разветвления вокруг операторов. если переменная содержит true (т. е. противоположное условию предыдущего примера). HLA реализует этот оператор, используя инструкцию CMP для сравнения логической переменной с нулем, а затем использует инструкцию JNZ (JNE) для перехода между операторами, если переменная содержит значение true. Следующий пример демонстрирует преобразование:
if(mem_reg relop mem_reg_const ) then > endif;
HLA переводит эту форму оператора IF в инструкцию CMP и условный переход, который пропускает операторы при противоположном условии, заданном оператором relop. В следующей таблице перечислены соответствия между операторами и инструкциями условного перехода:
2.8.3 Реализация сложных операторов IF с использованием полного логического вычисления
В предыдущем разделе не обсуждалось, как преобразовать логические выражения, включающие конъюнкции (И) или дизъюнкции (ИЛИ), в язык ассемблера.Этот раздел начнет это обсуждение. Есть два разных способа преобразовать сложные логические выражения, включающие конъюнкцию и дизъюнктию, в язык ассемблера: с использованием полного логического вычисления или вычисления короткого замыкания. В этом разделе обсуждается полное логическое вычисление. В следующем разделе обсуждается короткое логическое вычисление, которое является схемой, которую HLA использует при преобразовании сложных логических выражений в язык ассемблера.
Использование полного логического вычисления для вычисления логического выражения для оператора IF почти идентично преобразованию арифметические выражения на языке ассемблера. Действительно, предыдущий том описывает этот процесс преобразования (см. «Логические (булевы) выражения» на стр. 536). Единственное, что стоит отметить в этом процессе, это то, что вам не нужно сохранять окончательный логический результат в какой-либо переменной; как только оценка выражения завершена, вы проверяете, есть ли у вас ложный (ноль) или истинный (один или ненулевой) результат, чтобы определить, нужно ли переходить вокруг части THEN оператора IF. Как видно из примеров в предыдущих разделах, часто можно использовать тот факт, что последняя логическая инструкция (И/ИЛИ) устанавливает нулевой флаг, если результат ложный, и очищает нулевой флаг, если результат истинный. Это позволяет избежать явного тестирования результата. Рассмотрим следующий оператор IF и его преобразование в язык ассемблера с использованием полной логической оценки:
Этот код вычисляет логическое значение в регистре BL, а затем, в конце вычисления, проверяет это результирующее значение, чтобы увидеть, содержит истинное или ложное. Если результат ложный, эта последовательность пропускает код, связанный с Stmt1. В этом примере важно отметить, что программа будет выполнять каждую инструкцию, которая вычисляет этот логический результат (вплоть до инструкции JE).
Дополнительные сведения о полной логической оценке см. в разделе "Логический ( Boolean) Expressions" на странице 536.
2.8.4 Логическая оценка короткого замыкания
Если вы готовы потратить немного больше усилий на изучение сложного логического выражения, вы обычно можете преобразовать его в гораздо более короткую и быструю последовательность инструкций на языке ассемблера, используя ускоренное логическое вычисление. Сокращенное логическое вычисление пытается определить, является ли выражение истинным или ложным, выполняя только часть инструкций, которые вычисляют полное выражение. Выполняя только часть инструкций, оценка часто выполняется намного быстрее. По этой причине, а также из-за того, что логическое вычисление короткого замыкания не требует использования каких-либо временных регистров, HLA использует вычисление короткого замыкания при переводе сложных логических выражений на язык ассемблера.
Чтобы понять, как происходит короткое замыкание логическое вычисление работает, рассмотрите выражение "A && B". Как только мы определяем, что A ложно, нет необходимости оценивать B, поскольку выражение никак не может быть истинным. Если A и B представляют подвыражения, а не простые переменные, вы можете начать видеть экономию, которая возможна с помощью логических вычислений с коротким замыканием. В качестве конкретного примера рассмотрим подвыражение "((x t))" из предыдущего раздела. Как только вы определите, что x не меньше y , нет необходимости проверять, больше ли z t, поскольку выражение будет ложным независимо от значений z и t. В следующем фрагменте кода показано, как можно реализовать ускоренную логическую оценку для этого выражения:
Обратите внимание, как код пропускает дальнейшие проверки, когда определяет, что x не меньше y . Конечно, если x меньше y, то программа должна проверить z, чтобы увидеть, больше ли оно, чем t; в противном случае программа пропускает предложение THEN. Только если программа удовлетворяет обоим условиям, код не соответствует предложению THEN.
Для операции логического ИЛИ используется аналогичный метод. Если первое подвыражение истинно, то нет необходимости проверять второй операнд. Каким бы ни было значение второго операнда в этот момент, полное выражение по-прежнему оценивается как истинное. В следующем примере демонстрируется использование сокращенного вычисления с дизъюнкцией (ИЛИ):
Поскольку операторы конъюнкции и дизъюнкции коммутативны, вы можете сначала оценить левый или правый операнд, если это более удобно. . В качестве последнего примера в этом разделе рассмотрим полное логическое выражение из предыдущего раздела:
Обратите внимание, как код в этом примере сначала вычисляет "a != b", а оставшееся подвыражение последним. Это распространенная техника, которую программисты на ассемблере используют для написания лучшего кода.
2.8.5. Короткое замыкание и полная логическая оценка
Одним из фактов о полном логическом вычислении является то, что каждый оператор в последовательности будет выполняться при вычислении выражения. Сокращенное логическое вычисление может не требовать выполнения каждого оператора, связанного с логическим выражением.Как вы видели в предыдущих двух разделах выше, код, основанный на быстрой оценке, обычно короче и быстрее 1 . Таким образом, может показаться, что ускоренное вычисление является методом выбора при преобразовании сложных логических выражений в язык ассемблера.
Иногда, к сожалению, укороченное логическое вычисление может не дать правильного результата. При наличии побочных эффектов в выражении логическое вычисление с коротким замыканием даст результат, отличный от полного логического вычисления. Рассмотрим следующий пример C/C++:
Используя полное логическое вычисление, вы можете сгенерировать следующий код:
Используя укороченное логическое вычисление, вы можете сгенерировать следующий код: p>
Обратите внимание на очень тонкое, но важное различие между этими двумя преобразованиями: если оказывается, что x равно y , то первая версия выше все еще увеличивает z и сравнивает его с нулем перед выполнением кода, связанного с stmt. ; версия с коротким замыканием, с другой стороны, пропускает код, который увеличивает z, если оказывается, что x равен y. Следовательно, поведение этих двух фрагментов кода отличается от того, что происходит с z, если x равно y. Ни одна из реализаций не является особенно неправильной, в зависимости от обстоятельств вы можете или не хотите, чтобы код увеличивал z, если x равен y. Однако важно, чтобы вы понимали, что эти две схемы дают разные результаты, поэтому вы можете выбрать подходящую реализацию, если влияние этого кода на z имеет значение для вашей программы.
Многие программы используют преимущества короткого замыкания. логическое вычисление и полагаться на тот факт, что программа может не вычислять определенные компоненты выражения. Следующий фрагмент кода C/C++ демонстрирует, вероятно, наиболее распространенный пример, требующий упрощенного логического вычисления:
Если окажется, что Ptr имеет значение NULL в этом операторе IF, тогда выражение ложно и существует нет необходимости вычислять оставшуюся часть выражения (и, следовательно, код, использующий упрощенное логическое вычисление, не будет вычислять оставшуюся часть этого выражения). Этот оператор полагается на семантику короткого логического вычисления для правильной работы. Если бы C/C++ использовал полное логическое вычисление, а переменная Ptr содержала бы NULL, то вторая половина выражения пыталась бы разыменовать указатель NULL (что приводит к аварийному завершению большинства программ). Рассмотрим перевод этого оператора с использованием полной и короткой логической оценки:
Обратите внимание, что в этом примере, если Ptr содержит NULL (ноль), эта программа попытается получить доступ к данным в нулевой ячейке памяти через "mov ([eax], al);" инструкция. В большинстве операционных систем это вызовет ошибку доступа к памяти (общая ошибка защиты). Теперь рассмотрим короткое логическое преобразование:
Как видно из этого примера, проблемы с разыменованием указателя NULL не существует. Если Ptr содержит NULL, этот код пропускает операторы, пытающиеся получить доступ к адресу памяти, который содержит Ptr.
При программировании в MS-DOS и 16-разрядных версиях Windows приходилось иметь дело с моделями памяти. Эти термины относятся не к моделям памяти архитектуры процессора (как процессоры взаимодействуют с памятью), а к тому, как программа внутренне организует себя. Сама операционная система ничего не знает о моделях памяти приложений; это просто удобный способ рассказать о том, как программа работает с различными типами кода и данных.
Термины для моделей памяти пришли от компилятора C, так как это сообщает компилятору, какой тип кода следует генерировать. Четыре основные модели укладываются в красивую таблицу:
Размер указателя данных | |||
---|---|---|---|
Рядом th> | Далеко | ||
Размер указателя кода | Близко | Маленький | Компактный |
Далеко th> | Средний | Большой |
В 8086 использовалась сегментированная память, а это означает, что указатель состоит из двух частей: сегмента и смещения. дальний указатель состоит как из сегмента, так и из смещения. ближний указатель состоит только из смещения с подразумеваемым сегментом.
Если у вас было более 64 КБ кода или более 64 КБ данных, вам приходилось переключаться на дальние указатели кода или дальние указатели данных (соответственно), чтобы обращаться ко всему, что вам нужно.
Большинство моих программ были компактными, то есть в них было мало кода, но много данных. Это потому, что программы, которые я написал, обычно обрабатывали много данных.
Модель Medium была полезна для программ, в которых было много кода, но мало данных.Код пользовательского интерфейса часто попадал в эту категорию, потому что вам приходилось писать много кода для управления диалоговым окном, но результатом всей этой работы была просто текстовая строка (из поля редактирования) и несколько флажков (из некоторых флажков). ). Многие компьютерные игры также попали в эту категорию, потому что у них было много игровой логики, но мало игрового состояния.
Дальние указатели могли получить доступ к любой памяти в адресном пространстве процессора 8086 размером 1 МБ, но размер каждого объекта по-прежнему был ограничен 64 КБ, поскольку арифметические операции с указателями выполнялись только для смещенной части указателя. Огромные указатели могли ссылаться на блоки памяти размером более 64 КБ путем корректировки сегмента всякий раз, когда смещение переполнялось.¹ Арифметика указателей с огромными указателями требовательна к вычислительным ресурсам, поэтому вы не использовали их часто.²
Вы не были ограничены перечисленными выше моделями памяти. Вы можете придумать свою собственную, известную как программирование смешанной модели. Например, вы можете сказать, что большая часть вашей программы использует модель памяти Small, но есть одно место, где вам нужно получить доступ к памяти за пределами сегмента данных по умолчанию, поэтому для этой цели вы объявили явный указатель far. Точно так же вы можете явно определить функцию far, чтобы переместить ее из сегмента кода по умолчанию.
Модель памяти определяет поведение по умолчанию, поэтому, если вы вызываете, скажем, memcpy , вы получаете версию, размер указателя которой соответствует вашей модели памяти. Если у вас была программа модели Small или Medium и вы хотели скопировать память, находящуюся за пределами вашего сегмента данных по умолчанию, вы могли вызвать функцию _fmemcpy, которая была такой же, как memcpy, за исключением что потребовались дальние указатели. (Если вы использовали модель памяти Compact или Large, то memcpy и _fmemcpy были идентичны.)
Один из моих бывших коллег вернулся в школу и разговаривал со своим (младшим) консультантом. Как-то тема перешла к процессору 8086. Мой коллега и его друг объяснили сегментированную адресацию, ближние и дальние указатели, как ведут себя равенство и сравнение указателей³, таинственный шлюз A20 и как работает загрузочный сектор реального режима. «Выражение ужаса на его лице от того, как раньше работали сегментные регистры, было бесценным».
Я быстро поправил своего коллегу. «Работал? Они до сих пор так работают!»
Дополнительная болтовня: вы можете увидеть остатки 16-битных моделей памяти в макросах NEAR и FAR, определенных в windows.h . Они больше ничего не значат, но остаются для обратной совместимости исходного кода.
¹ В MS-DOS огромные указатели работали, помещая старшие 16 бит 20-битного адреса в сегмент и помещая оставшиеся 4 бита в смещение. Смещение большого указателя в MS-DOS всегда было меньше 16.
² В 16-разрядной версии Windows существовала системная функция hmemcpy, которая копировала блоки памяти, размер которых мог превышать 64 КБ. Теперь вы знаете, что означает префикс h.
³ Сегментированная память — отличный источник контрпримеров. Например, в модели сегментированной памяти у вас может быть указатель p на буфер размера N , а другой указатель q может удовлетворять неравенствам
несмотря на то, что он не указывает на буфер.
Это одна из причин, по которой языки программирования C и C++ не указывают результат сравнения между указателями, которые не указывают на элементы одного и того же массива.
Читайте также: