Модуль memory asm


Содержание

Assembler memory allocation

I was trying to solve this problem for a few days. I have no idea how to allocate memory to copy string into another location. Here’s the code:

Then in CPP file when I use the function:

It changes the value of word as is was passed by reference. I want it to do nothing until I’ll do:

I was been trying to solve this problem searching all over the Internet, but I found nothing helpful. I guess that solution is simple, but I cannot find it. I was trying to do sth with ESI and EDI registers. I guess I have to allocate new memory then copy the string into this allocated location. How to do it?

1 Answer 1

Following from the discussion in the comments, the basic approach to copy a string is to declare a label in .bss and reserve the number of bytes required to hold the copy of the string. This is using intel syntax as I do not have MASM available. As an example, you can declare your original string in .data. e.g.:

For the copy, load the address of str_str1 into esi and the address of str_str2 in edi . Load the number of characters to copy in cx and then call rep movsb to move the number of bytes specified in cx from esi to edi . e.g.:

That will copy str_str to str_str2 . You can then add the code to print them, etc. Nasm offers a simple macro language to help with repetitive tasks like read calls and write calls (especially for indents, spacing, printing, etc) The complete example in nasm syntax is:

Nasm compile/link

Output

Look at the MASM syntax differences and make the needed changes. There should be no reason you shouldn’t be able to accomplish the exact same thing.

Determining String Size — 32-bit

As discussed, to determine the length of an unknown string, the string itself must be null-terminated to provide an end sentinel character to test. The string address is placed in edi and then you effectively check each character for zero (numeric zero, not character zero) and the length is determined by the number of iterations it takes to reach the null-terminating character:

Of course, you do not have to use it as a function, you can just put the code in the body of your assembly program, but since it is something you use often, it helps to use it as a function.

c++&asm illegal memory reference

09.10.2011, 12:57

При сборке выдает ошибку «MASM: fatal error A1000: : 1.asm.asm»
«MASM : fatal error A1000: : 1.asm.asm :\1>link16 /TINY 1.asm.obj, 1.asm.com» Вопользовался.

Создание проекта с участием c++ и asm модулей. Ошибка asm модуля
Доброго времени суток! Подскажите, пожалуйста, как исправить данную ошибку (А2008)?

NASM: Invalid memory reference (SIGSEGV)
Всем привет. Изучаю ассемблер, пользуюсь NASM. Подскажите нубасу, что не так в коде? Я вот что-то.

Delphi и ASM — не работает вызов функции Invoke через asm
В Delphi не работает вызов функции Invoke через asm. часть кода: asm invoke.

Доступ к динамически выделенной памяти из asm-кода

Сделал модуль на asm:

вангуя 50 следующих тредов с аналогичными вопросами

gcc -S -masm=intel -march=native -O3

— LEA RBX,[arr2] + MOV RBX,[arr2]

а разве для для 64битного ABI fastcall существует? Вроде ж выпилили весь зоопарк stdcall, cdecl, pascal, fastcall и прочие соглашения о вызовах, сделав одно стандартное

Точно. Наверное я невнимательно пробовал этот вариант.

Дык, он в этом случае лишь для Си’шного OBJ-файла сделает дизасм. Или я что-то не понял?
Ассемблер простой бинарник [для дизассемблирования] не делает если есть внешние объекты:

а разве для для 64битного ABI fastcall существует?

Я examples для себя пишу. Лишнее в коде оставляю на память.

Проблема не в асме, а в непонимании в чем конкретно заключается работа компоновщика и как все это устроено.

а в непонимании в чем конкретно заключается работа компоновщика и как все это устроено.

Я раньше писал на TASM/DOS. Для него не было описаний работы линкера. Можно ли сейчас найти какую-то книгу, где есть сравнительные примеры работы линкера? Сравнение с тем, как было раньше под DOS (с сегментами), и сейчас — для Yasm,NASM/Windows и Linux? Мне нужно не 1000-страничный мануал от INTEL, а что-то краткое, для начинающих. Но в тоже время, чтобы сравнивать со своим опытом 1990х — наглядность для меня имеет большое значение. Теория в 37летнем возрасте уже не очень усваивается, и времени нет «загребать» кучу знаний по-новой. Я встречал какой-то 200-страничный мануал про то, как устроена память под FreeBSD и Дреппера. Но это не то.

Сравнение с тем, как было раньше под DOS (с сегментами), и сейчас — для Yasm,NASM/Windows и Linux?


Пацан пришёл на лор с интел-синтаксисом и иным маздайским говном, аля фасткалл, днище екстерны и прочее.

Сравнение с тем, как было раньше под DOS (с сегментами), и сейчас — для Yasm,NASM/Windows и Linux?

Ну это уже синдром секретарши, аля «осёл — и есть интернет». Смысла в объяснении нет.

Можно ли сейчас найти какую-то книгу, где есть сравнительные примеры работы линкера?

То говно, что ты видел — это говно. Задача «линкера» — это пройтись реплейсом имя/дифф — всё. И дос/хреном и масм/говнясм тут не причём.

Но в тоже время, чтобы сравнивать со своим опытом 1990х — наглядность для меня имеет большое значение.

Опыт. Громко сказано, скорее бесполезное и бездумное, бездарное, без малейшего понимания сути, ваяние говна со «знаниями» основанными на убогой, протухшей, не имеющей к реальности никакого отношения макулатуры и мусора из пустых черепушек таких же адептов.

Теория в 37летнем возрасте уже не очень усваивается, и времени нет «загребать» кучу знаний по-новой.

Проблема в том, что она никогда не усваивалась. Это было следствие мейнстрима и обезьяньего эффекта в нём, — подражательства, — т.е. любая обезьяна может подражать среде.

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

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

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

OldStuff — История оперативной памяти SIMM

Привет друже, есть у меня в коллекции вот такая кучка оперативки формата SIMM (предшественник DIMM)
И я хочу тебе поведать немного о них

