vak: (Default)
[personal profile] vak posting in [community profile] besm6
Сейчас к периферийным регистрам можно обращаться из Паскаля через указатели. К примеру, код:
program test();
var
src, dst: @integer;
begin
src := ptr(77755B);
dst := ptr(77756B);
repeat
dst@ := src@
until false;
end.
превращается в:
    c2:  10 010 0000  xta (8)        ; =77755
01 000 0050 atx 050(1) ; src
c3: 10 010 0001 xta 1(8) ; =77756
01 000 0051 atx 051(1) ; dst
c4: 01 23 00050 wtc 050(1) ; src@
00 010 0000 xta
c5: 01 23 00051 wtc 051(1) ; dst@
00 000 0000 atx
c6: 00 30 00004 uj 4
00 22 00000 utc
Команда WTC выполняет косвенное обращение к периферийному регистру через указатель. При этом нужно выделять память на указатели.

Есть более эффективный способ, если чуть улучшить линкер в юниксном стиле. Объявляем внешнюю структуру, которая по адресам сооветствует расположению периферийных регистров. Обращение к полям этой структуры будет превращаться в чтение/запись регистров. При этом будет использоваться команда UTC, чуть более эффективная, и не нужна дополнительная память на указатели. Пример:
program test(gpio);
var
gpio: record { периферийные регистры блока GPIO }
unused0, { 77750 - на будущее }
unused1, { 77751 }
unused2, { 77752 }
unused3, { 77753 }
CNEN, { 77754 - маска отслеживаемых пинов }
CNIE, { 77755 - вкл. прерывание по изменению сигнала }
PORT, { 77756 - запись - выдача, чтение - прием }
TRIS: integer { 77757 - направление данных }
end;
begin
repeat
gpio.PORT := gpio.CNIE
until false;
end.
Результат:
    c2:  00 22 77750  utc 77750
00 010 0005 xta 5 ; gpio.CNIE
c3: 00 22 77750 utc 77750
00 000 0006 atx 6 ; gpio.PORT
c4: 00 30 00002 uj 2
00 22 00000 utc
Надо только научить линкер привязывать внешние common-блоки к фиксированным адресам, но это нетрудно.

Date: 2019-06-04 12:37 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Или так: объявляем структуру, присваиваем указателю на неё нужное значение, потом пишем with ptr@ do begin ... end, и всё внутри блока будет адресоваться по регистру.

Date: 2019-06-04 01:10 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Без repeat, ясное дело, работает. Тогда понятно: он хочет, чтобы установка регистра по with и использование было в одном линейном участке, а repeat это портит.

Date: 2019-06-04 02:33 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Можно ли как-то побитовое "И" делать? Мне в цикле нужно следить за появлением символа в FIFO, а это определенный бит регистра состояния.

Date: 2019-06-04 03:39 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
В стандартном Паскале подобные трюки делаются с помощью аналога сишного union, например:

type union = record case integer of 0: (c:char); 1: (s:set of 0..47) end;

var u:union;

Тест наличия конкретного бита или набора битов (0-старший, 47-младший) делается с помощью выражения [N] <= u.s - так дешевле, чем
N in u.s. А если номер бита переменный, то лучше N in u.s.

Несмотря на то, что поле c - типа char, преобразование ord(u.c) сделает integer из всех 40 разрядов мантиссы, просто добавив целый порядок.
И наоборот, присваивание u.c := chr(i) отрежет только порядок.


Edited Date: 2019-06-04 03:45 am (UTC)

Date: 2019-06-04 06:18 am (UTC)
x86128: (Default)
From: [personal profile] x86128
С отдельными битами понятно.
А как вот такое Сишное выражение написать tmp = a & 0x3; ?

Date: 2019-06-04 07:07 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Конкретно это для целых a и tmp пишется как tmp := a mod 4;
Ну или для a типа char, соответственно, ord(a) mod 4.

В общем виде, используя битовые множества и переменные типа union, будет

tmp.s := a.s * MASK; (для битовых множеств операции сложения, умножения и вычитания понимаются в булевском смысле: OR, AND и AND-NOT); в данном случае MASK будет [46,47].


Date: 2019-06-04 02:19 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Вот при такой реализации выглядит красиво.

