Перейти к публикации
Alleksh

redpower Гайд по RedPower. Компьютеры. Low-Level.

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


Часть №1. Понимание системы.

 


Сейчас я вам поведаю о том, как формируются слова в памяти.
Для начала - создадим функцию DUMP, она будет принимать на вход адрес и два значения:


( addr n1 n2 -- ) DUMP 

Выводит n1 строк по n2 байт по адресу addr.
Код:

 


VARIABLE PARAM
DECIMAL
: DUMP
16 RADIX ! PARAM ! 
0 DO CR
DUP I PARAM @ * + DUP U. BS ." : "
PARAM @
0 DO DUP I + C@ DUP 16 < IF ." 0" THEN . LOOP
PARAM @ 0 DO DUP I + C@ EMIT LOOP
DROP LOOP ;

Когда вы напишете в консоли


 ' %word%

В стек запишется адрес исполняемой части слова, которую можно выполнить написав EXECUTE.
Попробуем:
image.png.296c4271aa1b0091f65e12a7ac651b37.png
Да, это работает отлично! Но как именно..?
Инструкции RedCPU:
http://www.eloraam.com/nonwp/redcpu.php

Каждое созданное Вами слово будет начинаться с 0x22, что является инструкцией ENT.
Каждая следующая инструкция после ENT будет указателем на исполняемую часть другого слова.
Например:
image.png.599a759a1b9bf2884cef1af336d7235b.png
Примечание:
20 21 01...  - мусор из ОЗУ. Главное - первые 7 байт.

Как мы можем заметить  - начинается она с 0x22.
Что же значит DE 2D, F9 2D, 5C 05?
А это - указатели на функции.
Давайте посмотрим на адреса TEST, TEST2:
image.png.85dc9bf1c3769f333796520caa7ba3cb.png
Да! Всё записалось отлично!
Примечание: 
Каждая цифра, адрес и т.п. записывается наоборот(записав 0x4488 в памяти окажется 0x88 0x44). 

У вас, возможно, мог возникнуть вопрос:
-А что же находится по адресу 0x55C?
Что же, давайте воспользуемся командой >NAME (она берёт из стека адрес функции и ищет её имя ) :
image.png.a2a47735ece4530ad2f583471f68a932.png
А это - функция EXIT.
Это не удивительно, так как далее находится всякий мусор, который нам явно не нужно исполнять.
А теперь давайте теперь узнаем, как записываются числа и строки, ведь у них нет определенного адреса:
image.png.de45e5ddeb5baabdf383cf788462c9d4.png
Первая использует какую-то функцию по адресу 0xFE2, а вторая - 0x550.
Давайте узнаем, что это:
image.png.59e3e7cae381512eaa0397cabeca73d7.png
Как можно увидеть - используются специальные слова, которые определяют вхождение в строку и число. Так же нужно отметить, что строка заканчивается на 0x00.
Давайте теперь, учитывая это, напишем декомпилятор:

 


HEX VARIABLE NOW_ADDR
: DECOMPILE
OVER 1 + NOW_ADDR !
0 DO CR NOW_ADDR @ U. BS ." : "
NOW_ADDR @ @
DUP 550 = IF DROP NOW_ADDR @  2 + @ . NOW_ADDR @ 4 + NOW_ADDR !
ELSE FE2 = IF NOW_ADDR @ 
BEGIN 1 + DUP C@ DUP EMIT ."  " 0= UNTIL
NOW_ADDR !
ELSE NOW_ADDR @ @ >NAME TYPE NOW_ADDR @ 2 + NOW_ADDR ! 
THEN THEN LOOP ;

Давайте теперь попробуем декомпилировать функцию SAVE":
image.png.715a0cd6a38849a3bbcbe98cf27c57d8.png
Всё сработало отлично!
Что это за "крякозябры" у do/loop?
Давайте воспользуемся DUMP:
image.png.37ea6bcc2abee2265a3353fdcfedaa68.png
Всё просто!
Это - адреса, куда нужно переходить в том или ином случае.
У DO - 1B2D, что является выходом. То есть, когда у нас первый итератор превышает другой - начинаем выполнять код по адресу 1B2D.
У LOOP - 1B1B, что есть началом цикла(DUP).

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

