Оберон-клуб «ВЄДАsoft»

Твердыня модульных языков
Текущее время: 17 июн 2025, 18:21

Часовой пояс: UTC + 2 часа




Начать новую тему Ответить на тему  [ Сообщений: 69 ]  На страницу Пред.  1, 2, 3, 4, 5, 6, 7  След.
Автор Сообщение
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 17:06 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
А теперь ограничим константные массивы на возможность записи. Для этого в модуле OPP чуть подправим процедуру Block:
Код: "OBERON"
  1. IF obj^.conval^.arr # NIL THEN
  2. obj^.mode := VarPar; (* как будто это переменная-параметр *)
  3. obj^.vis := inPar; (* причём входной параметр*)
  4. obj^.typ := x^.obj^.typ;
Изменению подверглись только строчки с комментариями.
Теперь константные массивы воспринимаются как входной параметр (IN), поэтому изменить его напрямую нельзя. В то же время, это не константа (например, нельзя использовать элемент массива после BY в цикле FOR). А с другой стороны, раз это параметр, он имеет адрес, который можно куда-то передать, вместо копирования всех значений, как это происходит для констант.

А вот нужен ли нам массив-типизированная константа (как в ТурбоПаскале)? Можно назвать это "преинициализированная переменная-массив". Это массив данных, которые загружаются при старте программы и могут изменяться в процессе работы. Но при этом получается, что нельзя просто так начать игру заново, необходимо её заново загрузить.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 17:23 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Saferoll писал(а):
А вот нужен ли нам массив-типизированная константа (как в ТурбоПаскале)?
В информатике принято делить сущности на код и данные. Есть архитектуры, где такое деление задаётся на аппаратном уровне. В нашем случае если константные массивы можно приравнять к коду, то изменяемый константный массив уже нечто смешанное: либо самомодифицируемый код, либо статически инициализированные данные. Обе эти вещи нехарактерны для Оберон-парадигмы. Это с идеологической точки зрения, не знаю, может я плохо объяснил. А вот с практической? Ну не знаю, мне кажется изменяемые константные массивы не нужны. А ты как считаешь?


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 18:27 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
В книгах по ТурбоПаскалю рекомендовалось использовать типизированные константы, так как это уменьшает размер кода и увеличивает быстродействие за счет экономии на операторах присваивания. В присваивании X:= c в коде присутствует само значение "с" и машинные коды оператора присваивания. Если же задать const x:T= c;, в коде от оператора присваивания ничего не останется, лишь само значение с будет присутствовать в разделе данных экзешника. Особенно велик выигрыш, если присваивается большой массив или много строк.
А вот нужны ли они в XDev? У Спектрума память ограничена, поэтому можно себе представить, скажем, лабиринт, который загружается напрямую в память для данных и там изменяется в процессе игры. Если задать такой лабиринт константным массивом, то мы не можем его изменить, а можем, например, скопировать в буфер-переменную, где он будет изменяться.
Но что, если места в памяти не хватит для буфера и для константного массива одновременно? Если бы это была "типизированная константа", то можно было бы её изменять. Т.е. это способ решить проблему. А как мы можем это сделать?

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


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 18:49 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Выявилась небольшая проблема. Если константный массив описан внутри процедуры, то "const" перед ним не выводится.
Код: "OBERON"
  1. MODULE Unsigned; (*$MAIN*)
  2. IMPORT SYSTEM, P := Platform, B := Basic;
  3.  
  4. TYPE TheClocks = ARRAY 23 OF BYTE;
  5. CONST AnimClockData = TheClocks (0040, 00070, 00000,17, 00100, 0038, 00000, 0070, 030, 00099, 100, 0017, 00010,12, 00103, 0030, 0030, 0040, 031, 00019,1,2,3);
  6.  
  7. VAR b:BYTE;
  8.  
  9. PROCEDURE Proc;
  10. TYPE TheClocks3 = ARRAY 23 OF BYTE;
  11. CONST AnimClockData3 = TheClocks3 (0040, 00070, 00000,17, 00100, 0038, 00000, 0070, 030, 00099, 100, 0017, 00010,12, 00103, 0030, 0030, 0040, 031, 00019,1,2,3);
  12. VAR c:BYTE;
  13. BEGIN
  14. c:=AnimClockData3 [1];
  15. END Proc;
  16.  
  17. BEGIN
  18. b:= AnimClockData [1];
  19. END Unsigned.