В 1983 компания Wang Laboratories подала патент на новый тип оперативной памяти для компьютеров. Который назвала SIMM или «Single In-line Memory Module» что по нашему значит односторонний модуль памяти. Разработала она его всё в том же 1983 году. Как говорит Википедия — «Первоначально модули были керамическими и имели штырьки.» (фото не нашёл, у кого имеются буду весьма признателен)
Ссылка на патент — http://www.google.com/patents/US5973951
Для ЛЛ ниже изображения из патента:

Первые версии SIMM модулей памяти не имели фиксации в слотах на материнских платах. Но очень скоро начали применять ZIF-слоты с защёлками.
Самые первые оперативки SIMM были всего на 30 контактов и имели объем памяти от 64 килобайт до аж целых 16 МЕГАБАЙТ. (Представляете? В то время это было много) Шина данных у них была восьмиразрядной, но иногда добавляли девятую линию контроля четности памяти.

Применялися они в компьютерах с Центральным Процессором, он же попросту ЦП, Intel 8088, 286, 386.

Там где стоял ЦП 8088, модули ставились по одному, если процессоры были 286, 386SX то модули ставились парами, ну а на компьютеры с процессорами 386DX — по четыре модуля ОДИНАКОВОЙ ёмкости.

Когда до рынка массовых (персоналок) компьютеров добрался и надёжно укрепился процессор 80486 фирмы INTEL, а также ему аналогичные для которых 30 пиновые SIMM нужно было ставить КАК МИНИМУМ ЧЕТЫРЕ!

Тогда на свет появилась версия на 72 контакта (пина), которая по сути была не чем иным как 4 объединённых 30 пиновых SIMM в одном флаконе так сказать, с общими линиями адреса и раздельными линиями данных. Таким образом, модуль становится 32-разрядным и достаточно было всего одного модуля.

Теперь на одной планке размещалось от 1 мегабайта аж до целых космических 64 МЕГАБАЙТ (Чувствуете? чувствуете какую мощь они привнесли?)

Изначально в эпоху процессора INTEL 486 они появились только на брэндовых PC таких как Compaq, HP, Acer и других. Но, с приходом в этот прекрасный мир процессора Pentium их начали масово ставить практически на все материнские платы.
С распространением в массовых компьютерах процессоров Pentium, по причине низкого быстродействия динамической памяти SIMM-модулей, их спецификация претерпела изменения, в результате чего более новые модули, обладая немного большим быстродействием (их называли EDO от англ. Extended Data Out) стали несовместимы со старыми (FPM от англ. Fast Page Mode).

Так как на материнских платах для процессора Pentium с 64-разрядной шиной данных 72-контактные модули уже потребовалось ставить парами, постепенно и их физически попарно «объединили» путём расположения микросхем на обеих сторонах печатной платы модуля памяти, результатом чего стало появлением первых модулей DIMM.

Материнские платы для процессоров Pentium, как правило, поддерживали оба типа памяти, в то время как большинство материнских плат для процессора 486 поддерживали только старый (FPM) тип модулей памяти. Отличить модули по внешнему виду было практически невозможно (внешнее отличие было только в маркировке микросхем), и на практике чаще использовался метод «научного тыка». Установка «неправильного» типа памяти не приводила к неисправностям — система просто не видела памяти.

Существовали также 64-контактные (применявшиеся например в Macintosh IIfx) и 68-контактные (VRAM в Macintosh LC) варианты.

А вот и фото моих экземпляров из личной коллекции

Руководство по ассемблеру x86 для начинающих

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

В этой статье мы напишем с нуля калькулятор обратной польской записи (RPN) на чистом ассемблере x86. Когда закончим, то сможем использовать его так:

Весь код для статьи здесь. Он обильно закомментирован и может служить учебным материалом для тех, кто уже знает ассемблер.

Начнём с написания базовой программы Hello world! для проверки настроек среды. Затем перейдём к системным вызовам, стеку вызовов, стековым кадрам и соглашению о вызовах x86. Потом для практики напишем некоторые базовые функции на ассемблере x86 — и начнём писать калькулятор RPN.

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

Настройка среды

Как уже сказано, мы используем Linux (64- или 32-битный). Приведённый код не работает в Windows или Mac OS X.

Для установки нужен только компоновщик GNU ld из binutils , который предварительно установлен в большинстве дистрибутивов, и ассемблер NASM. На Ubuntu и Debian можете установить и то, и другое одной командой:


Я бы также рекомендовал держать под рукой таблицу ASCII.

Hello, world!

Для проверки среды сохраните следующий код в файле calc.asm :

Комментарии объясняют общую структуру. Список регистров и общих инструкций можете изучить в «Руководстве по ассемблеру x86 университета Вирджинии». При дальнейшем обсуждении системных вызовов это тем более понадобится.

Следующие команды собирают файл ассемблера в объектный файл, а затем компонует исполняемый файл:

После запуска вы должны увидеть:

Makefile

Это необязательная часть, но для упрощения сборки и компоновки в будущем можно сделать Makefile . Сохраните его в том же каталоге, что и calc.asm :

Илон Маск рекомендует:  Связанные подзапросы

Затем вместо вышеприведённых инструкций просто запускаем make.

Системные вызовы

Системные вызовы Linux указывают ОС выполнить для нас какие-то действия. В этой статье мы используем только два системных вызова: write() для записи строки в файл или поток (в нашем случае это стандартное устройство вывода и стандартная ошибка) и exit() для выхода из программы:

Системные вызовы настраиваются путём сохранения номера системного вызова в регистре eax , а затем его аргументов в ebx , ecx , edx в таком порядке. Можете заметить, что у exit() только один аргумент — в этом случае ecx и edx не имеют значения.

eax ebx ecx edx
Номер системного вызова arg1 arg2 arg3

Стек вызовов

Стек вызовов — структура данных, в которой хранится информация о каждом обращении к функции. У каждого вызова собственный раздел в стеке — «фрейм». Он хранит некоторую информацию о текущем вызове: локальные переменные этой функции и адрес возврата (куда программа должна перейти после выполнения функции).

Сразу отмечу одну неочевидную вещь: стек увеличивается вниз по памяти. Когда вы добавляете что-то на верх стека, оно вставляется по адресу памяти ниже, чем предыдущий элемент. Другими словами, по мере роста стека адрес памяти в верхней части стека уменьшается. Чтобы избежать путаницы, я буду всё время напоминать об этом факте.

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