Часть №2. Ускорение заводов.

 

Сейчас мы поговорим о том, как можно ускорять свои заводы, исполняя меньшее кол-во действий.
Для начала Вам необходимо кое-что знать:
Для того, чтобы подключиться к устройству - необходимо использовать команду " RBP! ", ID устройства должен находится в стеке.

Взаимодействие проходит таким образом:
На Вашем устройстве кусок памяти 0x300-0x3FF это 0x400-0x4FF на другом. И наоборот.
То есть, изменяя байт 0x300 мы изменяем 0x400 на другом.
Не нужно использовать во время диалога, т.к. любое отображение ставит в RBP! номер терминала, который указан в TERMADDR.
Вот моя сеть:
image.thumb.png.0651a6f8740d3aa9f325bbffead814ab.png
Попробуем написать функцию, которая отправит данные другому устройству:
: SEND CR ." ENTER MESSAGE: "
400 100 ACCEPT DROP RBP!
400 300 OVER STRLEN MOVE ;

Описание:

На 0x100 находится TIB буфер(для строк), мы не пишем сразу в  0x300 т.к. сейчас мы подключены к матрице, следовательно будем изменять её.

Попробуем отправить устройству с ID 15 нашу строку:

image.png.a0fa82ee9c4e9a09a58181f13ab1925a.png
И вот, о чудо, мы получили нашу строку:

image.png.f96293b1c92537f32905a79925c8362d.png

 

И так, вернёмся к периферии.