Но лучше, наверное, как-то статически все метки настраивать во время линковки/сборки, по принципу библиотек .h файлов для Arduino где есть куча #define с адресами регистров.

Date: 2019-06-04 12:58 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Еще вариант - научить компилятор привязывать внешние имена к фиксированным адресам, примерно как внешние файлы привязываются к направлению-зоне.

Date: 2019-06-04 01:54 am (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Для компиляции Паскаля абсолютному символу не нужно быть внешним.

Date: 2019-06-04 02:44 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Было бы здорово настраивать под платформу с помощью меток линковщика.
Подобно сделано в nios: с помощью мастера делается текстовый файл с метками для linker'а и текстовый файл с #define для стандартной библиотеки Си.

Date: 2019-06-04 02:49 am (UTC)
x86128: (Default)
From: [personal profile] x86128
на самом деле большой вопрос
Стало любопытно, существует ли какое общее соглашение по передаче параметров из разных языков при вызове процедур? (calling convention, __fastcall, _stdc, FFI и т.д.)

Date: 2019-06-04 10:03 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Ну вот в первом приближении в симуляторе заработало.
Код:
{=P-    Disable debug information and crash dump}
{=T-    Disable range checks}
{=S8    Disable checking for stack overflow}

program uart_pas();
const
    tx_done = 35;
    rx_empty = 36;
type
    union = record
        case integer of
            0: (i:integer);
            1: (s:set of 0..47)
        end;

var
    uart_data, uart_ctrl, uart_cset: @integer;

procedure uart_init(baud_rate: integer);
begin
    uart_data  := ptr(77740B);
    uart_ctrl  := ptr(77747B);
    uart_cset  := ptr(77746B);

    (* set baud rate *)
    uart_ctrl@ := 50000000 div (baud_rate * 16);
    (* enable loopback mode *)
    uart_cset@ := 16777216;
    (* enable RX TX *)
    uart_cset@ := 1000B;
end;

function uart_recv: integer;
var tmp: union;
begin
    (* blocking read *)
    repeat
        tmp.i := uart_ctrl@;
    until not(rx_empty in tmp.s);
    uart_recv := uart_data@;
end;

procedure uart_tx_wait;
var tmp: union;
begin
    repeat
        tmp.i := uart_ctrl@;
    until tx_done in tmp.s;
end;

procedure uart_send(value: integer);
var tmp: union;
begin
    (* wait for previous send *)
    uart_tx_wait;
    uart_data@ := value;
end;

begin
    uart_init(115200);
    uart_send(85); (* 01010101 *)
    repeat
        uart_send(uart_recv);
    until false;
end.


Если посмотреть трейс (усеченный):
cat output.trace | grep 7774 | grep Store
(1767)        Store [77747] = 6400 0000 0000 0033
(1782)        Store [77746] = 6400 0001 0000 0000
(1794)        Store [77746] = 6400 0000 0000 1000
(1978)        Store [77740] = 6400 0000 0000 0125
(6534)        Store [77740] = 0000 0000 0000 0125

видно что пролазит экспонента. Как всё-таки "красиво" с побитовыми операциями работать? Как задавить лишнее?

Я еще тот паскалист, конечно :)

Date: 2019-06-04 02:24 pm (UTC)
x86128: (Default)
From: [personal profile] x86128
Еще такой момент вылез: длина имени переменной в блоке var различается только по первым 8-ми символам.
Иначе вылазит:
Error 2: Identifier already defined 
 00001    7  1     src_bignam, src_bignam2: @integer;
       ^^^^^                             0
 IN 14 LINES 1 ERRORS

Date: 2019-06-05 07:37 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Да может это и не надо пока.
Вдруг будет нужда линковаться со старым кодом на БЭСМ.

Date: 2019-06-05 05:09 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Можно ли в нашем Паскале делать сдвиги целых влево/вправо на определенное число бит?

y = x << i; где i заранее неизвестное переменное целое число
Edited Date: 2019-06-05 05:16 am (UTC)

Date: 2019-06-05 07:34 am (UTC)
x86128: (Default)
From: [personal profile] x86128
В коде сдвиги есть, но не нашел такой операции в токенах.

Date: 2019-06-05 04:41 pm (UTC)
x86128: (Default)
From: [personal profile] x86128
Протестировал на железе работу Паскаля.
При достижении конца программы, она перезапускается.

