Вход

Просмотр полной версии : GCC 11.2.1 с поддержкой процессоров 1801вм1 и 1801вм2



yu.zxpk
10.11.2021, 00:55
После продолжительных и многократных подходов в GCC была добавлена поддержка процессоров 1801вм1 и 1801вм2.
Бинарники, собранные под Windows с помощью MING-W64, выложены Славе на сайт:

http://www.1801bm1.com/files/pdp11/cross-compilers/ming-w64-pdp11-aout-11.2.1.zip

c4d55afc5150262f9ebbe3a9749a9d86 ming-w64-pdp11-aout-11.2.1.zip
Дата сборки: 13.11.21 ~ 16:41

Выбор модели "target" осуществляется опциями `-mbm1a`, `-mbm1g` или `-mbm2` в дополнение к оригинальным `-m10`, `-m40` и `-m45`.

`-mbm1` есть алиасом для `-mbm1a` -- процессор 1801вм1а поддержки MUL, как в 1801вм1г.


Нужно заметить, что данная сборка - это чистый кросс-компилятор без стандартной библиотеки C, без libgcc (нет софтверной реализации MUL, DIV операций). Дело наживное, можно "потянуть" из наработок DEC.
Соотвественно, буду со временем допиливать libgcc и базовую libc для RT11 (работа с файлами для начала).

Сборка поддерживает только компиляторы C и C++. Попытка добавить Ada и Fortran с наскока не получилось, возможно оно требует установки каких-то компонентов в систему при сборке, или какие-то специальные ключи конфигураци GCC.

Известно, что для свежего GCC 11+ есть Modula-2 frontend. Возможно, попробую прикрутить его для любителей изделий Вирта (Паскаль).

В сборку так же входят утилиты bin2load.exe и lda2sav.exe для облегчения получения выполняемых файлов в RT11.
Есть репозиторий с примером использования кросс-компилятора для генерации файлов в формате Files11 (*.SAV), использование ASM вставок для вывода символов/строк с помощью EMT 0341, 0351

https://github.com/yshestakov/pdp11-toolchain/tree/master/hello-gcc

Oleg N. Cher
10.11.2021, 04:28
Поделитесь правильной последовательностью создания .sav-файлов при помощи утилиты lda2sav?

Я делаю так:


@ECHO OFF
:: http://ancientbits.blogspot.com/2012/07/programming-barebones-pdp11.html

SET CodeAdr=01000
SET Options=-Wno-write-strings -fverbose-asm -quiet -O2 -Os -fomit-frame-pointer
SET Include=-I ..\Lib\C -I ..\Lib\Obj -I ..\Lib
SET Libraries=%Libraries% -L ..\Lib ..\Lib\CGLIB.lib

SET PATH=..\Bin\gcc\bin
SET CC=..\Bin\gcc\libexec\gcc\pdp11-aout\10.0.1\cc1.exe
SET AS=pdp11-aout-as.exe

%CC% %Options% %Include% %1.c
IF errorlevel 1 PAUSE
%AS% %1.s -o %1.o
IF errorlevel 1 PAUSE
pdp11-aout-ld.exe --entry %CodeAdr% -T ldaout.cmd %1.o -o %1.out %Libraries%
IF errorlevel 1 PAUSE
bin2load.exe -a -f %1.out -o %1.lda -b %CodeAdr%
IF errorlevel 1 PAUSE
lda2sav.exe -o %1.sav %1.lda
IF errorlevel 1 PAUSE