Цель регистра esp — указать на вершину стека. Любые данные выше esp считаются не попавшими в стек, это мусорные данные. Выполнение инструкции push (или pop ) перемещает esp . Вы можете манипулировать esp и напрямую, если отдаёте отчёт своим действиям.

Регистр ebp похож на esp , только он всегда указывает примерно на середину текущего кадра стека, непосредственно перед локальными переменными текущей функции (поговорим об этом позже). Однако вызов другой функции не перемещает ebp автоматически, это нужно каждый раз делать вручную.

Соглашение о вызовах для архитектуры x86

В х86 нет встроенного понятия функции как в высокоуровневых языках. Инструкция call — это по сути просто jmp ( goto ) в другой адрес памяти. Чтобы использовать подпрограммы как функции в других языках (которые могут принимать аргументы и возвращать данные обратно), нужно следовать соглашению о вызовах (существует много конвенций, но мы используем CDECL, самое популярное соглашение для x86 среди компиляторов С и программистов на ассемблере). Это также гарантирует, что регистры подпрограммы не перепутаются при вызове другой функции.

Правила вызывающей стороны

Перед вызовом функции вызывающая сторона должна:

  1. Сохранить в стек регистры, которые обязан сохранять вызывающий. Вызываемая функция может изменить некоторые регистры: чтобы не потерять данные, вызывающая сторона должна сохранить их в памяти до помещения в стек. Речь идёт о регистрах eax , ecx и edx . Если вы не используете какие-то из них, то их можно не сохранять.
  2. Записать аргументы функции на стек в обратном порядке (сначала последний аргумент, в конце первый аргумент). Такой порядок гарантирует, что вызываемая функция получит из стека свои аргументы в правильном порядке.
  3. Вызвать подпрограмму.

По возможности функция сохранит результат в eax . Сразу после call вызывающая сторона должна:

  1. Удалить из стека аргументы функции. Обычно это делается путём простого добавления числа байтов в esp . Не забывайте, что стек растёт вниз, поэтому для удаления из стека необходимо добавить байты.
  2. Восстановить сохранённые регистры, забрав их из стека в обратном порядке инструкцией pop . Вызываемая функция не изменит никакие другие регистры.

Следующий пример демонстрирует, как применяются эти правила. Предположим, что функция _subtract принимает два целочисленных (4-байтовых) аргумента и возвращает первый аргумент за вычетом второго. В подпрограмме _mysubroutine вызываем _subtract с аргументами 10 и 2 :

Правила вызываемой подпрограммы

Перед вызовом подпрограмма должна:

  1. Сохранить указатель базового регистра ebp предыдущего фрейма, записав его на стек.
  2. Отрегулировать ebp с предыдущего фрейма на текущий (текущее значение esp ).
  3. Выделить больше места в стеке для локальных переменных, при необходимости переместить указатель esp . Поскольку стек растёт вниз, нужно вычесть недостающую память из esp .
  4. Сохранить в стек регистры вызываемой подпрограммы. Это ebx , edi и esi . Необязательно сохранять регистры, которые не планируется изменять.

Стек вызовов после шага 1:

Стек вызовов после шага 2:

Стек вызовов после шага 4:

На этих диаграммах в каждом стековом фрейме указан адрес возврата. Его автоматически вставляет в стек инструкция call . Инструкция ret извлекает адрес с верхней части стека и переходит на него. Эта инструкция нам не нужна, я просто показал, почему локальные переменные функции находятся на 4 байта выше ebp , но аргументы функции — на 8 байт ниже ebp .


На последней диаграмме также можно заметить, что локальные переменные функции всегда начинается на 4 байта выше ebp с адреса ebp-4 (здесь вычитание, потому что мы двигаемся вверх по стеку), а аргументы функции всегда начинается на 8 байт ниже ebp с адреса ebp+8 (сложение, потому что мы двигаемся вниз по стеку). Если следовать правилам из этой конвенции, так будет c переменными и аргументами любой функции.

Когда функция выполнена и вы хотите вернуться, нужно сначала установить eax на возвращаемое значение функции, если это необходимо. Кроме того, нужно:

  1. Восстановить сохранённые регистры, вынеся их из стека в обратном порядке.
  2. Освободить место в стеке, выделенное локальным переменным на шаге 3, если необходимо: делается простой установкой esp в ebp
  3. Восстановить указатель базы ebp предыдущего фрейма, вынеся его из стека.
  4. Вернуться с помощью ret

Теперь реализуем функцию _subtract из нашего примера:

Вход и выход

В приведённом примере вы можете заметить, что функция всегда запускается одинаково: push ebp , mov ebp , esp и выделение памяти для локальных переменных. В наборе x86 есть удобная инструкция, которая всё это выполняет: enter a b , где a — количество байт, которые вы хотите выделить для локальных переменных, b — «уровень вложенности», который мы всегда будем выставлять на 0 . Кроме того, функция всегда заканчивается инструкциями pop ebp и mov esp , ebp (хотя они необходимы только при выделении памяти для локальных переменных, но в любом случае не причиняют вреда). Это тоже можно заменить одной инструкцией: leave . Вносим изменения:

Написание некоторых основных функций

Усвоив соглашение о вызовах, можно приступить к написанию некоторых подпрограмм. Почему бы не обобщить код, который выводит «Hello world!», для вывода любых строк: функция _print_msg .

Здесь понадобится ещё одна функция _strlen для подсчёта длины строки. На C она может выглядеть так:

Другими словами, с самого начала строки мы добавляем 1 к возвращаемым значением для каждого символа, кроме нуля. Как только замечен нулевой символ, возвращаем накопленное в цикле значение. В ассемблере это тоже довольно просто: можно использовать как базу ранее написанную функцию _subtract :

Уже неплохо, верно? Сначала написать код на C может помочь, потому что большая его часть непосредственно преобразуется в ассемблер. Теперь можно использовать эту функцию в _print_msg , где мы применим все полученные знания:

И посмотрим плоды нашей тяжёлой работы, используя эту функцию в полной программе “Hello, world!”.

