Как мне управлять памятью с помощью моего собственного компилятора? - PullRequest
0 голосов
/ 31 октября 2011

Я создаю симулятор для 8-битного процессора на Java. Его архитектура очень проста, всего 4 байта и 256 байтов основной памяти. Я уже реализовал «аппаратный» стек, поэтому процессор поддерживает PUSH, POP и GET. Стек заполняется в обратном направлении от последней ячейки памяти, поэтому «нормальное» использование памяти должно начинаться с ячейки 0. Вам не нужно резервировать память, программа может использовать полные 256 байт по умолчанию.

Я также создаю компилятор для этого процессора, который компилируется из простого языка, который я изобрел. На данный момент каждая определенная переменная (я просто поддерживаю один 8-битный целочисленный тип) присваивается одной ячейке памяти, начиная с 0 и увеличиваясь. Таким образом, у меня максимум 256 (если стек пуст) переменных. В настоящее время я не хочу это менять.

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

Ответы [ 3 ]

2 голосов
/ 31 октября 2011

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

+------------------+
|  Return address  |
+------------------+
|   Parameter 0    |
+------------------+
|       ...        |
+------------------+
|   Parameter N    |
+------------------+
|   Local Var 0    |
+------------------+
|       ...        |
+------------------+
|   Local Var N    |   <--- Top of Stack
+------------------+

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

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

+--------------+
|   JMP FUNC   | Call the function
+--------------+
|  Parameter 0 | <--- return address points here
+--------------+
|     ...      |
+--------------+
|  Parameter N |
+--------------+
| (other code) | <--- after function call, return address should be fixed to point here
+--------------+

Это означает, что вам придется проделать немного больше работы внутри функции, чтобы загрузить параметры. Если вы помните, адрес возврата - это первое значение в стеке. Вы можете загрузить это значение, а затем индексировать его, чтобы загрузить ваши параметры. Как только вы закончите загружать ваши параметры, вам нужно будет настроить значение вашего обратного адреса так, чтобы он указывал на код, который запускается после параметров, которые вы определили.

1 голос
/ 31 октября 2011

Возможно, есть несколько правильных решений. Вы можете разместить переменные с данными вызова / возврата в «аппаратном» стеке или реализовать стековую схему для «памяти», чтобы «верх памяти» сохранялся при вызове и извлекался при возврате. И, без сомнения, 2 или 3 другие схемы.

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

Возможно, вы захотите изучить, как это было на старых машинах Берроуза .

1 голос
/ 31 октября 2011

Конечно, было бы неплохо хранить два стека, но тогда вы застряли с решением, куда поместить два стека. Должно ли аппаратное обеспечение начинаться с 255, а программное обеспечение - с 127? Это сократит вашу эффективную память в два раза, а также сократит глубину рекурсии вдвое, поскольку у вас будет всего 127 слотов для вызовов функций.

Чередование стеков - аппаратное обеспечение на 255 и программное обеспечение на 254, с каждым последующим скачком значения на 2 точки решает потерю диапазона памяти, но тогда вы все равно тратите память, если / когда стеки разбалансированы (10 аппаратных средств в очереди слоты стека, 20 используемых слотов программного обеспечения = 10 слотов потрачено впустую), а также в любом случае сократили адресное пространство стека вдвое

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...