Во первых - таблица ( эта и все последующие - взяты с http://minecrafting.ru/topic/6612/ ) :

0x01: столбец x
0x02: строка y
0x03: режим (0: спрятан, 1: сплошной, 2: мигающий)
Буфер ввода с клавиатуры (16 байт, FIFO):
0x04: Указатель начала очереди
0x05: Указатель конца очереди
0x06: Значение клавиши в начале очереди
Действия с областями:
0x07: Команда (1: заполнить, 2: выделить(инверсия цвета); 3: копировать)
Координаты левого верхнего угла:
0x08: x координата области источника / Значение для заполнения
0x09: y (необходимо только для копирования)
0x0A: х координата области, требуемой для изменения
0x0B: y координата области, требуемой для изменения
0x0C: Ширина области
0x0D: Высота области
Доступ к видеопамяти:
0x00: Номер строки для чтения/изменения
0x10-0x60: Содержимое строки?

Это - память терминала (чтобы править, нужно использовать 0x300+addr).
Зная всё это, мы можем понять, что, например, с монитором не всё так просто.
Воспользуемся командой DECOMPILE, чтобы просмотреть, как работает EMIT:

image.thumb.png.2264671e31c089fbb81d53255dc36653.png
Всё просто - она подключается к терминалу по адресу TERMADDR, переключается на него.
Записывает в 0x300 наш символ.
Далее идёт смена координат на мониторе, это не столь важно.

А не так и сложно, как показалось с первого взгляда, верно?

Давайте посмотрим, как работают функции взаимодействия с дисководом.
Воспользуемся таблицей:

0x00-0x7F: Буфер дисковода, размером в 1 сектор (128 байт).
0x80-0x81: Номер сектора
0x82: Команда для дисковода: (далее значения этого байта)
0: Ничего или удачное завершение предыдущей команды
1: Прочитать имя дискеты
2: Записать имя дискеты
3: Прочитать ID дискеты (серийный номер)
4: Прочитать сектор дискеты
5: Записать сектор дискеты
0xFF: Неудачное завершение предыдущей команды

Вот исходный код функции SAVE":

image.png.715a0cd6a38849a3bbcbe98cf27c57d8.png

Она записывает на диск данные от 0x500 до HERE.
Во первых - она использует слово DISKNAME".
Давайте декомпилируем и его:
image.thumb.png.0b8869d2544afc1700f79e8c2dc975a2.png
Не так уж и сложно, записывает строку на 0x300-0x380 и записывает номер команды(2) в 0x382.
Вернёмся к SAVE".
Далее идёт подготовка к запуску цикла, он записывает каждые 128 байт от 500 до HERE используя DISKWS.
Давайте посмотрим и на него:
 image.png.dd31a6491bfd67bd54650b30e7875e89.png
И снова, не всё так уж и сложно. Запись данных в 0x300-0x380, запись номера команды(5) в 0x382.

Давайте перейдём к самому вкусному - сортроны.
Таблица:

0x00: микрокоманда
1: Определить размер инвентаря
2: Прочитать содержимое слота
3: Извлечь
4: Фильтр
0x01: Количество предметов
0x02: Номер слота
0x04: Идентификатор предмета (4 байта!)
0x08: Повреждённость предмета
0x0A: Предел прочности предмета
0x0C: Исходящий цвет
0x0D: Входящий цвет

Разберём на примере SORTPULL:
image.png.6a3ee944ec4f4dd45426052605338490.png
И снова, запись адреса в RBP, запись номера слота в 0x302, кол-ва предметов в 0x301.
Она использует еще и SORTCMD.
Что же он делает?
image.thumb.png.452ee693d975658d5c5d00b1292a264e.png
Во первых, что мы можем увидеть - снова запись адреса в RBP!.
Ожидание исполнения команды, проверка на то, не случилось ли каких-либо ошибок при работе.
Если случились - выводим Sorter Error выходя в диалоговый режим.

Вы все ещё можете спросить - как это нам позволит ускорить заводы?
Во первых - Вы можете убрать из SORTPULL последних две команды, если они Вам, очевидно, не нужны.
И две первых, если Вы все ещё работаете с сортроном, к которому подключены.
Давайте теперь попробуем написать свою команду для извлечения всех предметов  из сундука.
Заменим SORTCMD, уберём проверку на ошибки и переключение SORTADDR.
Оставим ожидание выполнения.

HEX
: BETTER_SORTCMD DUP 300 C! 300 C@ = 5 TICKS ;
: GET_ALL_ITEMS SORTADDR 
@ RBP! 1 BETTER_SORTCMD 302 @ 
0 DO 40 301 C! I 302 ! 3 BETTER_SORTCMD LOOP
;

Вторая функция, которая использует стандартные методы:

: GET_ALL_ITEMS2 SORTSLOTS 0 DO 64 I SORTPULL DROP LOOP ;

Вот, мы убрали переключение на SORTADDR, убрали проверки каждый раз в SORTCMD.

Давайте теперь сравним две функции
Наша GET_ALL_ITEMS справилась за 4.2 секунды.
GET_ALL_ITEMS2 справилась за 6.9 секунд.
Этот пример НЕ СОВЕРШЕНЕН, можно ещё лучше ускорить.

ВАЖНО:
Оставляйте проверку на ошибки в конце функции. 
Почему?
Требование международных стандартов.
Просто если что-то пойдёт не так, сортрон может зависнуть, или шину разорвёт на стыке чанков.

Часть №3. Собственные структуры данных.

 

И так, вам необходимо реализовать структуру, в которой необходимо хранить больше одного числа? Два числа? Число и строку? Число, строку, исполняемый код?
Вам определённо нужно прочитать эту статью.
Для начала стоит ознакомиться с некоторыми командами:
HIDE - прячет последнее слово из словаря.
REVEAL - восстанавливает спрятанное слово в словарь.
HEADER - берёт следующую строку из буфера, создает слово с этим именем.
ALLOT - берёт n со стека, выделяет n байт и возвращает начало выделенной памяти.
, - выделяет 2 байта, записывает в них значение со стека.
,C - выделяет и записывает 1 байт.
,S - Аналогично ',' , только для строки. Адрес должен находиться в стеке.

Давайте узнаем, как работает VARIABLE:
image.png.df4481370300f09b33d3c4bdc3a223ea.png
Она вызывает функцию CREATE, выделяет 2 байта, записывает туда 0.
Давайте узнаем, что же делает CREATE:

image.png.b2fe3f454240c46092f1cc68f96492b3.png

Она вызывает HEADER, определение которой находится в таблице выше.
Выделяет один байт, записывает в него 0x22.
Примечание:
Во второй части я рассказывал, что это инструкция ENT, которая определяет, что нужно брать по два байта, переходить по ним и исполнять.

Выделяет еще два байта, записывает туда 0x534.
Давайте узнаем, что это за функция:
image.png.ecda171d19fe760e3287292b036d64f5.png
А это - функция DOVAR, которая записывает адрес следующего байта и возвращает нас в предыдущее слово.
image.png.4317eab333a03b2e0728f4060ee65940.png
То есть, когда мы пишем VAR - исполняется функция DOVAR. DOVAR - то же, что и EXIT. Лишь оставляет адрес следующих двух байт.
image.png.ff9308158857db9eab74033a8db39921.png
То есть, VAR @ можно заменить на 25FD @:
image.png.07666cb1597467b79e68bfccb284731f.png
Ладно, мы понимаем, как можно записывать переменные и строки. 
Но как записывать исполняемую часть? 
Давайте посмотри на исходный код слова " : ":
image.png.1bae6776756f3454ab0dd3ff486ee424.png
Определение HEADER и HIDE нам известно. Но что делает " [ "?
image.png.437a94adf6c9e0d75b777511ad10a137.png
"Что за чёрт?" - можете спросить вы.
Всё просто - в STATE хранится режим, в котором мы сейчас находимся.
То есть, когда STATE = 0, мы находимся в режиме диалога.
Если STATE = 1, мы находимся в режиме компиляции.
Давайте рассмотрим слово " ; ", которое завершает компиляцию:
image.png.f3e9e09c4422ca4df61ecb43589e736f.png
Выделяет два байта, записывает в них адрес EXIT.
Вызывает " ] ", что противоположно " [ ".
Вызывает REVEAL, определение которого Вы можете найти в таблице выше.

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

0x00 - 0x22, вхождение в слово.
0x01-0x02 - 0x0534(DOVAR). Будет записывать адрес RedPower id #1 в стек.
0x01-0x02 - RedPower id #1
0x03-0x04 - RedPower id #2
0x05-0xXX - наша строка, где на 0xXX будет храниться 0x00, что является концом строки.

Давайте теперь напишем функцию, которая будет выделять память для всего этого:

 

HEX
: ADD_ITEM
HEADER 22 ,C 534 ,
SORTSLOT@ DROP , ,
CR ." Enter string size: "
100 80 ACCEPT UATOI 1+ DUP 1+ ALLOT 
CR ." Enter item name: "
OVER ACCEPT DROP ;

Протестируем:

image.png.c886e4219fdace8739a58d72087be43a.png

Как можно увидеть по дампу - функция работает отлично.
Чтобы получить нужные нам данные - достаточно отступить от RedPower id #1 на x байт вперёд:

image.png.dbe38aefd963c81d2d4dc1129f4b156d.png

Всё работает отлично!
А теперь - давайте создадим массив таких предметов.
Вот и появилась первая проблема - размер строк не зарезервирован, то есть - он разный и невозможно подобрать определённое кол-во элементов в массиве, размер будет разным.
У данной проблемы есть несколько решений:
1) Можно создать массив с определенным кол-вом выделенных байт, каждая структура данных будет указывать на место следующей, кол-во элементов указываем в начале массива.
2) Создать массив с определенным кол-вом элементов, у которых будет точный размер.
Давайте реализуем первый.
Разметка:

0x00-0x01 - размер массива(байт).
0x02-0x03 - кол-во элементов в массиве.
0x04-0xXX - элементы массива(где XX - размер массива в байтах + 2).

Разметка элемента:

0x00-0x01  - указатель на место в памяти следующего элемента.
0x02-0x03 - RedPower id #1
0x04-0x05 - RedPower id #2
0x05-0xXX - наша строка, где на 0xXX будет храниться 0x00, что является концом строки.

Реализация:
 

: ALLOCATE_MASSIVE CREATE DUP ALLOT ! ;
VARIABLE MASSIVE_ADDR
VARIABLE ELEM_ADDR
VARIABLE MASSIVE_SIZE
: MASSIVE_GET_ELEMSIZE MASSIVE_ADDR @ 2 + @ ;
: MASSIVE_SET_ELEMSIZE MASSIVE_ADDR @ 2 + ! ;
: ADD_ELEMENT_TO_MASSIVE
MASSIVE_ADDR !
MASSIVE_ADDR  @ @ MASSIVE_SIZE !
MASSIVE_GET_ELEMSIZE 1+ MASSIVE_SET_ELEMSIZE
MASSIVE_ADDR @ 4 + ELEM_ADDR !
MASSIVE_GET_ELEMSIZE 0<>
IF MASSIVE_GET_ELEMSIZE 1- 0 DO 
ELEM_ADDR @ @ ELEM_ADDR @ !
LOOP THEN
SORTSLOT@ DROP
ELEM_ADDR @ 2 + ! ELEM_ADDR @ 4 + !
CR ." Enter string size: "
100 80 ACCEPT UATOI  DUP 4 + ELEM_ADDR @ !
CR ." Enter string: "
ELEM_ADDR @ OVER 6 + ACCEPT ; 

Попробуем инициализировать массив:
image.png.7f3a9f2bbe03f68586fbd59222c69e91.png
Всё сработало отлично! Размер массива инициализирован.
Попробуем добавить элемент "Diamond" в массив.
Просмотрим дамп памяти массива:

image.png.06bec523d5dbbbeface441f8a9a12ea0.png
Поздравляю!
Всё сработало отлично х2!
Теперь по адресу 0xFF2 размещен указатель на 0x2FFF, где будет находится следующий элемент массива.

Часть №4. Сеть и работа с дисками на примере чата.

 

Что же может дать такая сеть из нескольких компьютеров?
1. Ускорение заводов.
Ведь если несколько пк будут одновременно работать с сортронами - завод будет работать быстрее, верно?
2. Создание баз данных.
Где это может быть полезно?
К примеру  - имеем мы сеть из 30 компьютеров,  а нам нужно обновить базу данных предметов для каждого.
Неужели в каждый добавлять одно и то же?
В этом случае вполне полезны базы данных.
Покажу использование сетей на примере чата.
И так, давайте создадим чат!
Примечание:
Данный чат можно реализовать и через один пк, используя несколько мониторов.

Идея:
Есть два вида устройств - сервер и клиент.
Сервер всего один, а клиентов - множество.
Сначала - напишем сервер.
Идея сервера:
Получаем ID устройства в нашей сети, куда нужно отправлять данные.
Получаем номер команды, данные.
Отправляем по указанному ID ответ.
Храним сообщения на диске.
Команды сервера:
0 - отправить кол-во сообщений(записать в 0x400-0x401).
1 - отправить сообщение по номеру(берём из 0x403-0x404 номер, записываем в 0x301-0x3FF ответ).
2 - Получить сообщение - в 0x403-0x4FF находится сообщение.

Разметка сервера:

0x400 - ID
0x401 - номер команды
0x402 - Можно ли начинать работу?
0x403-0x4FF - получаемые данные.
0x300 - завершена ли работа?
0x301-0x3FF - данные клиента.