Хотите верьте, хотите нет, но мы рассмотрели все основные темы, которые нужны для написания базовых программ на ассемблере x86! Теперь у нас есть весь вводный материал и теория, так что полностью сосредоточимся на коде и применим полученные знания для написания нашего калькулятора RPN. Функции будут намного длиннее и даже станут использовать некоторые локальные переменные. Если хотите сразу увидеть готовую программу, вот она.

Для тех из вас, кто не знаком с обратной польской записью (иногда называемой обратной польской нотацией или постфиксной нотацией), то здесь выражения вычисляются с помощью стека. Поэтому нужно создать стек, а также функции _pop и _push для манипуляций с этим стеком. Понадобится ещё функция _print_answer , которая выведет в конце вычислений строковое представление числового результата.

Создание стека

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

Теперь можно реализовать функции _push и _pop :

Вывод чисел

_print_answer намного сложнее: придётся конвертировать числа в строки и использовать несколько других функций. Понадобится функция _putc , которая выводит один символ, функция mod для вычисления остатка от деления (модуля) двух аргументов и _pow_10 для возведения в степень 10. Позже вы поймёте, зачем они нужны. Это довольно просто, вот код:

Итак, как мы выводим отдельные цифры в числе? Во-первых, обратите внимание, что последняя цифра числа равна остатку от деления на 10 (например, 123 % 10 = 3 ), а следующая цифра — это остаток от деления на 100, поделенный на 10 (например, (123 % 100)/10 = 2 ). В общем, можно найти конкретную цифру числа (справа налево), найдя (число % 10**n) / 10**(n-1) , где число единиц будет равно n = 1 , число десятков n = 2 и так далее.

Используя это знание, можно найти все цифры числа с n = 1 до n = 10 (это максимальное количество разрядов в знаковом 4-байтовом целом). Но намного проще идти слева направо — так мы сможем печатать каждый символ, как только находим его, и избавиться от нулей в левой части. Поэтому перебираем числа от n = 10 до n = 1 .

На C программа будет выглядеть примерно так:

Теперь вы понимаете, зачем нам эти три функции. Давайте реализуем это на ассемблере:

Это было тяжкое испытание! Надеюсь, комментарии помогают разобраться. Если вы сейчас думаете: «Почему нельзя просто написать printf(«%d») ?», то вам понравится окончание статьи, где мы заменим функцию именно этим!

Теперь у нас есть все необходимые функции, осталось реализовать основную логику в _start — и на этом всё!

Вычисление обратной польской записи

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

Например, если мы хотим вычислить 84/3+6* (это выражение также можно записать в виде 6384/+* ), процесс выглядит следующим образом:

Шаг Символ Стек перед Стек после
1 8 [] [8]
2 4 [8] [8, 4]
3 / [8, 4] [2]
4 3 [2] [2, 3]
5 + [2, 3] [5]
6 6 [5] [5, 6]
7 * [5, 6] [30]

Если на входе допустимое постфиксное выражение, то в конце вычислений на стеке остаётся лишь один элемент — это и есть ответ, результат вычислений. В нашем случае число равно 30.

В ассемблере нужно реализовать нечто вроде такого кода на C:

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

Понадобится ещё добавить строку error_msg в раздел .rodata :

И мы закончили! Удивите всех своих друзей, если они у вас есть. Надеюсь, теперь вы с большей теплотой отнесётесь к языкам высокого уровня, особенно если вспомнить, что многие старые программы писали полностью или почти полностью на ассемблере, например, оригинальный RollerCoaster Tycoon!

Весь код здесь. Спасибо за чтение! Могу продолжить, если вам интересно.

Дальнейшие действия


Можете попрактиковаться, реализовав несколько дополнительных функций:

  1. Выдать вместо segfault сообщение об ошибке, если программа не получает аргумент.
  2. Добавить поддержку дополнительных пробелов между операндами и операторами во входных данных.
  3. Добавить поддержку многоразрядных операндов.
  4. Разрешить ввод отрицательных чисел.
  5. Заменить _strlen на функцию из стандартной библиотеки C, а _print_answer заменить вызовом printf .

AVR assembler, работа с памятью SRAM

Автор: Static, 14 марта 2020 в AVR

Рекомендованные сообщения

Присоединяйтесь к обсуждению

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

Похожие публикации

Добрый день.
Экспериментирую с платой TP4056 и MK ATmega8, конкретнее — пытаюсь собрать простенькое зарядное устройство с выводом параметров при зарядке/разрядке на АЦП МК. Для измерения тока нашел ACS712. Думаю, что получится выводить ток при зарядке (т.е. когда идет питание на TP4056 и нагрузка отключена) и при разрядке (питание выключено, нагрузка подключена). Нагрузка — резистор. Возникла проблема с измерением напряжения на АКБ. Вернее, проблема с отсутствием идей как это сделать. Может какую-нибудь схему делителя напряжения нужно сделать? Прошу помочь советом. Схему из пэинта прилагаю.
Спасибо.

Здравствуйте, только начинаю изучать программирование мк. Я уже задавал этот вопрос в моих прошлых схемах мне отвечали что-то типа «любой стабилизрованый источник постоянного напряжения», или кидали схему такого источника. Можно назвать мне конкретное устройство? В общем то что я смогу забить в гугл и приписать купить.

Я уже больше двух месяцев жду пока мне придут с aliexpress детали для маленького лабараторного блока, но этот вариант может разбиться о кривизну моих рук. Хочу просто купить такой источник и наконец заняться непосредственно МК.

Модуль memory asm

Глава 10. UPMM — Used Physical Memory Map.

Для того, чтобы обработчик страничного нарушения «знал», какие страницы физической памяти доступны и где именно они находятся, в нашем примере вводится битовая карта, которая будет называться UPMM (от англ. Used Physical Memory Map — карта использованной физической памяти). Эта аббревиатура и технология является число программным продуктом, придуманном мною лично, в процессорах Intel и их клонах нет ничего подобного; UPMM используется как один из возможных вариантов управления распределением памяти, при этом относительно простым и надёжным.

Илон Маск рекомендует:  Что такое код asp logodbcdatasource