С ком-портом нормально, а вот для vga нужны битовые операции, а с ними какие-то странности.
Пользуюсь тем хитрым типом union
type
    union = record
        case integer of
            0: (i:integer);
            1: (s:set of 0..47)
        end;


Вот такой процедурой пытаюсь выдать точку:
procedure plot_xy(x,y: integer);
var
    addr, bit: integer;
    pix: union;
begin
    addr := (x div 8) + 40 * y;
    bit  := 40 + x mod 8;

    vga_al@ := addr;
    pix.i   := vga_data@;
    pix.s   := pix.s + [bit];

    vga_al@   := addr;
    vga_data@ := pix.i;
end;


Но ставятся биты как-то странно, с "дырками" когда перебираешь x от 0 до 7.
Завтра буду смотреть внимательно еще раз.
В первом приближении показалось, что при определенных вызовах этой процедуры из разных мест кода (из цикла, не из цикла, из под условия) как-то целые аргументы портятся. Буду изучать.

Date: 2019-06-06 05:05 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Нашел, кажется,
вот такое выражение считается неправильно, где bit и x integer

bit := 40 + (x mod 8);

Причем bit := x mod 8;
Edited Date: 2019-06-06 09:25 am (UTC)

Date: 2019-06-06 09:38 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Хорошо грабли были запрятаны.

Дело в том, что у целых чисел в экспоненте стоит всегда 064 (в старших разрядах), поэтому в этом выражении:

bit := 40 + (x mod 8)

когда X приходил из ком-порта в старших разрядах был 0-лик до той поры пока к нему не применить операцию сложения или вычитания. Как применишь так в ответе мусор.
Любопытно что операции умножения эти биты не трогают.

Переделал немного UART чтобы при чтении байта читалось 064 в старших разрядах а принтяй байт в младших и все сразу же заработало.

Из питона посылаются случайные координаты:
Там где линии алгоритм Брезенхейма (точнее его частный случай, но на Паскале)
Пример №1 https://youtu.be/1qtg4VCXjYU
Пример №2 https://youtu.be/62jhjpRayWc

Любопытно, но похоже в нашем Паскале нет "else".

Date: 2019-06-07 03:34 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Вот такой код надо закодить:
plotLine(x0,y0, x1,y1)
  if abs(y1 - y0) < abs(x1 - x0)
    if x0 > x1
      plotLineLow(x1, y1, x0, y0)
    else
      plotLineLow(x0, y0, x1, y1)
    end if
  else
    if y0 > y1
      plotLineHigh(x1, y1, x0, y0)
    else
      plotLineHigh(x0, y0, x1, y1)
    end if
  end if

и сходу не получилось... ну это ладно попробую еще раз.

Добавление 064 можно избежать, если сделать например на мадлене те самые peek/poke c AOX , а на оборудовании не использовать биты с 40 по 48-й.

Date: 2019-06-07 04:57 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Ох уж этот паскаль.
procedure pltln(x0, y0, x1, y1: integer);
begin
    if abs(y1 - y0) < abs(x1 - x0) then
        if x0 > x1 then
            pltlo(x1, y1, x0, y0)
        else
            pltlo(x0, y0, x1, y1)
    else
        if y0 > y1 then
            plthi(x1, y1, x0, y0)
        else
            plthi(x0, y0, x1, y1)
end;


Date: 2019-06-07 05:05 am (UTC)
x86128: (Default)
From: [personal profile] x86128
Еще есть такой вопрос:
Как можно инициализировать массив:
var
   data: array[0..9] of integer = (10,20,30,40,50,60,71,80,90,91);

Ошибку выдает.
Error 103: Required token not found: SEMICOLON
00001 23 1 data: array[0..9] of integer = (10,20,30,40,50,60,71,80,90,91);
^^^^^ 0
IN 216 LINES 1 ERRORS
Edited Date: 2019-06-07 05:09 am (UTC)

Date: 2019-06-07 06:16 am (UTC)
x86128: (Default)
From: [personal profile] x86128
А если несколько массивов надо инициализировать то как будет выглядеть?

Profile

Сообщество любителей БЭСМ-6

January 2026

S M T W T F S
    123
45678910
11121314151617
18192021222324
2526272829 3031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Mar. 8th, 2026 04:15 am
Powered by Dreamwidth Studios