Реализация сервера.
Для начала - напишем базу данных,  в которой будем хранить сообщения:
 

HEX
: ADD_NEW_MESSAGE NOW_SIZE @ 1+ DUP NOW_SIZE ! BLOCK 70 MOVE FLUSH ;
: GET_MESSAGE BLOCK ;

Давайте её протестируем добавив несколько тестовых сообщений:
image.png.6a68de6caa0cd7c4feb679c02282f94f.png
Как можно увидеть, используя GET_MESSAGE - все работает отлично:
image.png.9dfdf8da9d10dbf2ffc8080053171b35.png
Давайте теперь доработаем наш сервер:

HEX
: CONNECT_TO_ID 400 C@ RBP! ; 
: PROCESS_COMMAND_0
CONNECT_TO_ID NOW_SIZE @ 301 ! ;
: PROCESS_COMMAND_1
403 C@ GET_MESSAGE 
CONNECT_TO_ID 301 70 MOVE ;
: PROCESS_COMMAND_2
404 ADD_NEW_MESSAGE CONNECT_TO_ID ;
: SWITCH_COMMAND
401 C@
DUP 0= IF DROP PROCESS_COMMAND_0 
ELSE DUP 1 = IF DROP PROCESS_COMMAND_1 
ELSE 2 = IF PROCESS_COMMAND_2 THEN THEN THEN ;
: START_SERVER
400 3 0 FILL
BEGIN
400 C@ 0<> 
IF BEGIN 402 C@ 0<>  UNTIL
SWITCH_COMMAND
CONNECT_TO_ID 1 300 C! 400 3 0 FILL
THEN KEY? UNTIL ;

Наш сервер готов!

Давайте теперь напишем клиент.
Для начала напишем функции для отправки/получения сообщений:
 

VARIABLE MYID
: CONNECT_TO_SERVER 0 RBP! ;
: SET_READY 1 302 C! ;
: WAIT_WHILE_EXECUTED BEGIN TICK 400 C@ 0<> UNTIL ;
: GET_SIZE 
CONNECT_TO_SERVER 0 301 C!
MYID C@ 300 C! 0 400 C! 
SET_READY
WAIT_WHILE_EXECUTED
401 @ ;
: RECEIVE_MESSAGE
CONNECT_TO_SERVER
1 301 C! 303 !
MYID C@ 300 C! 0 400 C! 
SET_READY WAIT_WHILE_EXECUTED 401 ;
: SEND_MESSAGE
CONNECT_TO_SERVER 2 301 C!
MYID C@ 300 C! 0 400 C! 
100 304 70 MOVE 
SET_READY WAIT_WHILE_EXECUTED ;
: SEND 100 80 ACCEPT SEND_MESSAGE ;

И так, код мы ввели.
Давайте всё это подключим:
image.thumb.png.b524b033a7a93ac7d90b23cc0a43805f.png
MYID у первого клиента - 5. У второго - 6.
Давайте протестируем:
#5:
image.png.b5f245c647b1084878b6eefe4bed9474.png
#6:
image.png.db293a606c88371dc8fd3cc23a152bf2.png
Всё сработало отлично!

  • Нравится 2
  • Одобряю 4

Поделиться сообщением


Ссылка на сообщение

Жесть. И для кого этот мод расчитан ? Такое ощущение, что читаю гайд по ассемблеру, хотя даже он по-понятнее будет (не в обиду гайду, просто видимо я тупой).

  • Нравится 1

Поделиться сообщением


Ссылка на сообщение

Гайд классный, но ни черта не понятно с первого раза Х)

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

Поделиться сообщением


Ссылка на сообщение
47 минут назад, KrisAlex сказал:

Гайд классный, но ни черта не понятно с первого раза Х)

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

Высокоуровневые вещи, с бо́льшим уровнем абстракции от работы с памятью тут:

 

А базовых вещей полно в интернетах.

Поделиться сообщением


Ссылка на сообщение

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас

×
×
  • Создать...