UPMM по сути представляет собой массив размером 4Кб, а по смыслу — битовую карту размером 32 Кбита. Каждый бит UPMM соответствует одной физической странице памяти; если он сброшен (т.е. равен 0), значит страница — свободна, если установлен (1) — то занята.

Работа с UPMM происходит блоками по 32 бита, т.е. если, например, нужно обратиться к 49-биту, производится чтение двойного слова по смещению 4 относительно начала UPMM, а уже в этом двойном слове обрабатывается 17-й бит.
Обработка битовой карты блоками по 32 бита позволяет максимально быстро обращаться к ней — ведь всё равно процессор тратит одинаковое число тактов при обращении к байту или двойному слову в памяти (если, конечно, процессор — не SX), а оптимизация по скорости у многих ранних моделей вообще относится только к командам, обрабатывающим 32-разрядные операнды.

Для удобства работы с UPMM, для неё создаётся отдельный сегмент данных, доступный через селектор UPMM_sel. Размер этого сегмента — 4Кб, что позволяет описать до 128Мб физической памяти. Для демонстрации страничного преобразования этого вполне достаточно, но для полнофункционального обработчика понадобится сегмент размером 128Кб, чтобы описать 4Гб памяти.

Установка сегмента, описывающего UPMM производится перед переходом в защищённый режим следующим образом:

Здесь в переменной UPMM_size содержится размер сегмента (4Кб). Для .com-программы его можно менять в небольших пределах, т.к. при работе наших примеров подразумевается, что они, во-первых, являются .com-программами, а во-вторых, вместе со всеми данными, занимают не более 64Кб памяти.

Прежде, чем будет создана UPMM, необходимо определить её размер в dd-переменной UPMM_size, а также какие страницы физической памяти 1-го мегабайта уже заняты и какие — свободны. Для этого предусмотрена процедура «setup_for_UPMM»:

Для создания UPMM используется процедура «create_UPMM». Она способна создать UPMM размером до 128Кб, но используется для 4 килобайтовой UPMM. Эта процедура вызывается сразу после процедуры «get_phys_mem_size» и в качестве входного параметра использует размер памяти, возвращённый ею в EAX. Процедура «create_UPMM» использует также dd-переменную UPMM_size и макрос UPMM_sel — селектор дескриптора сегмента, описывающего UPMM:

Для поиска свободной страницы в UPMM используется процедура «find_free_phys_page», которая, по сути, ищет первый нулевой бит в UPMM. После того, как он будет найден, процедура вычисляет адрес физической страницы, которой соответствует этот бит и возвращает его в EAX.
При возврате из этой процедуры флаг переноса CF (в EFLAGS) содержит информацию о процессе поиска — если он сброшен, значит всё в порядке, страница найдена и её адрес находится в EAX. Если же CF установлен, то в UPMM больше нет нулевых бит, следовательно, вся доступная физическая память уже занята. Везде в нашем примере после вызова этой процедуры проверяется флаг CF и если он установлен, выводится сообщение о недостатке памяти и производится аварийное прекращение программы. При реализации системы виртуальной памяти, в таком случае следовало бы найти наименее используемую физическую страницу, свопнуть её на диск и использовать её далее как свободную страницу, но в нашем примере внимание концентрируется именно на обработчике исключения страничного нарушения, а не механизме виртуальной памяти.

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

MPU (Memory Protection Unit)

MPU (англ. Memory Protection Unit – Блок защиты памяти) – это блок процессора, отвечающая за защиту распределение памяти между процессами, отслеживает случаи обращения к запрещенным для текущей рабочей программы участкам, или страницам, оперативной памяти и организацию для них прерываний в реализации программ.

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

Содержание

Назначение

MPU необходим для исключения изменения какой-либо информации, находящейся в ОП. Блок защиты памяти (БЗП) является средством, которое обеспечивает мультипрограммную работу ЭВМ. Когда на ЭВМ исполняется несколько программ, каждая из них обладает своим адресным пространством, назначенным процессором именно ему. При этом любая программа может работать только в своем адресном пространстве и обращения «чужим» адресам могут вызвать прерывание и прекращение работы программы. Обращение к таким адресам может произойти из-за неправильной разработке ПО или неисправности оборудования. Конструктивно МОЗУ совмещено с блоком защиты памяти, который предохраняет массивы информации, хранящиеся в запоминающем устройстве, от ошибочной записи в них посторонних кодов.

Когда происходит прерывание, содержимое регистра ключей защиты через блок управления каналами передается в MPU, где он сравнивается с ключом памяти. В случае несовпадения содержимого регистра ключей защиты канала с ключом памяти передается сигнал сбоя, и выполнение операций ввода-вывода прекращается.

Фиксация сигналов о сбоях производится в регистре ошибок (РО); регистр РБЗ является информационным для блока защиты памяти; эти регистры также относятся к числу служебных регистров.

В состав процессора могут входить сверхоперативная быстродействующая память небольшой емкости (СОЗУ), блок прерывания, блок защиты памяти, блок контроля правильности работы и диагностики процессора и другие блоки. Оперативное запоминающее устройство и каналы связи с периферийными устройствами выполняются в виде отдельных устройств, хотя в небольших ЭВМ могут конструктивно объединяться с процессором. [Источник 1]


Во встроенных системах данный блок применяется для повышения их устойчивости и надежности с помощью:

  • запретов пользовательским приложениям на доступ к данным, используемыми важными процессами (например, ядром операционной системы).
  • запретов на исполнение кода из определенных участков памяти для предотвращения непроизвольного доступа и порчи данных.
  • специальных атрибутов доступа к памяти.

По сравнению с MMU, MPU не имеет буфера ассоциативной трансляции (TLB – Translation lookaside buffer), а также в нем нет таблицы страниц. (рис. 1)

Рисунок 1 – Сравнение MMU и MPU

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

В MPU все виртуальное адресное пространство сопоставляется к физическому как 1 к 1. По этой причине использование MPU популярно в системах реального времени.

MPU в Cortex-M

MPU в Cortex-M может быть использован для защиты 8 регионов памяти. Каждый из них имеет свой номер (от 0 до 7), который соответствуют их приоритету. Таким образом, номера участка памяти в MPU – это их унифицированные идентификационные номера в системе. Также существуют специальные флаги доступа (на запись, чтение, исполнение и кэширование) которые можно выставлять регионам.

