Files
obsidian/Computer Science/Архитектуры/f32a.md
T
roma-dxunvrs fb5f9d4cf2 Add f32a
2026-09-18 13:47:58 +03:00

88 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
- 32-битная стековая архитектура
- Главный принцип - команды не имеют операндов в коде. Все данные неявно берутся с веришны стека, результат кладется обратно в стек.
- Небольшая пасхалка на [[Evaluate Reverse Polish Notation|вот эту задачу с литкода]]
## Регистры, флаги и стеки
- Имеет два независимых аппаратных стека и два регистра-указателя
### Стеки
- `Data Stack` - место где происходят вычисления
- `T` - вершина стека данных
- `S` - второй элемент на стеке данных
- `Return Stack` - стек возврата
- `R` - вершина стека возвратов
### Регистры
- `A` - указатель адреса памяти с поддержкой автоинкремента
- `B` - второй указатель памяти
### Флаги
- `C` - перенос
- `EAM` - режим расширенной арифметики
> [!info] Все регистры, флаги и стеки можно выводить в отчете: `A:dec`, `stack:dec`, `rstack:hex`, `C`, `EAM` и так далее
> [!info] Получение максимальной глубины стека, которая была достигнута на конкретном шаге через: `f32a:data-stack-max` и `f32a:return-stack-max`
## Система команд
- Комментарии - `\`
- Опкод - 1 байт. Если есть операнд, то еще 4 байта
### 1. Операции со стеком
- `dup` - дублировать вершину (`[X] -> [X, X`])
- `drop` - удалить вершину (`[X, Y] -> [X]`)
- `over` - скопировать второй элемент наверх (`[X, Y] -> [X, Y, X]`)
### 2. Операции над памятью
- `<value>` (или `lit <value>`) - положить константу на `Data Stack` (от слова literal)
- `a!` / `b!` - снять значение со стека и записать в регистр `A` / `B`
- `a` - скопировать значение из регистра `A` на стек
- `@p <addr>` - прочитать из адреса на стек
- `!p <addr>` - снять со стека и записать по адресу
- `@` / `!` - чтение/запись по адресу, который лежит в регистре `A`
- `@+` / `!+` - чтеение/запись по адресу в `A` и инкремент `A`
- `@b` / `!b` - чтение/запись по адресу, который лежит в регистре `B`
### 3. Арифметика
- `+` - сумма, снимает два верхних числа, складывает, кладет на вершину стека. Если в режиме `EAM`, то прибавляет еще и флаг переноса. Устанавливает флаг переноса в любом случае
- `+*` - один шаг умножения
- `+/` - один шаг деления
- `2*` - сдвиг влево вершины стека
- `2/` - сдвиг вправо вершины стека
- `inv` - побитовая инверсия вершины стека
- `eam` - снять число с `Data Stack` и записать его в `EAM`
### 4. Побитовые операции
- `and` - побитовое И над `T` и `S` (снимает их)
- `xor` - исключающее ИЛИ
### 5. Управление вычислительным процессом
- `;` - возврат из подпрограммы (снять адрес с `Return Stack` и прыгнуть)
- `<label>` - вызов подпрограммы (адрес возврата кладется на `Return Stack` + прыжок)
- `<label> ;` - прыжок (без записи адреса возврата)
- `next <label>` - цикл for, проверяет вершину стека возврата `R`, если `R != 0`, уменьшает его на 1 и прыгает на метку. Если `R==0`, удаляет `R` и выходит из цикла
- `if <label>` - снимает значение с `Data Stack`, если оно ноль, то прыжок
- `-if <label>` - снимает значение с `Data Stack`, если оно `>=0`. то прыгает
- `halt` - стоп
### 6. Взаимодействие со стеком возврата
- `r>` - снять элемент с `Return Stack` и положить на `Data Stack`
- `>r` - снять элемент с `Data Stack` и положить на `Return Stack`
## Подробнее про умножение и деление
### `+*`
- Как работает умноежение чисел в двоичной системе (`A*S`): смотрим на каждый бит числа A, если бит 1, то прибавляем S к результату, если бит 0, то пропускаем, в конце сдвигаем результат
- Как использует эта команда данные
1. Регистр `A` - множитель
2. `S` (второй элемент стека данных) - множимое
3. `T` (первый элемент стека данных) - накапливается ответ, сначала 0
- Теперь эта команда по шагам
1. Процессор смотрит на младший бит `A`
2. Если там `1`, то прибавляет `S` к `T` (`T = T + S`). Если `0` - ничего не прибавляет
3. Сдвигает регистр `A` вправо на 1 бит (`A >> 1`). Тот бит, который мы только что проверили выбрасывается
4. Сдвигает результат `T` вправо на 1 бит (`T >> 1`)
5. Сдвинутый из `T` бит вставляется в старший бит `A` (`A[31]`)
- После 32 повторений такой команды, в регистре `A` и на вершине стека `T` образуется 64-битное число - полный результат умножения. В `A` - младшая половина ответа, а в `T` - старшая
### `+/`
- Деление в двоичной системе: сдвигаем влево и пытаемся вычесть делитель. Если вычитается - пишем 1, если нет - 0
- Как использует данные команда
1. Регистр `A` - делимое
2. `Mem[B]` - делитель
3. `S` - старшая часть числа, здесь будет остаток
4. `T` - кладем 0, здесь будет частное
- Команда по шагам
1. Сдвигаем остаток `S` влево на 1 бит
2. Забираем старший бит из `A` и вставляем в младший бит `S`
3. Сдвигаем `A` влево
4. Сдвигаем `T` влево
5. Если можно вычесть делитель (`S >= Mem[B]`), то ставим в младший бит ответа единицу (`T[0]=1`), если нельзя - бит ответа оставляем нулем
- После 32 повторений на стеке в `T` будет лежать частное, в `S` - остаток от деления.