Здесь всё делается при помощи скрипта ldaout.cmd, задающего структуру .sav-файла, как я понимаю. Сам ldaout.cmd взят с этой ссылки (http://ancientbits.blogspot.com/2012/07/programming-barebones-pdp11.html). Но я всё-таки в глубине души подозреваю, что можно как-то без него... тем более, что полученный мной .sav с точки зрения IDA какой-то подозрительный. Хотя реальщики проверили на живом УКНЦ, работает. Но какое-то смутное сомнение всё-таки осталось...

- - - Добавлено - - -

P.S. Проверил насчёт выпиливания SETD/SETI. Не выпиливаются. Пока так.

http://i.piccy_.info/i9/19404e356613a251c0f14d69ab94c2b5/1636507618/70497/1320134/SETD.png

yu.zxpk
10.11.2021, 10:17
Последовательность команд сборки SAV включая crt0.s, конфиг линкера LD (pdp11-aout.ld):

https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/Makefile

По поводу вставки setd/seti в начало функции main() -- нашел, что оно зависит от "TARGET_FPU" (CPU поддерживает аппаратные операции с float point numbers). У меня там еще есть косяки вокруг парсера опций, исправлю.
Отключается SETI/SETD добавлением опции `-msoft-float`:

pdp11-aout-gcc -mbm2 -msoft-float -c -o hello.o hello.c

Oleg N. Cher
10.11.2021, 18:44
А что это за метка ___main и откуда она берётся? Компилирую пустой модуль, где в конце просто emt 0350 для выхода в RT-11.


pdp11-aout-ld.exe: Empty.o:Empty.o:(.text+0x2): undefined reference to `__main'

http://i.piccy_.info/i9/48b69edd6ea01063e720dea7b9e24855/1636557695/44356/1320134/GCC.png

- - - Добавлено - - -


Последовательность команд сборки SAV включая crt0.s, конфиг линкера LD (pdp11-aout.ld):

https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/Makefile

Это точно рабочий мейкфайл? А то там опции вызова GCC странные: -m10 вместо -mbm2


pdp11-aout-ld.exe: cannot open linker script file pdp11-aout.ld: No such file or directory

Так ругается на строку:
pdp11-aout-ld -T pdp11-aout.ld -o $@ $(OBJS) -Map=hello.out.map

Должен быть ещё скрипт pdp11-aout.ld, в этом и был вопрос.

И что - работает без задания адреса?

yu.zxpk
10.11.2021, 20:20
1. Функция ___main, возможно, есть частью стандартной libgcc или что-то вообще из новых стандартов C. В любом случае в crt0.s файле можно обьявить метку с инструкций "rts pc"

2. Да, Makefile рабочий. Просто я этот проект отлаживал года полтора-два тому, когда еще не сделал поддержки -mbm1/-mbm2. Опция -m10 -- это PDP11/10 с процессором LSI-11, подходит для ВМ1. Да, легко поменять под свои нужды

3. Файл pdp11-aout.ld лежит рядом с Makefile в репозитории. Сравните со своим LD скриптом. У меня там стартовый адрес 01000

https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-aout.ld

yu.zxpk
11.11.2021, 12:52
Поборол обработку опций для процессоров BM1/BM2:
* выключен FPU для этих процессоров
* генерация инструкций `seti / setd` в начале функции main() отключена

Из особенностей: для BM1 не разрешена MUL инструкция, потому `-mbm1` означает процессор 1801BM1a, а не BM1г. Исправлю в следующей сборке: добавлю `-mbm1g` или `-mbm1a` с соответствующей обработкой в генераторе инструкций

Oleg N. Cher
11.11.2021, 18:25
1. Функция ___main, возможно, есть частью стандартной libgccНо ведь GCC должен сам подхватывать libgcc без доп. параметров типа -lgcc?


В любом случае в crt0.s файле можно обьявить метку с инструкций "rts pc"Даже не знаю, где находится этот crt0.s


Сравните со своим LD скриптом.Отличаются одной строкой (в Вашем есть, в моём нет):

crt0lda.o(.text)

Кстати, откуда всё-таки берутся эти crt0*? Они не в libgcc вшиты?

yu.zxpk
11.11.2021, 19:06
Но ведь GCC должен сам подхватывать libgcc без доп. параметров типа -lgcc?

Даже не знаю, где находится этот crt0.s

Отличаются одной строкой (в Вашем есть, в моём нет):

crt0lda.o(.text)

Кстати, откуда всё-таки берутся эти crt0*? Они не в libgcc вшиты?

Нет, crt0*.s файлы никогда не были частью libgcc. Это "переходник" между средой (RT-11 как OS, или LDA, как ROM) и функцией main(). Функция main() на вход принимает argc и argv (аргументы программы).
Кстати, в далеком 88 году, когда мне в руки попал компилятор C для RT-11 на ДВК/УКНЦ, в комплекте не было crt0.s файла. Пришлось разбираться и писать его самому.

В случае "демо" проекта на GCC, есть 2 вариант crt0 файла: для RT-11 и для LDA (ROM):

https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/crt0rt.s
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/crt0lda.s

В том, что для RT11, используется EMT для корректного выхода обратно в OS. Там же нужно сделать обработку командной строки.

Oleg N. Cher
11.11.2021, 20:33
Спасибо.
Правильно ли я понимаю, что в RT-11 нет возможности передать в программу командную строку?
И Вы её эмулируете путём передачи argc=1 argv[]="hello.rom" ?
Может проще выпилить? Я вот так делаю:


#define main(argc, argv) main(void)

Обязательно ли задавать своё значение SP=01000? Прога не может работать с тем SP, что ей достался на момент её вызова?

yu.zxpk
11.11.2021, 20:50
В RT-11 есть возможность разобрать аргументы, переданные в программу. Просто я это не умею (забыл как делается).

На предмет SP=01000 - это такая договоренность. Программа (TEXT сегмент) начинается с 01000, а стек растет вниз от 01000.

SuperMax
12.11.2021, 06:26
Спасибо.
Правильно ли я понимаю, что в RT-11 нет возможности передать в программу командную строку?
И Вы её эмулируете путём передачи argc=1 argv[]="hello.rom" ?
Может проще выпилить? Я вот так делаю:

нее
если CИ делать то разбор командной строки очень нужен!

касаемо как это сделано в RT11 можно прочитать тут
https://forum.maxiol.com/index.php?showtopic=5004
ПО ДВК [TIFF] Книга 6 - Системные библиотеки и сообщения системы (Научный центр-1990)
запрос .CSISPC страница 54ая

shattered
12.11.2021, 19:24
или поискать исходники c runtime от decus c

yu.zxpk
12.11.2021, 21:46
Да, нашел runtime decus. Посмотрел бегло. Пока прикручу функции EIS для BM1 для начала. Это функции, которые должны быть в libgcc, по идее. Далее можно, собственно, портировать базу libc: IO (rt11), выделение памяти, printf, strings и т.д.
Цель: получить минимальный, но рабочий комплект для написания программ на C/C++ под RT-11 используя кросс-компилятор GCC.

Oleg N. Cher
13.11.2021, 06:13
Ну, мне сишные библиотеки нужны постольку поскольку, хотя юзать их из Оберона тоже можно.

Традиционно в библиотеке XDev есть модуль CmdLine. Если он будет реализован для RT-11, то вовсе не обязательно тем же способом, что сишный механизм argc/argv.

Я смотрю, много чего на асме для RT-11 делается через макросы. Так вот. Из GCC у нас нет возможности подключить файл этих макросов, взятый, например, из MACRO-11 - придётся всё делать на уровне асма, вызовами EMT и прочим, предусмотренным в RT-11. Пока я не понял как развернуть запрос .CSISPC в команды GNU ассемблера. Но понял, что .EXIT разворачивается в emt 0350 и т.д.

BlaireCas
13.11.2021, 09:25
как развернуть запрос .CSISPC в команды GNU ассемблера. Но понял, что .EXIT разворачивается в emt 0350
В самом macro-11 можно глянуть как это делается использовав директиву .list MEB. И тогда в листинге он "развернет" макросы. Например .csispc будет:

1 .mcall .csispc
2 .list MEB
3 000000 START: .csispc
000000 005046 CLR -(SP)
000002 005046 CLR -(SP)
000004 005046 CLR -(SP)
000006 104345 EMT ^o<345>
4 000000' .end START
.MAIN. MACRO V05.06R Saturday 13-Nov-21 08:34 Page 1-1
А чтобы вытащить самому системные макросы из SYSMAC.SML - можно "расковырять" код портированного на винду macro-11. (вот этого http://retrocmp.com/tools/macro-11-on-windows). Он тоже умеет развернуть макросы в листинг, правда более много "не лишнего" в листинг выводит.
Если макрос простой - то можно туповатую утилиту препроцессора написать которая regexp-ами позаменяет всякие .ttyin например. Предварительно набив библиотеку этих макросов запуская настоящий macro-11 на временных файлах. Системных макровызовов не слишком много ведь.

- - - Добавлено - - -


Бинарники, собранные под Windows с помощью MING-W64, выложены Славе на сайт
Заметил такую штуку что в сборке недостает msys-2.0.dll (тот-же lda2sav без нее не запустить). Оно естественно быстро нагугливается где скачать отдельно данную .dll, но все-же может стоит ее добавить? (не у всех будет mingw наверняка).

AFZ
13.11.2021, 17:11
Вообще-то, с командной строкой у RT-11 все плохо. Она принимает только командные строки формата CSI, то есть список выходных файлов от одного до трех, далее, знак "=" и список до шести входных файлов. При каждом из файлов могут быть один или несколько однобуквенных ключиков типа /A, /B=177 (oct), /C=88. (dec), /D=ABC (rad50). И все.

Начиная с RT-11 v05.00 допускается записать эти списки в несколько измененном виде в одну строку с вызовом программы, а именно,
RU DEV:PROG <список входных файлов> <список выходных файлов>, через пробел (внутри списков пробелы не допускаются). Да, для вызова программы с SY: RU DEV: набирать не надо, достаточно имени программы.

Начиная, кажется, с 05.03 или с 05.04 в RT-11, наконец-то, добавили передачу остатка командной строки в неизменном виде через поле памяти, начиная с адреса 510 (вроде бы, точно не помню), но рассчитывать, что RT-11 будет 5.4 или новее нельзя - есть, допустим, такая интересная система, как АДОС (зеленоградская), которая была сделана из 5.1 и ничего про это не знает...

Так вот, KMON системы RT-11 берет этот остаток командной строки и преобразует его к формату CSI, то есть переставляет в начало список выходных файлов, затем ставит знак равенства и список входных файлов, пробел, естественно, выбрасывает. То есть, если мы наберем

PROG IFIL1,IFIL2 OFIL1,OFIL2

то программа PROG будет вызвана с SY: и получит команду CSI

OFIL1,OFIL2=IFIL1,IFIL2

Типы каждого из выходных файлов, а также тип всех входных файлов указывается в запросе .CSIGEN или .CSISPC и, если не указать типы файлов явно (например, IFIL1.DAT,IFIL2.NUM), то типы файлов будут взяты из запроса CSIGEN/CSISPC.

Все Си PDP-11, которые я пробовал, запрашивают командную строку отдельным запросом GTLIN и разбирают ее по пробелам (выделяют отдельные слова из строки), что получается из остатка командной строки после преобразования ее в формат CSI, увы, не помню.

yu.zxpk
13.11.2021, 17:35
- - - Добавлено - - -
Заметил такую штуку что в сборке недостает msys-2.0.dll (тот-же lda2sav без нее не запустить). Оно естественно быстро нагугливается где скачать отдельно данную .dll, но все-же может стоит ее добавить? (не у всех будет mingw наверняка).

Спасибо!
Сам по себе GCC (его компоненты) собраны как "static" и не требуют msys-2.0.dll.
А вот lda2sav.exe надо поправить, пересобрать из исходников.

Пересобрал lda2sav.exe и bin2load.exe, проверьте пожалуйста свежий архив

/usr/bin/i686-w64-mingw32-gcc-9.3-win32 -o lda2sav.exe -static lda2sav.c

Возможно, нужно использовать i686-w64-mingw32-gcc-9.3-posix вместо i686-w64-mingw32-gcc-9.3-win32
В обоих случая бинарник получается с зависимосью только на KERNEL32.dll и msvcrt.dll

Нашел винду, проверил: bin2load и lda2sav.exe запускаются норм без msys-2.0dll

BlaireCas
13.11.2021, 23:04
Удалось скомпилять простую программку, работает вроде, печатает что надо.
Заглянув дебагером эмулятора обнаружил DIV и использование остатка деления, все верно скомпиляло.

extern int rt11_print(int i, const char* ptr);
int inline rt11_print(int i, const char* ptr)
{
asm ("mov 4(sp), r0");
asm ("add $48, r0");
asm ("emt 0341");
asm ("bcs .-2");
asm ("mov 6(sp), r0");
asm ("emt 0351");
}

const char* txt_msg1 = " - Hello, world!";

void main()
{
for (int i=20; i<30; i++)
{
int j = i%10;
rt11_print(j, txt_msg1);
}
} Но однако! Пока допетрил как все собрать ... жжуть.
Хорошо в стартовом посте ссылка на hello где есть pdp11-aout.ld и crt0lda.s из которого надо crt0lda.o ассемблировать.
Кстати упростил его, пашет

;
.text
.GLOBAL _main
.GLOBAL start
start:
mov $01000, sp
jsr pc, _main
emt 0350

.GLOBAL ___main
___main:
rts pc

.end Иначе вот ни в жизнь бы не догадался как скомпилить хоть что-нибудь :)

Вдруг кто тоже будет долго допетривать как под виндой собрать хеллоуворлд, а make берет вот под виндой и.. не пашет пушто его нету :)
1) путь на \bin в PATH
2) взять c гитхаба в стартовом посте файлы: pdp11-aout.ld, crt0lda.s, crt0lda.o (можно и упрощенный crt0lda.s выше привел)
2) если модифицировали crt0lda.s, то его скомпилять надо pdp11-aout-as crt0lda.s -o crt0lda.o (один раз достаточно сделать)
3) батником ниже компиляется тестовый файл: build.bat test

set PATH=.\bin
pdp11-aout-gcc -mbm2 -msoft-float -c -o %1.o %1.c
pdp11-aout-ld --entry 01000 -T pdp11-aout.ld %1.o -o %1.out
bin2load -a -f %1.out -o %1.lda -b 01000
del /f %1.sav
lda2sav.exe -o %1.sav %1.lda
Вообщем хорошо что есть рабочий GCC. (а то я пробовал DECUS C для УКНЦ - вот там адъ и погибель, там настоящий Керниган и Ричи стайл, про анси не слышали, только самый трушный С, только хардкор)

Oleg N. Cher
14.11.2021, 10:32
Тогда раз обращение к параметрам идёт через 4(sp) и 6(sp), то значит атрибут inline игнорится?



int inline rt11_print(int i, const char* ptr)
{
asm ("mov 4(sp), r0");
...
}
Кстати, если добавить опцию -fomit-frame-pointer, то указатель фрейма не будет сохраняться на стеке и секции enter/leave будут покомпактнее, а 4(sp) превратится в 2(sp).

BlaireCas
14.11.2021, 13:21
если добавить опцию -fomit-frame-pointer, то указатель фрейма не будет сохраняться на стеке
О, да. Так стало намного лучше.

А насчет inline - я вообще сомневаюсь что в GCC это работает без включения опций оптимизации. Но с включенной оптимизацией навроде -О1 - скомпиленное "ломается" и у меня не заработало в итоге. "Тонкие" настройки не пробовал. Можно как вариант атрибут на функцию попробовать повесить что-то типа always_inline.. Не изучал такой момент.

Насчет регистров - самому казалось что тут будет как для ARM, то-есть первые четыре параметра вне зависимости от опций компилятора всегда идут через R0-R3. Но нет, идет через стек. Даже если по стечению обстоятельств один из параметров идет через R0 - в стек он все-равно будет засунут.

yu.zxpk
14.11.2021, 14:17
у PDP-11 слишком мало регистров, чтоб через них передавать параметры. Например, в том же i386 так же все через стек сделано. Только в AMD64 изменили ABI, плюс регистров добавили.

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

BlaireCas
14.11.2021, 15:34
Поломать GCC, чтоб передавать параметры через регистры -- можно
Хакир :) Но возможно это не требуется. Ассемблерные процедурки которые требуют скорости - они вообще без параметров-же будут (ожидая значения в регистрах). А регистры можно загрузить через asm() как в примере там показано:

#define RT11_TTYOUT(c) asm(" mov %[input_c],r0\n emt 0341\n bcs .-2\n" : : [input_c] "r" (c) : "r0", "cc" )

Я не знаю что означают символы : : например, надо гуглить синтаксис. Но оно работает и судя по дизасму все верно делает. Вставляет код который загружает параметр в регистр R0 и вызывает EMT и надеюсь компилятор понимает что регистр R0 был использован и учитывает это.

- - - Добавлено - - -

Чего еще хочется от GCC - чтобы FIS инструкции (эти четыре FADD, FSUB, FDIV, FMUL) использовал и формат float-а был-бы DEC-овский. Например на УКНЦ эти инструкции повешены пусть и на прерывание, но у меня работали в какой-то версии RT-11.
А то ценность сишника без плавучки нереально сильно падает.

yu.zxpk
14.11.2021, 16:05
Я посмотрю, что можно сделать с поддержкой FIS для 1801ВМ2.
Уже пора прикрутить тесты собранного кода на "железной" реализации 1801ВМ1 и 1801ВМ2 в виде FPGA.
То есть разбираться с DejaGNU framework, написать минимальный ROM, который будет уметь только LDA грузить и работать с 1 COM-портом, сделать соотвествующее описание "board" в DejaGNU и методы работы с ним.

BlaireCas
14.11.2021, 16:40
Попробовал на УКНЦ комманды FIS, например FADD сработало. Вообще даже не встраивая их в сам GCC можно прикрутить обертки как я понимаю для операций с float. Но я не знаю какой формат GCCшного float-a да и конечно хорошо если бы они встроены были.
Как пример теста - складывает 12.0 и 12.0 получается 24.0, работает норм. Команды типа fadd ассемблер понимает, уже плюс.


#define _TTYOUT(c) asm(" mov %[input_c],r0\n emt 0341\n bcs .-2\n" : : [input_c] "r" (c) : "r0", "cc" )
#define LOAD_R0(c) asm(" mov %[input_c],r0\n" : : [input_c] "r" (c) : "r0", "cc" )

/*
char* buf = "00000";

extern void rt11_decimal (unsigned int u)
{
unsigned int k = 5;
while (u > 0) { buf[--k] = u%10+48; u = u/10; }
while (k < 5) RT11_TTYOUT(buf[k++]);
}
*/

extern void rt11_bin (unsigned int u)
{
for (int k=0; k<16; k++) {
if (u&0x8000) _TTYOUT('1'); else _TTYOUT('0');
u = u<<1;
}
}

unsigned int fbuf[4];

void main()
{
// 12.0f
unsigned int Ahi = 0b0100001001000000; // [1-bit sign] [8-bits exponent, 177=-1, 200=0, 201=1 etc] [24-bit mantissa with first hidden bit always assume = 1]
unsigned int Alo = 0b0000000000000000;
// 12.0f
unsigned int Bhi = 0b0100001001000000;
unsigned int Blo = 0b0000000000000000;
// output
rt11_bin(Ahi); rt11_bin(Alo); _TTYOUT('+'); _TTYOUT('\n'); _TTYOUT('\r');
rt11_bin(Bhi); rt11_bin(Blo); _TTYOUT('='); _TTYOUT('\n'); _TTYOUT('\r');
// struct for fis
fbuf[0] = Bhi;
fbuf[1] = Blo;
fbuf[2] = Ahi;
fbuf[3] = Alo;
// fadd
LOAD_R0(fbuf);
asm ("fadd r0\n");
// output
rt11_bin(fbuf[2]); rt11_bin(fbuf[3]);
}

yu.zxpk
14.11.2021, 21:38
в DECUS есть реализации FADD, FSUB, FMUL. Это можно "притянуть" в GAS (GNU Asm)/GCC. Но вот отмаппить `float` на FIS -- не просто, кмк

Oleg N. Cher
15.11.2021, 01:33
Директива asm в GCC имеет дополнительные параметры для макроподстановки входных и выходных аргументов. Думаю, мы должны их использовать вместо явного указания N(sp) и это, возможно, поможет правильно генерировать код в inline-функциях:


void Palette (int n)
{
asm("BICB $0177774, %0"::"g"(n)); // превращается в BICB $0177774, 02(sp)
...
}

https://ru.wikipedia.org/wiki/GCC_Inline_Assemblyhttp://asmcourse.cs.msu.ru/wp-content/uploads/2013/04/gcc-inline-asm.pdf

Oleg N. Cher
26.11.2021, 11:57
Коллеги, как можно установить вручную адрес секции .data? У меня .data всегда устанавливается в адрес (.code + 0x2000)
Хотя по содержимому скрипта pdp11-aout.ld этого и не скажешь.

То есть, когда юзаются библиотеки с переменными, я получаю бинарник от 8 Кб.

Простые неинициализированные переменные в .data ложатся раньше константного массива.

Если выкусить секцию .data вообще:

pdp11-aout-objcopy.exe --only-section .text --output-target binary %1.out ..\%1.bin
то мы получаем нормальный маленький бинарь, но константные массивы тоже выкусываются.

В идеале хотелось бы, чтобы константный массив ложился в секцию .data раньше, чем неинициализированные переменные (чтобы не увеличивать ими размер бинаря).

Пробовал пихать код и данные в одну секцию .text:


OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata);
data = .;
*(.data)
}
end = .;
}
Но с таким скриптом сборки переменные налазят на константный массив и занимают с ним одно адресное пространство. Помогает ALIGN:


OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(0100);
data = .;
*(.data)
}
end = .;
}
Но при таком решении приходится регулировать размер в ALIGN ручками, чтобы константный массив не налазил на переменные.

В общем, нужен хороший совет. Как сделать, чтобы секция .data ложилась сразу после секции кода?

Sandro
26.11.2021, 15:50
Попробовал на УКНЦ комманды FIS, например FADD сработало. Вообще даже не встраивая их в сам GCC можно прикрутить обертки как я понимаю для операций с float. Но я не знаю какой формат GCCшного float-a да и конечно хорошо если бы они встроены были.


IEEE-754 single precision, разумеется. Который совместим с плавучкой от PDP-11, только слова в обратном порядке.

yu.zxpk
26.11.2021, 16:02
Полезно посмотреть на *.map файл, там будет раскладка символов по адресам.
ALIGN можно сделать маленьким, хоть 010.
Не инициализированные переменные ложатся в секцию BSS.

Перезалил свои примеры `hello-gcc` на github.

https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-aout.ld

Oleg N. Cher
26.11.2021, 16:28
Насчёт ALIGN'а понятно, спасибо. Непонятно почему наслаиваются друг на друга переменные и конст. массив. И почему задано такое огромное окошко между .text и .data (0x2000). И как задать его размер. Это ведь не ALIGN 0100 так работает. Даже если убрать ALIGN - будет то же самое.

Среди опций pdp11-aout-objcopy.exe вроде бы нужных нет (чтобы задать размер окна между .text и .data).

Да, один нюанс: я экспериментирую с генерацией не в .sav, а в .bin для БК-0010/0011. Возможно, дело в этом.

Попробуйте генерить .bin и использовать переменные из других модулей и библиотек. Также я могу залить свой пример, чтобы помочь воспроизвести проблему (только вот кто будет его ковырять?)

- - - Добавлено - - -


Не инициализированные переменные ложатся в секцию BSS.Вы в этом точно уверены? Мне так не показалось. По-моему, GCC для PDP-11 вообще не использует секцию .bss

yu.zxpk
26.11.2021, 16:51
за 0x2000 - не скажу, не понимаю, о чем речь.

Вот свежий вариант LD скрипт для SAV файла:
https://github.com/yshestakov/pdp11-toolchain/blob/master/hello-gcc/pdp11-sav.ld

В нем в 1й блок файла (512байт) сделан в виде savhdr.o файла. То есть структура заголовка SAV описана в savhdr.s.
В частности, это позволяет в адрес 050 (Program’s high limit) внести адрес метки `.end` -- где конец программы с данными

- - - Добавлено - - -



- - - Добавлено - - -

Вы в этом точно уверены? Мне так не показалось. По-моему, GCC для PDP-11 вообще не использует секцию .bss

Нет, уже не уверен. Смотрю в файл hello.out.map, вижу секция .bss есть, но не инициализированная переменная, или инициализированная 0-лем попадает в секцию data.

Надо разбираться. Я за год+ все забыл.

Судя по всему, `pdp11-sav.ld` -- не рабочий. Я пользую pdp11-aout.ld скрипт + lda2sav программу для преобразования LDA->SAV.

- - - Добавлено - - -

При `ALIGN(040)` не работает rt11_print() и puts() функции. При `ALIGN(0100)` -- все работает. Почему так? Не помню. Надо разбираться, опять таки.
размер всей программы hello минус 512 байт заголовка == 384 байта. При этом на ALIGN между секциями фактически ушло 100 байт. Много ли это или мало?

Oleg N. Cher
27.11.2021, 17:00
100 байт это не так много конечно, но наверное стандарты RT-11 этого требуют. Других объяснений почему могут не работать функции rt11_print() и puts() - у меня нет.

Итак, я беру Ваш pdp11-sav.ld и сырой бинарь после pdp11-aout-ld.exe (в скрипте же уже выбран формат OUTPUT_FORMAT("binary") вместо "a.out-pdp11" как было раньше). Попутно появилась мысль не вызывать pdp11-aout-objcopy.exe для конвертации a.out в bin вообще. Но не тут-то было. Бинарь pdp11-aout-ld.exe даёт, но настраивает его с нашим скриптом крайне криво - при заданном стартовом адресе 01000 (для БК-0010/0011М) адрес константного массива находится _ниже_ адреса кода. Вот сейчас отладчик показывает адрес константного массива 0424, т.е. константный массив размещён _после_ кода, а адрес настроен как будто бы он находится _до_ кода. Притом увеличение этого числа ничего не даёт:

.data : AT(phys + data + 1000)

Я вынужден опять откатиться до формата a.out и вызова objcopy - так хотя бы криво, но работало. А бинарь после aout-ld со съехавшими адресами массивов вообще нерабочий. Повторюсь, что в случае вызова lda2sav этой проблемы наверное нет.

Кстати, может быть, что константные массивы ложатся в секцию (или подсекцию? как это правильно?) .rodata, а не .bss?

- - - Добавлено - - -

Я воспроизвёл проблему на маленьком исходнике:

76511

Как видим, здесь адрес массива равен 016, а должен быть больше 01000.

Я склоняюсь к тому, что проблема есть и в .sav, просто Вы не дошли до работы с конст. массивами.

http://i.piccy_.info/i9/10db24ac18ae7f7c1ee35b2eee4bca33/1638021372/27632/1320134/Test.png

- - - Добавлено - - -

Кстати, здесь и адрес данных получился странный, наложился на последнюю команду в коде. Видите? Хотя ALIGN(010) в скрипте есть.

yu.zxpk
27.11.2021, 23:46
Да, с LD скриптом проблема: при малом ALIGN (<0100) у меня портится строка, которая выводится. Починю - маякну

Oleg N. Cher
28.11.2021, 00:16
Спасибо.
Смотрите как я генерю рабочий бинарь с правильными смещениями (это может помочь. Наверное):


OUTPUT_FORMAT("a.out-pdp11")
ENTRY(start)
phys = 00001000;
SECTIONS
{
.text phys : AT(phys)
{
code = .;
*(.text)
*(.rodata)
. = ALIGN(phys);
*(.data);
}
}
.bss убираю вообще, а .data подпихиваю в .text

Теперь так:


pdp11-aout-ld.exe -T %PdpDev%\Bin\pdp11-bin.ld %PdpDev%\Lib\crt0.o %1.o %Modules% %Libraries% -o %1.out
IF errorlevel 1 PAUSE
pdp11-aout-objcopy.exe --output-target binary %1.out ..\%1.bin
IF errorlevel 1 PAUSE
%PdpDev%\Bin\make-bk.exe ..\%1 512
IF errorlevel 1 PAUSE

Здесь всё хорошо, кроме ". = ALIGN(phys)" перед данными. Что туда добавить - так и не понял. Делаю ALIGN(phys+phys) и тоже работает, хотя ничего визуально не меняется (даже сам бинарь). Делаю мелкое число - глючит.

AFZ
29.11.2021, 19:53
Коллеги, напоминаю. Традиционным для Си на PDp-11 считается расположение переменных и массивов, локальных для любой С-функции, включая main(), в стеке. Из-за чего обычных для RT-11 96 слов стека (192 байта) между тоже традиционным для RT-11 началом программы 001000 и "желтой" границей стека 000500, чаще всего, не хватает. Поэтому программы из-под компиляторов Си, обычно, линкуют с ключиком /BOT команды LINK, указывая конкретный адрес, откуда начнется программа и ниже которого будет стек.

Я при написании и отладке программ на С, обычно, сначала указывал /BOT:5000, а далее отладка покажет, много это или мало...

Hunta
29.11.2021, 20:40
желтой" границей стека 000500
Жёлтая граница стека - ниже 400, красная - ниже 340

Ну и если есть регистр границы стека - возможны варианты - он задаёт значение старшего байта для границы стека, а в младшем - см выше.

yu.zxpk
30.11.2021, 01:08
Значит так.
Утилита `bin2load.exe` (a.out -> LDA) имеет внутри harcoded значение ALIGN секции = 0100.
Если в LD скрипте выставить значение ALIGN меньше 0100, то смешение в результирующем LDA не будут соответствовать тому, что прописано в A.OUT файле линкером.

#define SECT_ALIGN 0100
/*
** If we have a data section, we align it to a boundary and
** save it in a new block.
** In this version the boundary is fixed (SECT_ALIGN, defined in bin2load.h)
** I'll give the chance of specifying it as a program argument... perhaps
**
** WARNING: The boundary size MUST be the same as the specified in the link
** script, else BAD THINGS WILL HAPPEN...
*/

Oleg N. Cher
30.11.2021, 01:14
Но дело не только в bin2load, ведь я её не использую. Или где-то ещё захардкожено 0100?

Такой вопрос. Столкнулся с тем, что при умножении идёт вызов к неопределённой подпрограмме __mulhi3:


;# Test.c:51: Laser2_PTBL2(x + 1, y * 10 + 28, 103);
mov $012,-(sp) ;#,
mov 026(sp),-(sp) ;# y,
jsr pc,___mulhi3 ;#
add $04,sp ;#,
Это, получается, 16-битное умножение. Хотя не очень понятно что здесь обозначает hi и 3.

Где-то есть готовая эффективная реализация умножения для 1801BM1 ?

yu.zxpk
30.11.2021, 01:45
Функция __mulhi3 должна быть частью LIBGCC, но для PDP11 она не реализована. И вообще, libgcc для PDP-11 я ее не собирал (не дошли руки):

https://github.com/gcc-mirror/gcc/blob/master/libgcc/config/pdp11/t-pdp11

hi -- half-integer (16-bit) в терминах GCC. qi -- quarter integer (8 bit), si -- 32-bit integer.

https://www.cse.iitb.ac.in/grc/intdocs/gcc-implementation-details.html

в Decus-C я нашел реализацию умножения над 2-мя long. Думаю, ее и надо брать за основу для реализации __mulhi3 (для 1801ВМ1 в общем).

http://www.ibiblio.org/pub/academic/computer-science/history/pdp-11/language/decus-C/5,4/mull.mac

Там рядом лежит fmul.mac, кстати.

Для процессора MSP430 эта функция реализована так:

/* Public domain. */
extern int __mulhi3 (int, int);

int
__mulhi3 (int x, int y)
{
char bit;
int neg = 0;
int rv = 0;

if (y < 0)
{
y = - y;
neg = 1;
}

for (bit = 0; y && bit < sizeof (y) * 8; bit ++)
{
if (y & 1)
rv += x;
x <<= 1;
y >>= 1;
}

return neg ? - rv : rv;
}

Oleg N. Cher
30.11.2021, 12:50
В журнале "Персональный компьютер БК-0010, БК-0011М" №2-1995 есть такая реализация умножения:


; Подпрограмма умножения
; 8-разрядных чисел
; R2-множимое, R3-множитель,
; R4-результат, R5-счетчик итераций
MUL: MOV #10, R5 ; Цикл из 8 итераций
CLR R4 ; Очистить аккумулятор результата
1: ASR R3 ; Очередной разряд множителя
ВСС 2 ; Если 0 - дальше
ADD R2, R4 ; Иначе прибавить множимое
2: ASL R2 ; Сдвиг множимого на один разряд
SOB R5, 1 ; Если не конец - в цикл
RET ; Выход
; Алгоритм этой программы построен совершенно аналогично
; правилу умножения «в столбик» для двоичных чисел
; и, видимо, в пояснениях не нуждается.

Вроде выглядит неплохо. Меня смутило только то, что заявлено умножение только 8-разрядных чисел. Как можно её модифицировать для 16-разрядных? Также я могу расковырять подпрограмму умножения из OMSI Pascal. Надо?

- - - Добавлено - - -

А хотя, скорее всего, OMSI Pascal юзает аппаратное умножение, он же для 1801BM2 и RT-11.

Sandro
30.11.2021, 13:41
Заменить #10 на #20. Но тогда, естественно, уже возможно перепеолнение.

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



; r0 = r1 * r2
mul: clr r0
cmp r1, r2
bhis 1$
xor r1, r2
xor r2, r1
xor r1, r2
1$: tst r2
beq 0$
clc
ror r2
bcc 2$
add r1, r0
2$: asl r1
add r1, r0
asr r2
bcs 2$
beq 3$
sub r1, r0
br 2$
3$: sub r1, r0
0$: rts r7


Набил по памяти, но вроде правильно. Это т.н. алгоритм с восстановлением, классическая оптимизация. Тело цикла умножения -- это 2$..bcs 2$ для единичного бита и 2$..br 2$ для нулевого. Выполняется до исчерпания единичных бит в меньшем множителе.
Можно ещё быстрее, но это приводит к ещё большему объёму кода.

А ещё быстрее -- через таблицу квадратов, пользуясь следующим равенством:

((x + y) ^ 2) / 4 - ((x - y) ^ 2) / 4 = x * y;