Участки памяти в MPU всегда содержит следующие атрибуты: * номер участка в MPU.

  • адрес участка памяти.
  • флаги доступа к участку и его размер.

При разработке драйвера MPU следует учитывать, что участки памяти могут пересекаться между собой. Тогда уровень доступа к общему региону памяти определяется по участку с наибольшим приоритетом.

Также адрес участка памяти должен соотноситься со своим размером. Например, если размер составляет 64КB, то адрес должен быть кратен 64КВ. Так как 64КВ эквивалентно 0x10000, то адрес участка должен быть равен 0x10000, 0x20000 и так далее. Это необходимо для того, чтобы участок памяти был выровнен по своему размеру.

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

Драйвер MPU

Поддержка флагов доступа

Всего под атрибуты каждого участка памяти выделено 28 бит, как показано в таблице ниже:

Биты Имя флага Описание
28 XN Доступ на исполнение
26:24 AP Флаги доступа к данным
21:19 TEX Тип блока памяти
18 S Разделяемая память
17 C Возможность кэширования
16 B Возможность буферизации
15:8 SRD Флаги субрегионов
5:1 SIZE Определяет размер участка

Флаг XN контролирует исполнение кода. Для выполнения инструкции в пределах участка памяти, необходимо иметь доступ на чтение в привилегированном режиме и XN должен быть равен 0. В противном случае возникнет исключение.

Поле разрешения доступа (AP) определяет доступность участка памяти для чтения и записи в зависимости от режима (пользователя или ядра).

Обработка исключений

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

Исключение вызывается только в случае высокого приоритета в режиме исполнения (потока).

В случае появления исключения с более высоким приоритетом, оно вытесняет старое. При вытеснении предыдущее исключение сохраняется на стеке. Его обработка продолжается после завершения нового.

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

После попадания в стек, указатель указывает на самый первый адрес. Смещением кадра управляет бит STKALIGN в регистре управления конфигурацией.

В стековом кадре хранится адреса следующей инструкции в прерванной программе.

Одновременно с операциями на стеке выполняется чтение стартового адреса обработчика исключений из таблицы векторов прерываний. Обработчик задействуется, когда операции на стеке завершаются. В тоже время на стек записывается значение в LR. Это указывает, какой указатель на стек соответствует стековому кадру, и в каком режиме находился процессор перед тем, как произошло исключение.

Если не возникло исключения c более высоким приоритетом во время входа, то вызывается обработчик исключения, при этом статус соответствующего ожидающего прерывания меняется на действующий.

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

  • загружается регистр PC.
  • загружается значение на PC через инструкцию LDR. * затем создается ветка с обработчиком исключения.

Переключение контекстов


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

Эта проблема была решения с помощью исключений. В процессорах архитектуры Cortex-M 3/4 обработчик исключений находится всегда в привилегированном режиме, поэтому переключение можно сделать через вызов супервизора (SVC).

Для переключения контекста в системе использовались прерывания и стеки. Во время переключения, система должна сохранять состояние предыдущих задач, чтобы восстановить их при очередном переключении. При переключении контекста необходимо:

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

Обновление флагов доступа

Рисунок 2 – Обновление флагов блока памяти

Для обновления атрибутов блока памяти необходимо, чтобы были выполнены следующие действия (рис. 2):

  • Данный участок памяти должен быть отключен.
  • Чтобы избежать неопределенного поведения, нужно отключить прерывание.
  • Также перед обновлением MPU необходимо выполнить инструкции DSB и ISB. Это нужно для того, чтобы быть уверенными, что все незавершенные операции с памятью выполнены.
  • Вышесказанные инструкции не требуются, если процедура обновления проходит в обработчике исключений, так как в этом случае завершение операций автоматически произойдет во время входа и выхода из исключения.

Реализация в ARM-архитектуре

MPU может быть использован для повышения устойчивости и надежности системы, предоставляя следующие возможности:

  • MPU запрещает пользовательскому приложению изменять данные, используемые критическими задачами (такой, как ядро операционной системы).
  • MPU определяет область памяти SRAM как неисполняемую для предотвращения атаки внедренного кода.
  • MPU изменяет атрибуты доступа к памяти.

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

Области пронумерованы от 0 до 7. В дополнение есть другая область, названная областью по умолчанию, с идентификатором -1. Все области памяти в диапазоне от 0 до 7 имеют приоритет над областью по умолчанию.

Области могут перекрываться, и могут быть вложенными. Область 7 имеет более высокий приоритет, область 0 низкий, и это определяет поведение перекрывающихся областей. Приоритеты фиксированы и не могут быть изменены.

Рис. 3 показывает пример с 6-ю областями. Этот пример показывает, что область 4 перекрывает области 0 и 1. Область 5 включена полностью в область 3. Так как приоритет идет в порядке возрастания, у областей перекрытия (желтые) есть приоритет над другими областями. Таким образом, если область 0 определена как доступная для записи, а область 4 наоборот, недоступная, то адрес, попадающий в зону перекрытия областей, не будет доступен для записи.

Рисунок 3 – Пример перекрытия областей

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

MPU может быть использован для определения других атрибутов памяти, таких как кэширование, которые могут быть экспортированы (применены) в системном блоке кэш-памяти или в контроллере памяти.

Илон Маск рекомендует:  Что такое код current

Управление кэшем происходит глобально посредством регистра команд кэша, но MPU может определить политики кэша, и будет ли область кэшируемой или нет. MPU позволяет установить атрибуты кэша для области кэшем уровня L1 (доступно только для серии STM32F7, у которой есть кэш L1).

Модель памяти. В архитектуре STM32 процессор имеет по умолчанию фиксированную карту распределения памяти, которая предоставляет до 4 гигабайт адресуемой памяти. На рис. 4 показана карта распределения памяти.

Рисунок 4 – Распределение адресного пространства процессора

Типы памяти, регистры и атрибуты

Карта распределения памяти и настройка MPU разделяют карту на области. Каждая область имеет определенный тип памяти и её атрибуты.