транслируется в
Код: "C"
/*  Ofront 1.2 -xtspkaem */
#include "SYSTEM.h"
#include "Basic.h"
#include "Platform.h"
 
typedef
BYTE Unsigned_TheClocks[23];
 
 
const Unsigned_TheClocks AnimClockData =
{40,70,0,17,100,38,0,70,30,99,
100,17,10,12,103,30,30,40,31,19,
1,2,3};
static BYTE Unsigned_b;
 
 
static void Unsigned_Proc (void);
 
 
/*============================================================================*/
 
typedef
BYTE TheClocks3__2[23];
 
static void Unsigned_Proc (void)
{
TheClocks3__2 AnimClockData3 =
{40,70,0,17,100,38,0,70,30,99,
100,17,10,12,103,30,30,40,31,19,
1,2,3};
BYTE c;
c = AnimClockData3[1];
}
 
 
export main(int argc, char **argv)
{
__INIT(argc, argv);
__IMPORT(Basic__init);
__IMPORT(Platform__init);
__REGMAIN("Unsigned", 0);
/* BEGIN */
Unsigned_b = AnimClockData[1];
__FINI;
}
 
Как видим, typedef вложенной процедуры вышел на глобальный внешний уровень, а вот константный массив TheClocks3__2 AnimClockData3 потерял опцию const (причём это не зависит от предыдущего патча с inPar).
Видимо, для вложенных процедур генерация идёт по иному. Нужно теперь найти такое место и тоже его подправить. И лучше там генерировать не __CONSTARR, а __CONSTARR2 или еще как-то, чтобы отличать константный массив на глобальном уровне и внутри процедуры. А если отличать не надо, то нет никакой проблемы задать в библиотеке такое же определение препроцессора, как для __CONSTARR.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 18:52 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Просто если делать инициализированные изменяемые массивы, тогда нужно делать и инициализированные изменяемые переменные, а то чего их так дискриминировать. Более того, меня утешает присутствие константных массивов в Amiga Oberon и Active Oberon. Значит не одни мы озаботились таким вопросом. А от инициализированные изменяемые массивы — беспрецендентный случай в Оберон-мире. Но, в любом случае, мы сделали один шаг в сторону от стандарта. А изменяемые константные массивы — это уже следующий шаг, а нам нужно осознать первый и получить профит, опыт его использования.

Теперь о реализации. Тогда наверное придётся вынести из секции CONST в секцию VAR. А есть ещё механизм системных атрибутов. М.б. сделать так?
Код: "OBERON"
  1. VAR [1] a: INTEGER = 123;

Saferoll писал(а):
В книгах по ТурбоПаскалю рекомендовалось использовать типизированные константы, так как это уменьшает размер кода и увеличивает быстродействие за счет экономии на операторах присваивания.
Но зато появляется секция инициализированных данных, которая раньше могла быть пустой и забитой чем угодно.

Saferoll писал(а):
Особенно велик выигрыш, если присваивается большой массив или много строк.
А, кстати, почему-то в SDCC если проинициализированный массив не снабдить словом const, то массив занимает в памяти двойную длину. Здесь мы получим не больше выигрыша, чем в случае двух массивов с прямым копированием данных из одного в другой. Можно допустить неэффективность реализации этого механизма именно в SDCC. Но нужно потестировать и другие компиляторы.

Saferoll писал(а):
Но что, если места в памяти не хватит для буфера и для константного массива одновременно? Если бы это была "типизированная константа", то можно было бы её изменять. Т.е. это способ решить проблему. А как мы можем это сделать?
Сейчас данные даже константного массива могут быть изменены с помощью системных механизмов, т.е. прямой записью в память (SYSTEM.PUT). В принципе с натяжкой такой способ даже можно назвать переносимым и кроссплатформенным.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 19:29 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Я тоже думаю, что не следует торопиться вводить инициализированные переменные. Если их сделать как типизированные константы ТП (а только это имеет смысл делать для экономии памяти и времени), то они оказываются даже "преинициалированными", потому что их значение устанавливается только в момент загрузки программы. Это значит, что программу невозможно запустить заново без загрузки с носителя.
Т.е. момент начального присваивания лежит вне цикла жизнедеятельности программы. А это очень сильное нарушение идеологии. Так что лучше действительно системные средства, чем новая конструкция в ЯП.
Раз уж у нас не хватает памяти для какой-то платформы, так лучше разработать для этой платформы модуль со спецзагрузчиками или менеджером памяти, чтобы загрузить такой-то кусок из файла, такой-то кусок памяти повторно использовать, а такой-то указатель наложить на константный массив, чтобы его изменять. Всё это узкоспециальные средства для узкоспециальных целей.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 28 ноя 2014, 20:05 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Цитата:
Выявилась небольшая проблема. Если константный массив описан внутри процедуры, то "const" перед ним не выводится.
Это удалось исправить. Внесём изменения в процедуру OPC.IdentList
Код: "OBERON"
  1. (* new variable base type definition required *)
  2. IF ~first THEN EndStat END ;
  3. first := FALSE;
  4. base := obj^.typ; lastvis := obj^.vis;
  5. BegStat;
  6. IF (vis = 1) & (obj^.vis # internal) THEN OPM.WriteString(Extern)
  7. ELSIF (obj^.mnolev = 0) & (vis = 0) THEN
  8. IF (obj^.conval # NIL) & (obj^.conval^.arr # NIL) THEN (* конст.массив на внешнем уровне*)
  9. OPM.WriteString("__CONSTARR ");
  10. ELSIF obj^.vis = internal THEN OPM.WriteString(Static)
  11. ELSE OPM.WriteString(Export)
  12. END
  13. ELSIF (obj^.conval # NIL) & (obj^.conval^.arr # NIL) THEN (* конст.массив внутри процедуры*)
  14. OPM.WriteString("__CONSTARRLOC ")
  15. END;
Вот теперь внутри процедуры константные массивы объявляются с опцией __CONSTARRLOC, а внутри модуля с опцией __CONSTARR. Если мы не различаем эти варианты, то нужно просто объявить в системной библиотеке одинаковые определения для этих директив препроцессора. А в будущем, может быть, для какого-то С-компилятора это различие пригодится.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 29 ноя 2014, 18:15 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
Zorko писал(а):
... Надо так:
Код: "OBERON"
  1. CASE elemsize OF (* Размер элементов массива в байтах (для BlackBox). *)
  2. | 1: WITH x^.conval^.arr: ConstArrOfByte DO (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  3. NEW(x^.conval^.arr); NEW(x^.conval^.arr.val, size);
  4. END;
  5. | 2: WITH x^.conval^.arr: ConstArrOfSInt DO (* типы с размером в 2 байта *)
  6. NEW(x^.conval^.arr); NEW(x^.conval^.arr.val, size);
  7. END;
  8. | 4: WITH x^.conval^.arr: ConstArrOfInt DO (* типы с размером в 4 байта *)
  9. NEW(x^.conval^.arr); NEW(x^.conval^.arr.val, size);
  10. END;
  11. | 8: WITH x^.conval^.arr: ConstArrOfLInt DO (* типы с размером в 8 байт *)
  12. NEW(x^.conval^.arr); NEW(x^.conval^.arr.val, size);
  13. END;
  14. END;
К сожалению, так тоже не пойдёт. WITH должен применяться к уже размещённой переменной (к указателю на существующий кусок памяти), а если переменная равна NIL, то будет ошибка. Если же сделать NEW(x^.conval^.arr) до CASE, то будет размещена корневая запись ConstArray и WITH в любой ветке выдаст ошибку, потому что тип ConstArray не совпадает ни с ConstArrOfByte, ни с ConstArrOfSInt , ни с последующими.
Поэтому WITH тут убираем. И применяем в ветке CASE процедуру "NEW с нужным нам типом".
При этом уловки NEW(y(ConstArrOfInt)) тоже не помогут, потому что "v(T)" это не преобразование переменной v к типу Т , это "охрана типов", т.е. проверка, что переменная v имеет тип T. Вернее, это значит - "убедимся, что динамический тип переменной вот такой и будем здесь с ней работать, используя именно этот динамический тип".
Поэтому WITH и тип в скобочках - это для уже размещённой структуры. А разместить можно только через NEW(переменная нужного типа). Аргументом NEW может быть только переменная, а значит нужно иметь переменные всех типов, которые мы можем разместить. И это, по-моему, единственный способ создавать "вариантную" запись.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 29 ноя 2014, 22:03 
Не в сети
Администратор
Аватара пользователя

Сообщения: 273
Откуда: Россия
С учётом вышеизложенного перепишем процедуру OPP.ConstArray так
Код: "OBERON"
  1. PROCEDURE ConstArray (VAR x: OPT.Node);
  2. VAR apar: OPT.Node; n,size,i: INTEGER; fp: OPT.Object; y: OPT.ConstArray;
  3.  
  4. (* переменные для создания конс.массива при помощи NEW *)
  5. arrByte:OPT.ConstArrayOfByte; arrSInt:OPT.ConstArrayOfSInt;
  6. arrInt:OPT.ConstArrayOfInt;
  7. BEGIN
  8. IF x^.obj^.typ^.BaseTyp^.form IN intSet THEN (* массив из целых, позднее добавим BYTE *)
  9. n:=x^.obj^.typ^.n; (* количество элементов массива *)
  10. CheckSym(lparen); (* скобка ( *)
  11. fp := OPT.NewObj(); fp^.typ := x^.obj^.typ^.BaseTyp;
  12. fp^.mode := Var; (* fp - переменная, элемент массива *)
  13. NEW(y);
  14. (* тут нужно бы выделить кусок памяти для n элементов массива *)
  15. CASE fp^.typ^.size OF (* Размер элементов массива в байтах (для BlackBox). *)
  16. | 1: (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  17. NEW(arrByte); NEW(arrByte.val, n); y := arrByte;
  18. | 2: (* типы с размером в 2 байта *)
  19. NEW(arrSInt); NEW(arrSInt.val, n); y := arrSInt;
  20. | 4: (* типы с размером в 4 байта *)
  21. NEW(arrInt); NEW(arrInt.val, n); y := arrInt;
  22. | 8: (* 64-разрядных типов пока нет *)
  23. END;
  24.  
  25. IF sym # rparen THEN
  26. i := 0;
  27. LOOP ConstExpression(apar); (* берем очередной элемент *)
  28. IF i < n THEN
  29. OPB.Param(apar,fp); (* проверим на совместимость*)
  30. (* тут нужно положить элемент в кусок памяти *)
  31. WITH
  32. | y : OPT.ConstArrayOfByte DO (* BOOLEAN, CHAR, SYSTEM.BYTE (для Ofront'а) *)
  33. y.val[i] := SHORT(SHORT(apar^.conval^.intval));
  34. | y : OPT.ConstArrayOfSInt DO (* типы с размером в 2 байта *)
  35. y.val[i] := SHORT(apar^.conval^.intval);
  36. | y : OPT.ConstArrayOfInt DO (* типы с размером в 4 байта *)
  37. y.val[i] := apar^.conval^.intval;
  38. END;
  39. INC(i);
  40. ELSE err(64)
  41. END ;
  42. IF sym = comma THEN OPS.Get(sym)
  43. ELSIF (lparen <= sym) & (sym <= ident) THEN err(comma)
  44. ELSE EXIT
  45. END
  46. END
  47. END ;
  48. (* x - тип массив, y -элементы массива в памяти, *)
  49. fp := x^.obj; (* запомним тип "массив"*)
  50. x := OPB.NewIntConst(n);
  51. x^.conval^.arr := y;
  52. x^.obj := fp; (* константа с типом "массив" *)
  53.  
  54. IF i # n THEN err(65) END;
  55. CheckSym(rparen);
  56. ELSE err(51)
  57. END;
  58. END ConstArray;
  59.  
  60.  
Переменные arrByte, arrSInt, arrInt пришлось ввести лишь потому, что NEW в качестве аргумента требует именно переменную. Поэтому другого способа разместить запись нужного типа по-видимому нет, кроме как иметь переменную-указатель на этот тип.
А вот если мы эту "вариантную" запись разместили, дальше можно с ней работать при помощи WITH, который выберет нужный вариант (что дальше и видим при сохранении прочитанных из исходника элементов).

Изменений потребует также модуль OPC. Там внутрь процедуры IdentList поместим вспомогательную процедуру
Код: "OBERON"
  1. PROCEDURE WriteElem (arr: OPT.ConstArray; i: INTEGER);
  2. (* выводит в листинг i-й элемент константного массива arr*)
  3. BEGIN
  4. WITH
  5. | arr : OPT.ConstArrayOfByte DO OPM.WriteInt(arr.val[i]);
  6. | arr : OPT.ConstArrayOfSInt DO OPM.WriteInt(arr.val[i]);
  7. | arr : OPT.ConstArrayOfInt DO OPM.WriteInt(arr.val[i]);
  8. END;
  9. END WriteElem;
  10.  
А в теле IdentList будем выдавать в листинг элементы массива так:
Код: "OBERON"
  1. ELSIF (obj^.conval # NIL) & (obj^.conval^.arr # NIL) THEN (* элементы конст.массива *)
  2. OPM.WriteString(" ="); OPM.WriteLn;
  3. OPM.WriteString(" {");
  4. FOR i := 0 TO obj^.conval^.intval-2 DO
  5. WriteElem(obj^.conval^.arr , i); OPM.WriteString(",");
  6. IF (i+1) MOD 10 = 0 THEN OPM.WriteLn; OPM.WriteString(" ") END;
  7. END;
  8. WriteElem(obj^.conval^.arr , obj^.conval^.intval-1);
  9. OPM.WriteString("}");
  10. END
Здесь, как видим, просто использована процедура WriteElem вместо непосредственного вывода через OPM.WriteInt.
Таким образом, реализация константных массивов сделана практически вся. Теперь нужно тестировать.


Вернуться к началу
 Профиль  
Ответить с цитатой  
 Заголовок сообщения: Re: Константные массивы
СообщениеДобавлено: 29 ноя 2014, 22:33 
Не в сети
Аватара пользователя

Сообщения: 1019
Откуда: Днепропетровская обл.
Отлично, Олежка! :)
Вышли, пожалуйста, все изменения одним архивом.

А я тем временем набросал утилитку bin2ob для формирования из двоичных файлов блоков данных в виде исходника. Утилита выводит данные в виде B.DATA12(...) группами по 12 байт в строке. В принципе, не совсем то, что нужно для константных массивов, но ничего, доработаем.


Вернуться к началу
 Профиль  
Ответить с цитатой  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 69 ]  На страницу Пред.  1, 2, 3, 4, 5, 6, 7  След.

Часовой пояс: UTC + 2 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
© VEDAsoft Oberon Club