Есть три общих типа памяти:

  • Память Normal: позволяет загружать и хранить байты, полуслова и слова, которые будут эффективно расположены процессором (компилятору неизвестно о типах областей памяти). Для этой области загрузка/хранение не обязательно выполняется для процессора в том порядке, который определен в исходном коде программы.
  • Память Device: в этом регионе загрузка и хранение совершаются в строго упорядоченном виде. Это должно гарантировать, что последовательность загрузки регистров выполнена в надлежащем порядке.
  • Строго упорядоченная память: всегда выполняется в порядке, написанном в программе, ожидает завершения инструкции записи/хранения (эффективный доступ к шине) прежде, чем выполнить следующую инструкцию в потоке программы. Это может серьезно увеличить производительность.


Регистры MPU. Регистры MPU расположены по адресу 0xE000ED90. Есть 5 основных регистров MPU и несколько регистров-псевдонимов (alias registers) для каждой области. Следующие регистры используются для установки областей в MPU:

  • MPU_TYPE регистр только для чтения, используется для детекции наличия MPU.
  • MPU_CTRL регистр управления.
  • MPU_RNR номер области, используется для определения допустимых операций к ней.
  • MPU_RBAR базовый адрес области.
  • MPU_RASR атрибуты области и размер.
  • MPU_RBAR_An n-й псевдоним MPU_RBAR, где n в диапазоне от 1 до 3(a).
  • MPU_RASR_An n-й псевдоним MPU_RASR, где n в диапазоне от 1 до 3(a).

Примечание (a): В Cortex®-M0+ эти регистры не реализованы. [Источник 2]

Другие методы защиты памяти

Метод граничных регистров

Рисунок 5 – Метод граничных регистров

Идея метода состоит в том, что вводят два граничных регистра, указывающих верхнюю и нижнюю границы области памяти, куда программа имеет право доступа (см. рис. 5).

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

Метод ключей защиты

Рисунок 6 – Метод ключей защиты

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

Метод защиты отдельных ячеек

В небольших управляющих вычислительных устройствах, работающих, например, в составе АСУ ТП, необходимо обеспечить возможность отладки новых программ параллельно с функционированием находящихся в памяти рабочих программ, управляющих технологическим процессом. Этого можно достичь выделением в каждой ячейке памяти специального разряда защиты. Установка 1 в этот разряд запрещает производить запись в данную ячейку. Это так называемый метод контрольного разряда. [Источник 3]

Обзор двух USB-коробочек для NVMe-накопителей на чипах ASMedia ASM2362 и JMicron JMS583

Оглавление

Тестируя в конце прошлого года внешний SSD Samsung T5, мы убедились в очевидной вещи: при сохранении «традиционной» схемы из SATA-накопителя и моста USB—SATA переход на «полноценный» USB 3.1 Gen2 (а Т5 — как раз одна из первых массовых «флэшек», поддерживающих эту версию стандарта) особого смысла не имеет. Причина? Не менявшийся уже более 10 лет SATA банально. стал медленнее USB.

Такого на рынке внешних накопителей не было никогда. Были моменты во время внедрения новых версий USB (сначала 2.0, а потом и 3.0), когда от них отставали сами носители данных, но не их интерфейсы. Да и отставали-то не слишком долго — на момент появления USB 2.0, например, ноутбучные винчестеры (на тот момент самый подходящий для этой области носитель данных) имели максимальную скорость в ≈20 МБ/с, но во время его массового использования и до выхода в свет USB 3.0 «ушли» за 60 МБ/с хотя бы в начале диска. В любом случае, использовали они в то время интерфейсы типа АТА66, АТА100, а дальше и разные модификации SATA, которые в каждый момент времени были быстрее, нежели существовавшие версии USB.

Но сама медлительность носителей как раз и начала вынуждать наиболее требовательных пользователей мигрировать на внешние SSD — которые получались как правило в результате «прикручивания» моста с поддержкой уже существовавшего USB 3.0 к внутреннему SSD с SATA600 (первые модели поддерживали только SATA300, но они в любом исполнении не были массовыми из-за высокой цены — и во многом вследствие этого низкой емкости). Первое время делалось это при помощи «стандартных» коробочек для винчестеров и, соответственно, SSD в форм-факторе 2,5” 7 мм, затем появились более компактные «карточные» твердотельные накопители (mSATA и M.2), которые лучше подходили для «карманного» использования, так что многие компании освоили выпуск уже специальных коробочек для них, а то и вовсе законченных решений на этой базе — и всех все устраивало. «Красоты» появившегося около пяти лет назад USB 3.1, правда, пришлось демонстрировать при помощи странноватых мутантов, типа внешнего RAID0 на паре SATA SSD, но самая скоростная (возможно — уже и навсегда) версия стандарта долгое время была лишь экзотикой, так что положения дел не меняла. Но год назад Intel встроил поддержку USB 3.1 в свои чипсеты, AMD сделала это еще раньше — и дорогая экзотика (внезапно!) превратилась в массовое решение. Которому нужны были соответствующие накопители, что привело к обновлению ассортимента их производителей. Но вот оказались они (как и следовало, конечно, ожидать) не совсем соответствующими.

Есть ли решение проблемы? Да — твердотельные накопители с интерфейсом PCIe появились достаточно давно, а после разработки протокола NVMe (учитывающего их особенности в отличие от древнего AHCI — изначально дебютировавшего совместно с винчестерами или, даже, еще более медленными накопителями) они постепенно начали становиться массовыми. Одна линия PCIe 3.0 медленнее режима SuperSpeed10 (самого быстрого в рамках USB), а вот двух уже достаточно — но как раз столько и используется в бюджетных SSD этого сегмента. «Небюджетные» рассчитаны на PCIe 3.0 х4, но могут работать и в режимах х2 и даже х1 — так что тоже подойдут. Просто нужны соответствующие контроллеры, первый из которых появился приблизительно год назад — им стал JMicron JMS583. К концу прошлого года свет увидело аналогичное, но чуть более дешевое решение от ASMedia — ASM2362. Впрочем, более дешевое оно лишь относительно — коробочки на втором стоят порядка $15, а на первом — $20 и более, в то время, как «обычный» USB—SATA (типа используемого в том же Samsung T5 ASM235) позволяет уложиться и в 10 долларов, а то и менее. Кроме того, пока еще дороже стоят сами NVMe-накопители — паритет с SATA-собратьями той же емкости ожидается лишь к концу года. В итоге нет ничего удивительного в том, что эти мосты пока еще как правило игнорируются производителями «готовых» внешних SSD: в обоих случаях получается накопитель с USB 3.1, а попробуй еще объясни нормальному «человеку с улицы», что более дорогое устройство, но с тем же интерфейсом просто заметно быстрее из-за внутренних особенностей. Энтузиастам же никто не мешает приобрести подобную коробочку самостоятельно, добавить к ней SSD «по вкусу» и пользоваться в свое удовольствие. Поэтому мы решили протестировать оба варианта — и заодно познакомить с ними широкие массы трудящихся: вдруг им такое тоже нужно, а они об этом еще не знают :)

Заметим, что есть и более радикальный способ решения проблемы — Thunderbolt 3, скоростные возможности которого полностью соответствуют возможностям PCIe 3.0 х4, т. е. и самым быстрым SSD тоже. Такой вариант мы тоже тестировали и он нам понравился всем, кроме. Цены и низкой распространенности самого интерфейса. Впрочем, сейчас он «превратился» в USB 4.0, да и Intel грозится встроить его поддержку непосредственно в чипсеты, но вот совместимости с «классическим» USB 2.0/3.x у него от этого не появится. Поэтому USB 3.1 долго сохранит свою актуальность, несмотря на ограниченную производительность — снабженный им накопитель можно хоть к телевизору подключить, хоть к старому компьютеру, а с Thunderbolt 3 такой номер не пройдет. В общем, даже если в ближайшее время этот интерфейс и станет стандартом для новых топовых ПК — то только для них, в случаях, когда действительно нужен максимум производительности.

Испытуемые

Как сказано выше, «производство» готового накопителя в данном случае целиком и полностью ложится на плечи покупателя — который для начала должен где-то приобрести саму коробочку. Мы сделали это при помощи Aliexpress, дальше привычные 10 дней ожидания, пока посылки дойдут до Московской области, распаковка.

Устройства выглядят как братья (пусть и не близнецы): солидный брусок из анодированного алюминия с закрепленными болтиками торцевыми крышечками. Коробочка на JMS583 покрупнее: 125×40×10 мм. На ASM2362 — компактнее: 105×35×12 мм.

OldStuff — История оперативной памяти SIMM

Привет друже, есть у меня в коллекции вот такая кучка оперативки формата SIMM (предшественник DIMM)
И я хочу тебе поведать немного о них

В 1983 компания Wang Laboratories подала патент на новый тип оперативной памяти для компьютеров. Который назвала SIMM или «Single In-line Memory Module» что по нашему значит односторонний модуль памяти. Разработала она его всё в том же 1983 году. Как говорит Википедия — «Первоначально модули были керамическими и имели штырьки.» (фото не нашёл, у кого имеются буду весьма признателен)
Ссылка на патент — http://www.google.com/patents/US5973951
Для ЛЛ ниже изображения из патента:

Первые версии SIMM модулей памяти не имели фиксации в слотах на материнских платах. Но очень скоро начали применять ZIF-слоты с защёлками.
Самые первые оперативки SIMM были всего на 30 контактов и имели объем памяти от 64 килобайт до аж целых 16 МЕГАБАЙТ. (Представляете? В то время это было много) Шина данных у них была восьмиразрядной, но иногда добавляли девятую линию контроля четности памяти.

Применялися они в компьютерах с Центральным Процессором, он же попросту ЦП, Intel 8088, 286, 386.

Там где стоял ЦП 8088, модули ставились по одному, если процессоры были 286, 386SX то модули ставились парами, ну а на компьютеры с процессорами 386DX — по четыре модуля ОДИНАКОВОЙ ёмкости.

Когда до рынка массовых (персоналок) компьютеров добрался и надёжно укрепился процессор 80486 фирмы INTEL, а также ему аналогичные для которых 30 пиновые SIMM нужно было ставить КАК МИНИМУМ ЧЕТЫРЕ!

Тогда на свет появилась версия на 72 контакта (пина), которая по сути была не чем иным как 4 объединённых 30 пиновых SIMM в одном флаконе так сказать, с общими линиями адреса и раздельными линиями данных. Таким образом, модуль становится 32-разрядным и достаточно было всего одного модуля.

Теперь на одной планке размещалось от 1 мегабайта аж до целых космических 64 МЕГАБАЙТ (Чувствуете? чувствуете какую мощь они привнесли?)

Изначально в эпоху процессора INTEL 486 они появились только на брэндовых PC таких как Compaq, HP, Acer и других. Но, с приходом в этот прекрасный мир процессора Pentium их начали масово ставить практически на все материнские платы.
С распространением в массовых компьютерах процессоров Pentium, по причине низкого быстродействия динамической памяти SIMM-модулей, их спецификация претерпела изменения, в результате чего более новые модули, обладая немного большим быстродействием (их называли EDO от англ. Extended Data Out) стали несовместимы со старыми (FPM от англ. Fast Page Mode).

Так как на материнских платах для процессора Pentium с 64-разрядной шиной данных 72-контактные модули уже потребовалось ставить парами, постепенно и их физически попарно «объединили» путём расположения микросхем на обеих сторонах печатной платы модуля памяти, результатом чего стало появлением первых модулей DIMM.

Материнские платы для процессоров Pentium, как правило, поддерживали оба типа памяти, в то время как большинство материнских плат для процессора 486 поддерживали только старый (FPM) тип модулей памяти. Отличить модули по внешнему виду было практически невозможно (внешнее отличие было только в маркировке микросхем), и на практике чаще использовался метод «научного тыка». Установка «неправильного» типа памяти не приводила к неисправностям — система просто не видела памяти.

Существовали также 64-контактные (применявшиеся например в Macintosh IIfx) и 68-контактные (VRAM в Macintosh LC) варианты.

А вот и фото моих экземпляров из личной коллекции

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL