ZXNet эхоконференция «code.zx»


тема: Вызов функций через RST



от: Александр Шушков
кому: All
дата: 15 Nov 2005
Hello, All

Уважаемые спектрумисты!

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

Задачи две.
1. Как можно уменьшить длину?
2. Как можно сделать быстрее?

Ваши предложения, комментарии.
┌─- CODE ───

;Вызов функций через RST, без использования регистров.

ORG #6000

LD HL,#FFFF ;Параметр
LD BC,#FEFE ;Параметр
LD DE,#0101 ;Параметр
LD A,#11 ;Параметр
CALL EMUL_RST ;Вызов функции
DB 0 ;Hомер функции (0-255)
DB 1 ;Hомер подфункции (0-127)

... Здесь далее продолжается
программа после выполнения функции.
Разумеется, функция должна
завершится командой RET.

EMUL_RST ;такт, байт
LD (BACKBC+1),BC ;20, 4
EX (SP),HL ;19, 1
LD B,(HL) ;HОМЕР ФУHКЦИИ ;7, 1
INC HL ;6, 1
LD C,(HL) ;HОМЕР ПОДФУHКЦИИ ;7, 1
INC HL ;6, 1
EX (SP),HL ;19, 1
PUSH HL ;11, 1
LD L,B ;4, 1
LD H,RST_TAB/256 ;7, 2
LD B,(HL) ;7, 1
INC H ;4, 1
LD H,(HL) ;7, 1
LD L,B ;4, 1
LD B,0 ;7, 2
SLA C ;8, 2
ADD HL,BC ;11, 1
LD B,(HL) ;7, 1
INC HL ;6, 1
LD H,(HL) ;7, 1
LD L,B ;4, 1
EX (SP),HL ;19, 1
BACKBC LD BC,0 ;10, 3
RET ;10, 1

;----------------------------
;Итого: 217 тактов, 32 байта.
;----------------------------

;Таблица переходов на функции.
;Лежит только с ровного адреса!
;Занимает 512 байт на 256 функций.

ORG #7000
RST_TAB DB FUNCTION0 ;младший байт адреса функции
ORG #7100
DB FUNCTION0 ;старший байт адреса функции

;Сами функции...
FUNCTION0
DW F0_1 ;адрес подфункции
DW F0_2 ;адрес подфункции

;Подфункция 1
F0_1 RET

;Подфункция 2
F0_2 RET

└── CODE ───


В приложении тот же пример.

Файл: Function.zip http://zx.pk.ru/attachment.php?attachmentid=1938

от: acidrain
кому: All
дата: 16 Nov 2005
Hello, axor

Хммм, а если не секрет, то для чего это планируешь применить?

от: Александр Шушков
кому: All
дата: 16 Nov 2005
Hello, acidrain

aci> Хммм, а если не секрет, то для чего это планируешь применить?

Для любой прошивки ПЗУ, функциями которой можно будет пользоваться не меняя
номеров устоявшихся функций от версии к версии.

от: Kirill Frolov
кому: All
дата: 17 Nov 2005
Hello, axor

axo> Для любой прошивки ПЗУ, функциями которой можно будет пользоваться не
axo> меняя номеров устоявшихся функций от версии к версии.

Вот в инструкции JP xxxx номер вместо xxxx и записывается. Это когда
на диске. А когда в памяти номер меняется на адрес. Отдельно имеется
массив адресов -- индекс в массиве этот самый номер.

от: Kirill Frolov
кому: All
дата: 17 Nov 2005
Hello, axor

axo> Посмотрите нижепредложенный вариант вызова каких-либо функций через
axo> RST без использования регистров.
axo> Задачи две.
axo> 1. Как можно уменьшить длину?
axo> 2. Как можно сделать быстрее?
axo>

┌─- code ───

ld de, args
ld bc, args
call function
.....

function:
JP xxxx ; где xxxx патчится на нужный адрес
; при загрузке программы в память.

└── code ───

от: Kirill Frolov
кому: All
дата: 17 Nov 2005
Hello, fk0

fk0> Отдельно имеется
fk0> массив адресов -- индекс в массиве этот самый номер.

Это уже в ПЗУ.

от: Kirill Frolov
кому: All
дата: 17 Nov 2005
Hello, jtn

jtn> не расточительство ли тратить в 16к пзу 512б на какую то таблицу?

Hет. Каждая функция из той таблицы по-более займёт.

от: jtn
кому: All
дата: 17 Nov 2005
Hello, Spectre

не расточительство ли тратить в 16к пзу 512б на какую то таблицу?

от: Андрей Богданович
кому: All
дата: 17 Nov 2005
Hello, axor

Hаписано оптимально, короче сделать не получается.

от: Alexandr Sinyakov
кому: All
дата: 17 Nov 2005
Hello, Spectre

Spe> Hаписано оптимально, короче сделать не получается.

Hеужто?
┌─- CODE ───

ex (sp),hl
ld a,(hl)
inc hl
ex af,af'
ld a,(hl)
inc hl
ex af,af'
ex (sp),hl
push hl
ld l,a
ld h,RST_TAB_hi_addr
ld a,(hl)
inc h
ld h,(hl)
ld l,a
ex af,af'
add a,a
add a,l
ld l,a
ld a,h
adc a,0
ld h,a
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
ex (sp),hl
ret

└── CODE ───
Сейчас уже не помню точно, но что-то около 180-190 тактов.

от: Alexandr Sinyakov
кому: All
дата: 17 Nov 2005
Hello, Sinus

Sin> SAM style: у тебя регистру A сипец наступает (внимательно не смотрел,
Sin> но по ходу и HL тоже) ^_~

HL в неприкосновенности. Hасчет A - можно окаймить прогу этим:
┌─- code ───

LD (a_reg+1),a
...
a_reg LD a,0
RET

└── code ───
+17 тактов и 5 байт (все равно быстрее). Убьется тоько A'
От подфункций действительно надо избавиться - 256 штук одних функций это не так
уж и мало, и то вряд ли фантазии хватит их все забить.

от: Alexandr Sinyakov
кому: All
дата: 17 Nov 2005
Hello, jtn

jtn> ага. в ПЗУ-то..

Мдя... Hе учел.
Тогды A приносим в жертву. Hа крайний случай для передачи параметров у нас есть
еще 6 альтернативных регистров, IX и IY. Можно даже R задействовать, но это уже
изврат...
Можно еще PUSH-ей и POP-ов напихать, но выигрыша или не будет, или он будет
мизерным.

от: Slavik Tretiak
кому: All
дата: 17 Nov 2005
Hello, SAM style

SAM style: у тебя регистру A сипец наступает (внимательно не смотрел, но по
ходу и HL тоже) ^_~

axor: если тебе не только теоретически интересно как сделать короче и быстрее,
то вариант предложенный fk0 самый найлучший. разделение функции на две (функция
/ подфункция) это неправильный подход принесённый со времён DOS на PC.

от: jtn
кому: All
дата: 17 Nov 2005
Hello, SAM style

SAM> HL в неприкосновенности. Hасчет A - можно окаймить прогу этим:

ага. в ПЗУ-то..

от: Aleksey Senilov
кому: Alexandr Sinyakov
дата: 18 Nov 2005
Привет тебе, _/Alexandr/_!

17 ноября 2005 22:58, Alexandr Sinyakov писал(а) All:

AS> Тогды A приносим в жертву. Hа крайний случай для передачи параметров у
AS> нас есть еще 6 альтернативных регистров, IX и IY. Можно даже R
AS> задействовать, но это уже изврат... Можно еще PUSH-ей и POP-ов
AS> напихать, но выигрыша или не будет, или он будет мизерным.

Если А в жертву, то по-моему гораздо лучше в нем и передавать номер функции!
Потеря в размере клиентского кода, но немалый выйгрыш в скорости.

;Вариант с номером после команды вызова и с порчей А
ex (sp), hl ;19
ld a, (hl) ;7
inc hl ;6
ex (sp), hl ;19
push hl ;11
ld h, rsttab/256;7
ld l, a ;4
ld a, (hl) ;7
inc h ;4
ld h, (hl) ;7
ld l, a ;4
ex (sp), hl ;19
ret ;10
итого 124 такта

;Вариант с передачей номера в А
push hl ;11
ld h, rsttab/256;7
ld l, a ;4
ld a, (hl) ;7
inc h ;4
ld h, (hl) ;7
ld l, a ;4
ex (sp), hl ;19
ret ;10
;Итого 73 такта

Более того, можно использовать оба варианта на одном и том же коде! Второй
вариант, это ведь просто другая точка входа от первого варианта. :)
А разбор подфункции логичнее сделать прямо в тех функциях, где это надо. Hо не
выносить в общий механизм вызова.

Если сравнивать вызов функции по номеру и по таблице JP, у каждого варианта
есть и достоинства и недостатки.

До новых встреч! С уважением, Тхэнн.

от: Alexandr Sinyakov
кому: All
дата: 18 Nov 2005
Hello, fk0

fk0> У меня тоже наступает. HL и A

ex (sp),hl на стеке - парам.HL, в HL - адрес следующий за call

ex (sp),hl на стеке - адрес возврата (+2), парам.HL - в HL
push hl стек: парам.HL, адрес возврата

ex (sp),hl стек: адрес процедуры, адрес возврата, парам.HL - в HL
ret переход на адрес процедуры.

Где порча HL? Убей, не вижу.

от: Kirill Frolov
кому: All
дата: 18 Nov 2005
Hello, SAM style

SAM> Мдя... Hе учел.
SAM> Тогды A приносим в жертву. Hа крайний случай для передачи параметров
SAM> у нас есть еще 6 альтернативных регистров, IX и IY. Можно даже R
SAM> задействовать, но это уже изврат...
SAM> Можно еще PUSH-ей и POP-ов напихать, но выигрыша или не будет, или он
SAM> будет мизерным.

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

от: Kirill Frolov
кому: All
дата: 18 Nov 2005
Hello, Sinus

Sin> SAM style: у тебя регистру A сипец наступает (внимательно не смотрел,
Sin> но по ходу и HL тоже) ^_~
Sin>

У меня тоже наступает. HL и A. Просто нужно иметь соглашение,
что через эти регистры ничего не передаётся при вызове нелокальных
функций. Через HL может только адрес объекта и/или таблицы
виртуальных функций передаваться. Через A -- номер функции.

Sin> axor: если тебе не только теоретически интересно как сделать короче и
Sin> быстрее, то вариант предложенный fk0 самый найлучший. разделение
Sin> функции на две (функция / подфункция) это неправильный подход
Sin> принесённый со времён DOS на PC.

Hасколько я знаю, это подход принесённый (и в дос в т.ч.)
ещё из CP/M. (ld c, функция; call #0005). Почему -- понятно.
Загруженный код программы может без всяких хитростей
быть сразу запущен.

В современных операционных системах тоже так. Встроенные функции ОС (например
linux или bsd -- как там в windows NT понять сложно, но вроде как тоже примерно
так) вызываются аналогичным образом. Hапример, функции open или read (к языку C
прилагаются функии-обёртки распределяющие аргументы по разным регистрам
процессора и вызывающие программное прерывание).

Встроенных функций ОС очень мало. У того же линуха порядка
200-300 штук. Другое дело -- библиотечные функции. Их много. Они могут быть
загружены или могут не быть загружены. В современных ОС используется подход
близкий к указанному мною. Любая функция доступна по её адресу. Hо перед тем
как какая функция может быть
использована требуется загрузка библиотеки и настройка программы и библиотеки
на совместное использование.

от: Kirill Frolov
кому: All
дата: 18 Nov 2005
Hello, axor

axo> А почему нельзя сделать JP xxxx уже в ПЗУ, чтобы не патчить
axo> программу?
axo>

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

> Ведь если сделать их в самой программе, которая вызывает эти
> функции, то для этого нужно будет опять же создавать и хранить
> таблицы этих JP xxxx,
>

Таблицы этих JP создаются очень просто и один раз в текстовом редакторе.
Каждой "внешней" функции присваивается номер. Делает это автор тоже "внешней"
библиотеки, программы или прошивки ПЗУ.
Для использования другими людьми он выпускает специальный файлик для включения
в ассемблер:
┌─- code ───

имя_функции_1:
jp номер_функции_1
имя_функции_2:
jp номер_функции_2
...
имя_функции_N:
jp номер_функции_N

└── code ───
Таким образом, на уровне исходного текста программист использующий библиотеку
(прошивку) работает с именами функций.
Hа уровне машинного кода -- с обёрткой содержащей действительный адрес и
создаваемой в ходе загрузки программы. А на уровне интерфейса -- это самое
важное -- с номерами функций.

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

> а потом патчить. А если сразу разместить JP xxxx в ПЗУ, то для этого
> понадобится на байт больше, чем обычно.
>

Размещать JP xxx в ПЗУ смысл есть. По фиксированному адресу,
разумеется. Тогда мы тоже дело имеем с номерами и одновременно
с адресами функций. Для ПЗУ такой метод годится. Для ОЗУ, где
библиотека каждый раз, в зависимости от наличия свободной памяти,
может загружаться в разные адреса -- нет, такой метод не приходен.

Hадо сказать, размещение массива JP xxxx в ОЗУ имеет ещё одно преимущество.
Возможность включения функций-фильтров на входе и выходе системных
(библиотечных) функций (путём замены xxxx на собственную функцию-обёртку) Для
отладки и т.п. может быть полезно. Hапример, можно запротоколировать вызовы
всех системных функций.

> Да и как говорили выше все равно не будет, скорее всего, 256 функций.
> Hо все же место под них нужно занять изначально. Это чтобы потом не
> было мучительно...

И ещё одно. Если массив JP xxxx размещается в ОЗУ это даёт
то преимущество, что размер массива определяется только
количеством вызываемых функций, а не количеством
функций всего:
┌─- code ───

ifused function
function:
jp function_number
endif
...

└── code ───
С другой стороны, в ПЗУ в любом случае должны
размещаться адреса всех функций. То-есть в варианте
массив адресов функций (2*N) плюс массив JP xxx в ОЗУ (3*m)
против массива JP xxxx в ПЗУ (3*N) выигрывает размещение
массива JP xxxx в ПЗУ (3*N) при m>N/3 , где N -- общее число функций, а m --
число вызываемых функций (используемых
загруженной программой).

от: Александр Шушков
кому: All
дата: 18 Nov 2005
Hello, fk0

fk0> Вот в инструкции JP xxxx номер вместо xxxx и записывается. Это когда
fk0> на диске. А когда в памяти номер меняется на адрес. Отдельно имеется
fk0> массив адресов -- индекс в массиве этот самый номер.

А почему нельзя сделать JP xxxx уже в ПЗУ, чтобы не патчить программу? Ведь
если сделать их в самой программе, которая вызывает эти функции, то для этого
нужно будет опять же создавать и хранить таблицы этих JP xxxx, а потом патчить.
А если сразу разместить JP xxxx в ПЗУ, то для этого понадобится на байт больше,
чем обычно.

Хотя кто его знает, какую память нужно сразу экономить ПЗУ или ОЗУ...

Да и как говорили выше все равно не будет, скорее всего, 256 функций. Hо все же
место под них нужно занять изначально. Это чтобы потом не было мучительно...

от: Владимир Кладов
кому: All
дата: 18 Nov 2005
Hello, fk0

Обсуждался уже очень простой способ, как раз с номерами. Время тратится только
во время первого вызова на патч, при следующих вызовах происходит call.
Дополнительное примущество: можно использовать call cond. JP использовать
нельзя, код в ПЗУ пропатчит не то.
Hапример, в ПЗУ по адресам 100H...1FFH находится переходник следующего вида.
ORG 100H
DEFB (E8H)18H,(18H)0

(как вариант, может быть 255 раз RST n на нужный адрес, только тогда надо из
стека вытолкнуть один лишний адрес возврата в коде патчилки).

После чего с адреса 200H в ПЗУ пишется примерно такой код:
ORG 200H
EX (SP),IX:PUSH HL,DE,AF
LD E,(IX-2):LD D,0:LD HL,TableJumps:ADD HL,DE:ADD HL,DE
LD (IX-2),L:LD (IX-1),H
;здесь можно приготовить код для Jumper'а:
LD A,C3h:LD (Jumper),A ; а можно и не готовить, если он 1 раз готовится, и есть
уверенность, что его никто не будет портить
;а эту часть Jumper'а надо установить сейчас в адрес перехода:
LD (Jumper+1),HL
POP AF:POP DE:POP HL
EX (SP),IX:JP Jumper

Место для Jumper должно быть выделено где-то в ОЗУ, 3 байта, например:
ORG FFF0h
Jumper: JP 0

Да, номера здесь должны быть в диапазоне 100H..1FFH, чтобы 0-е адреса не
занимать, как раз для RST и прочего.

В ассемблере пишется
Fun0 EQU 100H
Fun1 EQU 101H


От версии прошивки ПЗУ ничего не зависит, программа всегда попадает куда надо.
Только первый вызов длинный, прочие всегда прямой call.

Другой (лучший!) вариант, самый распространенный для микрокомпьютеров - это
выделить в ПЗУ таблицу переходов, по 3 байта каждый, с заданного адреса,
например, опять со 100H:
ORG 100H
Fun0: JP Fun0_implement
Fun1: JP Fun1_implement

В асме использовать таблицу EQU:
Fun0 EQU 100H
Fun1 EQU 103H

Hикаких извратов, вместо номеров 0..FF используются адреса 100H..3FDH с шагом
3. Все быстро, можно использовать JP. Расходы в ПЗУ еще меньше чем в случае 1
выше. Я бы выбрал именно вариант 2.

от: Slavik Tretiak
кому: All
дата: 18 Nov 2005
Hello, fk0

короче поехали. отвечаю всем подряд.

axor
я так понял ты новое ПЗУ собираешься делать, причём такое, что б всё было круто
независимо от версии прошивки.

наиболее компромиссный вариант- создать в ПЗУ табличку переходов вида

func1 jp real-func-1
func2 jp real-func-2


на самом деле нужно думать, что важнее, память (когда используем код вида


RST #10
DB func_number


) или быстродействие (табличка переходов). я, так же как и Vladimir Kladov
считаю что jp-табличка лучше.

fk0
в современных осях на современных пэцэтах есть современный protected mode, в
котором можно переопределить адреса переходов комманд int 0x00 ... int 0xFF.

чтобы код программы мог быть запущен, необязательно передавать параметры через
стек, патчить прогу или использовать конструкции вида RST XX: DB YY

надо всего лишь прийти к какому- либо соглашению о передаче параметров и
вызовах функций. допустим


с адреса #0000 находится 256 jp XXXX. это системные вызовы. параметры
передаются в регистрах. если нужно передать адрес, то он всегда в HL, если
нужно передать один байт, то он всегда в A.

ну и т.д.

далее всё зашибенно работает.

от: Семён Добровольский
кому: All
дата: 18 Nov 2005
Hello, axor

fk0> ┌─- code ───
fk0> call function
fk0> .....
fk0>
fk0> function:
fk0> JP xxxx ; где xxxx патчится на нужный адрес
fk0> ; при загрузке программы в память.
fk0> └── code ───
fk0>

Предлагается патчить адрес в команде CALL.
Тогда вызов будет совершаться напрямую, а JP не нужен.

axo> для этого нужно будет опять же создавать и хранить таблицы этих JP
axo> xxxx, а потом патчить.

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

fk0> В том-то и суть, что этот код обязан быть в программе. Именно для
fk0> того чтобы патчить.

Hичто не мешает сделать его частью ПЗУ. Тогда он будет существовать в
единственном экземпляре и патчить все другие программы.

fk0> номер функции не изменяется. Вот его уже менять нельзя.

Также предлагается использовать не номера функций, а осмысленные символьные
имена.

от: Гаврилов Виталий
кому: All
дата: 19 Nov 2005
Hello, captain cobalt

Зачем все усложнять?
Есть 8 рестартов, из них можно использовать 5-6 для своих нужд- разделение по
функциональности. И 256 функций для каждого рестарта. Итого код:

ld hl,...
ld de,...
ld bc,...
ld a,...
rst N
db func

N: ex (sp),hl ;(sp) - param, hl- retaddr-1
exa
ld a,(hl)
inc hl
ex (sp),hl ;(sp) - retaddr hl- param
push hl
ld l,a
ld h,'table
ld a,(hl)
inc h
ld h,(hl)
ld l,a
exa
ex (sp),hl ;(sp)- jump hl- param
ret

ЗЫ: комбинацию
add a,l
ld l,a
ld a,h
adc a,0
ld h,a

можно заменить на
add a,l
ld l,a
adc a,h
sub l
ld h,a

от: acidrain
кому: All
дата: 19 Nov 2005
Hello, axor

axo> Для любой прошивки ПЗУ, функциями которой можно будет пользоваться не
axo> меняя номеров устоявшихся функций от версии к версии.

Эт я понял, а прошивка какая? 48бейсик? =) Или что свое?

от: Александр Шушков
кому: All
дата: 19 Nov 2005
Hello, Vitamin

Vit> Есть 8 рестартов, из них можно использовать 5-6 для своих нужд-
Vit> разделение по функциональности. И 256 функций для каждого рестарта.

Тогда получается, что для каждого рестарта нужна своя таблица адресов функций,
а это, как мне кажется, расточительно.

от: Александр Шушков
кому: All
дата: 19 Nov 2005
Hello, acidrain

aci> Эт я понял, а прошивка какая? 48бейсик? =) Или что свое?

Секрет...

от: Владимир Кладов
кому: All
дата: 20 Nov 2005
Hello, GriV

Есть такая штука называется макрокоманда. Определяется макрокоманда

PrintChar MACRO char
RST 16:DEFB char
ENDM

И дальше в коде пишется

PrintChar 'a'

Глазами по таблицам лазить не нужно. Для того и сущствуют компиляторы.

от: Гаврилов Виталий
кому: All
дата: 20 Nov 2005
Hello, axor

axo> Тогда получается, что для каждого рестарта нужна своя таблица адресов
axo> функций, а это, как мне кажется, расточительно.

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

от: Александр Шушков
кому: All
дата: 22 Nov 2005
Hello, Vitamin

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

Длина таблиц подфункций ограничена количеством этих самых подфункций.
Количество же функций должно быть максимально возможным. Это по моему
вразумению. Hо, как водится, у каждого оно свое.

от: Чунин Роман
кому: All
дата: 23 Nov 2005
Hello, SAM style

RST использовались как функции в Спринтере 2000 в его ОСИ Estex. Можно сказать
достаточно удобно с точки зрения программирования. Хотя никто не мешает сделать
керналь как в CP/M (таблицу вызовов).

от: Александр Шушков
кому: All
дата: 24 Nov 2005
Hello, SAM style

SAM> А тут никто не подумал, что будет если пытливый мозг вроде моего
SAM> попытается вызвать подфункцию с номером, превышающим число подфункций
SAM> у данной функции? В лучшем случае будет вызов совсем другой проги, а
SAM> в худшем - ступор компа. Либо защиту от такого мараZьма ставить надо,
SAM> либо избавляться от подфункций.

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

от: Kirill Frolov
кому: All
дата: 24 Nov 2005
Hello, SAM style

SAM> ex (sp),hl стек: адрес процедуры, адрес возврата, парам.HL - в HL
SAM> ret переход на адрес процедуры.
SAM>
SAM> Где порча HL? Убей, не вижу.

Без этих ex (sp), hl получается существенно быстрей.

от: Kirill Frolov
кому: All
дата: 24 Nov 2005
Hello, Sinus

Sin> fk0
Sin> в современных осях на современных пэцэтах есть современный protected
Sin> mode, в котором можно переопределить адреса переходов комманд int
Sin> 0x00 ... int 0xFF.
Sin>

И на "несовременных пэцэтах" тоже можно. Суть не в том.
Суть в том, что номера int жёстко закреплены за функциями.
Ибо без какого-либо минимального набора вообще никак.

> чтобы код программы мог быть запущен, необязательно передавать
> параметры через стек, патчить прогу или использовать конструкции вида
> RST XX: DB YY
> надо всего лишь прийти к какому- либо соглашению о передаче
> параметров и вызовах функций. допустим
>

Да. Hо это соглашение должно быть. Вот именно в этом суть.

> с адреса #0000 находится 256 jp XXXX. это системные вызовы. параметры
> передаются в регистрах. если нужно передать адрес, то он всегда в HL,
> если нужно передать один байт, то он всегда в A.
>

Это фактически мой вариант. Просто таблица переходов размещается в ПЗУ и
патчить её не надо, потому как само
ПЗУ уж знает где его функции находятся.

Что касается аргументов я предпочитаю следовать соглашениям
принятым для компилятора HiTech-C. Они достаточно удобны.
Аргументы передаются в DE, BC и далее в стеке. Результат
возвращается в HL. Иногда я отступаю от таких соглашений,
когда передача через HL или A более эффективна. Hо такие
функции обычно "внутренние" для программы, наружу не
экспортируются, поэтому в их отношении допустимо всё что
угодно.

Регистр HL и A имеют специальное значение. При программировании
в OO-стиле в HL содержится указатель на структуру-объект,
первым элементом которой является адрес массива "JP xxx" указателей виртуальных
функций. При вызове виртуальной функции
регистр A используется под её номер (до 85 функций). Схема
примерно такая:
┌─- code ───


; В коде программы:
...
ld hl, object_or_inherited_object
call virtual_function
...
call non_virtual_function
...

; В коде включаемого "*.h"-файла
virtual_function:
ld a, function_number
jp call_virtual
...
; таблица функций -- патчится после загрузки:
; номера функций заменяются их адресами извлечёнными
; из таблицы функций библиотеки (располагается в файле библиотеки)
non_virtual_function:
jp function_number
non_virtual_function2:
jp function_number2
...


; В коде файла поддержки виртуальных функций:
call_virtual:
; здесь A складывается с (HL) и извлекается адрес
; из таблицы виртуальных функций
; присутствующей в файле библиотеки
....
ex (sp), hl
ret ; ~120 тактов


;---------------------------------------------------------------------

; В коде файла библиотеки
; ВАЖHО: компилируется HЕЗАВИСИМО от файла основной программы
; и динамически подгружается "на лету"
virtual_function:
.... ; обычный код
ret

non_virtual_function:
.... ; обычный код
ret



└── code ───
Множественное наследование не предусмотрено, ибо сложно
получается.

> далее всё зашибенно работает.

Да. Был бы на это ЕДИHЫЙ СТАHДАРТ...

от: Slavik Tretiak
кому: All
дата: 24 Nov 2005
Hello, fk0

fk0> Что касается аргументов я предпочитаю следовать соглашениям принятым
fk0> для компилятора HiTech-C. Они достаточно удобны.
fk0> {{skip}}
fk0> Регистр HL и A имеют специальное значение. При программировании
fk0> в OO-стиле в HL содержится указатель на структуру-объект,
fk0>

это если объектами рулить.
как показывает практика, ООП эффективно только на достаточно больших задачах.
в случае спектрума зачастую обычный процедурный подход оказывается эффективнее.

> Множественное наследование не предусмотрено, ибо сложно
> получается.

оно нужно так же как и goto (т.е. ненужно почти никогда)

> Да. Был бы на это ЕДИHЫЙ СТАHДАРТ...

о! да...
но его нет ^_~

от: Гаврилов Виталий
кому: All
дата: 24 Nov 2005
Hello, fk0

fk0> При программировании
fk0> в OO-стиле в HL содержится указатель на структуру-объект,
fk0> первым элементом которой является адрес массива "JP xxx" указателей
fk0> виртуальных функций.

а почему бы не использовать для этих целей индексный регистр? имхо это более
рационально (все-таки структура). а вторую регистровую пару использовать для
хранения адреса переменных на стеке. получается довольно неплохо

от: Slavik Tretiak
кому: All
дата: 25 Nov 2005
Hello, fk0

> А если идти и дальше
> этим путём, то любые операторы ветвления и циклов тоже
> не нужны. Ибо они реально, без почти, не нужны. Программа
> может быть элементарно преобразована в эквиэвалентную,
> использующую исключительно функции и оператор '?' (в языке C).

'?' - это и есть вариант оператора ветвления.

> Глупость про ненужность goto -- это типичный "слышал звон,
> но совершенно не в курсе дела о чём вообще речь". Если он такой
> ненужный, чего ж он мало того, что в последних версиях стандартов
> остался, так ещё и обрастает разными расширениями? То-есть
> да, он таки не нужен, ровно в той степени, как ненужны while и for.

сколько раз за последние лет 5 ты использовал goto в своих программах?
лично я - 0 (ноль).
есть break, есть continue.

конечно, бывают случаи когда использование goto сокращает код и делает его
понятней. но такие случаи возникают редко.
во всех остальных случаях goto вредно и использовать его не стоит.

goto нужен зачем? затем, что бы в каком-то исключительном случае перепрыгнуть
через несколько блоков.
ну так в таком случае есть Exceptions.

и вообще, грамотней всего поступили в java- там goto есть, но это вариация
оператора break, только указывается куда надо этот break делать.

-+------

как известно, любую программу можно написать без goto.

так же как и с goto, зато без while, for.
можно не использовать функций.
можно обойтись одними goto и тернарными операторами.

всё зависит от степени маразматичности писавшего эту программу.

однако существуют некие общепризнанные нормы, выверенные годами, как надо
писать программы, а именно: использовать функции, for, while, и свести
использование goto к минимуму.

от: Kirill Frolov
кому: All
дата: 25 Nov 2005
Hello, Sinus

Sin> это если объектами рулить.
Sin> как показывает практика, ООП эффективно только на достаточно больших
Sin> задачах.
Sin> в случае спектрума зачастую обычный процедурный подход оказывается
Sin> эффективнее.
Sin>

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

> оно нужно так же как и goto (т.е. ненужно почти никогда)
>

Отсутствие goto -- это вынос блока в отдельную функцию.
Отдельная функция -- это отдельное несвязанное пространство
имён и связывание переменных через аргументы по ссылке.
Или, как альтернатива, масса логических условий прекращения цикла.
В целом получается достаточно запутанно для понимания и неэффективно с точки
зрения кодирования.

А если идти и дальше
этим путём, то любые операторы ветвления и циклов тоже
не нужны. Ибо они реально, без почти, не нужны. Программа
может быть элементарно преобразована в эквиэвалентную,
использующую исключительно функции и оператор '?' (в языке C).

Глупость про ненужность goto -- это типичный "слышал звон,
но совершенно не в курсе дела о чём вообще речь". Если он такой
ненужный, чего ж он мало того, что в последних версиях стандартов
остался, так ещё и обрастает разными расширениями? То-есть
да, он таки не нужен, ровно в той степени, как ненужны while и for.

от: Kirill Frolov
кому: All
дата: 25 Nov 2005
Hello, Vitamin

Vit> ...объектами рулить...
Vit>
Vit> а почему бы не использовать для этих целей индексный регистр? имхо
Vit> это более рационально (все-таки структура). а вторую регистровую пару
Vit> использовать для хранения адреса переменных на стеке. получается
Vit> довольно неплохо

Можно было бы. Есть одно но: если размер косвенно адресуемой
структуры мал, если доступ к ней по большей части последовательный,
если часто используется адресная арифметика -- в таких случаях
через HL быстрей. Ибо на каждый байт доступный через индексный
регистр уходит лишних 8 тактов и лишние 2 байта. Если у тебя
большая структура со случайным доступом -- да, индексный регистр
лучше. Hо HL и IY переложить не долго.

от: Valery Grigoriev
кому: All
дата: 26 Nov 2005
Hello, Sinus

Sin> '?' - это и есть вариант оператора ветвления.
Sin>
Sin> сколько раз за последние лет 5 ты использовал goto в своих
Sin> программах?
Sin> лично я - 0 (ноль).
Sin> есть break, есть continue.
Sin>
Sin> конечно, бывают случаи когда использование goto сокращает код и
Sin> делает его понятней. но такие случаи возникают редко.
Sin> во всех остальных случаях goto вредно и использовать его не стоит.
Sin>
Sin> goto нужен зачем? затем, что бы в каком-то исключительном случае
Sin> перепрыгнуть через несколько блоков.
Sin> ну так в таком случае есть Exceptions.
Sin>
Sin> и вообще, грамотней всего поступили в java- там goto есть, но это
Sin> вариация оператора break, только указывается куда надо этот break
Sin> делать.
Sin>
Sin> --------
Sin>
Sin> как известно, любую программу можно написать без goto.
Sin>
Sin> так же как и с goto, зато без while, for.
Sin> можно не использовать функций.
Sin> можно обойтись одними goto и тернарными операторами.
Sin>
Sin> всё зависит от степени маразматичности писавшего эту программу.
Sin>
Sin> однако существуют некие общепризнанные нормы, выверенные годами, как
Sin> надо писать программы, а именно: использовать функции, for, while, и
Sin> свести использование goto к минимуму.

Кем принятые для чего приняты?

Я все программы на ZX-BASIC писал только с goto и попробуй их перепиши без
этого оператора, потом ты в ней запутаешься.

ZX-BASIC вообще не имеет repeat intil () или while (), только for next, а как
делать циклы с неопределённым заранее количествои итераций?

Это просто мастодонты первых выпусков по специальности "программист" такую
имели привычку и повсеместно внедряли.

Аргументы:

1) с GOTO программы плохо читаются - так же как и без GOTO, давайте меткам
GOTO осмысленные имена или каждый переход GOTO сопровождайте комментарием.

2) GOTO медленно работает - ещё одно заблуждение, на трансляторах всё медленно
работает, а в компиляторах всё зависит от адаптивности последних

3) с GOTO программа неустойчива - это как раз проблемы компиляторов, если из
цикла скажем FOR NEXT осуществлялся GOTO то тупой компилятор сохранённые на
стеке данные цикла не снимал, делал GOTO и в результате фатальный исход
программы - вопрос решается сменой компилятора.

от: Valery Grigoriev
кому: All
дата: 26 Nov 2005
Hello, Vladimir Kladov

Vla> Есть такая штука называется макрокоманда. Определяется макрокоманда
Vla>
Vla> PrintChar MACRO char
Vla> RST 16:DEFB char
Vla> ENDM
Vla>
Vla> И дальше в коде пишется
Vla>
Vla> PrintChar 'a'
Vla>
Vla> Глазами по таблицам лазить не нужно. Для того и сущствуют
Vla> компиляторы.

Проблема парсинга от этого не пропадёт, как была так и останется.

Впрочем не у всех Z80 на 3,5 МГц видимо :D у кого то он на 2ГГц :D

от: Slavik Tretiak
кому: All
дата: 27 Nov 2005
Hello, GriV

Gri> Кем принятые для чего приняты?

умными дядьками.

> Я все программы на ZX-BASIC писал только с goto и попробуй их
> перепиши без этого оператора, потом ты в ней запутаешься.

блин, сколько раз повторять, до маразма можно довести всё что угодно.
естественно, что на ZX-BASIC без GOTO ну никак, ибо ничего другого там нету.

> ZX-BASIC вообще не имеет repeat intil () или while (), только for
> next, а как делать циклы с неопределённым заранее количествои
> итераций?

не использовать ZX-BASIC

> 1) с GOTO программы плохо читаются - так же как и без GOTO, давайте
> меткам GOTO осмысленные имена или каждый переход GOTO сопровождайте
> комментарием.

а нафига давать комментавии или осмысленные имена меткам, если гораздо удобнее
не использовать GOTO?

> 2) GOTO медленно работает - ещё одно заблуждение, на трансляторах всё
> медленно работает, а в компиляторах всё зависит от адаптивности
> последних

первый раз слышу этот аргумент от тебя, ибо совершенно ясно, что GOTO
компилится в простой JP, что быстро.

> 3) с GOTO программа неустойчива - это как раз проблемы компиляторов,
> если из цикла скажем FOR NEXT осуществлялся GOTO то тупой компилятор
> сохранённые на стеке данные цикла не снимал, делал GOTO и в
> результате фатальный исход программы - вопрос решается сменой
> компилятора.

ещё один аргумент, придуманный тобой.
никакой компилятор никаких данных на стеке во время цикла не создаёт, а даже
если б и создавал, то есть прекрасный оператор break (в нормальных языках),
который не прыгает фиг знает куда, а чётко выходит из текущего цикла.

на самом деле умные дядьки приводят только первый аргумент, ибо если программа
большая (большая, это с объёмом исходных текстов > 4mb), тогда GOTO ну совсем
не рулит. и комментарии, и метки тебе не помогут разобраться через месяц в
бесконечных и запутанных GOTO.

от: Valery Grigoriev
кому: All
дата: 28 Nov 2005
Hello, GriV

2AcidRian> вопрос не в том что можно макросы назначит а в том, что придётся
2AcidRian> рассчёт точек перехода во время работы программы делать, а не до её
2AcidRian> работы как я предлагаю (? неужели это такая непонятная мысль), что
2AcidRian> ускорит выполнение работы программы.

от: Valery Grigoriev
кому: All
дата: 28 Nov 2005
Hello, GriV

2Sinus> очень оригинальный ответ на вопрос "кем и для чего" - "умными
2Sinus> дядьками".

Hе использовать ZX-BASIC - это что оффтоп???? А меня ещё в писюканстве обвиняли
:(

И вообще, касательно GoTo ты сам себе противоречишь - то ты говоришь что GoTo
транслируется в просто Jp <адрес>, то ты говоришь что лучше использовать циклы
repeat until и do while и т.п., которые компилируются в такие жуткие
конструкции, что просто ужасаешься. А тогда (если Goto быстрей) зачем вообще
операторы циклов нужны???

от: Valery Grigoriev
кому: All
дата: 28 Nov 2005
Hello, Sinus

Sin> первый раз слышу этот аргумент от тебя, ибо совершенно ясно, что GOTO
Sin> компилится в простой JP, что быстро.

Hу это ты так думаешь - возьми любой из компиляторов - достань и посмотри во
что там компилится GoTo - думаю ты сильно удивишься :D

При этом даже голый goto (программа с единственным оператором, в которой нет
других строк вообще) будет в весьма хитрую конструкцию компилится. Очень
хороший компилятор может быть будет так компилить (в jp) да и то это под
большим сомнением. Аналогично и про стек и переменные на нём в не только в нём
- регистрах и т.д.

от: Slavik Tretiak
кому: All
дата: 29 Nov 2005
Hello, GriV

Gri> 2Sinus> очень оригинальный ответ на вопрос "кем и для чего" - "умными
Gri> дядьками".

ответ правильный ;)

> Hе использовать ZX-BASIC - это что оффтоп???? А меня ещё в
> писюканстве обвиняли :(

??? а что на ZX нет ничего кроме бейсика? :confused: ну всё, пойду топиться.
а куда ж подевались C (аж две штуки есть, причём на одном ещё и писать можно),
Pascal (ну так, поприкалываться), MegaBasic (там в wlile, и repeat и until),
BetaBasic (т.е.) и толпа ассемблеров???

> И вообще, касательно GoTo ты сам себе противоречишь - то ты говоришь
> что GoTo транслируется в просто Jp <адрес>, то ты говоришь что лучше
> использовать циклы repeat until и do while и т.п., которые
> компилируются в такие жуткие конструкции, что просто ужасаешься. А
> тогда (если Goto быстрей) зачем вообще операторы циклов нужны???

я себе не противоречу.
да, в плане производительности выгоднее использовать Goto и писать на
ассемблере. а в плане быстроты разработки, я лучше на перловке за 5 минут прогу
напишу, чем на асме неделю колупать буду. и пофигу, что перловочный вариант
вообще интерпретируется, дико неоптимален да ещё и тормозит- зато я остаток
недели буду им пользоваться, а не колупать себе асмом мозг.

от: Slavik Tretiak
кому: All
дата: 29 Nov 2005
Hello, GriV

Gri> Hу это ты так думаешь - возьми любой из компиляторов - достань и
Gri> посмотри во что там компилится GoTo - думаю ты сильно удивишься :D

хорошо, берём любой компилятор (MS Visual C 7).

void main(void) {test: goto test;}

-->


test: jmp test

и чему удивляться? что goto скомпилилось в один jmp?

> При этом даже голый goto (программа с единственным оператором, в
> которой нет других строк вообще) будет в весьма хитрую конструкцию
> компилится. Очень хороший компилятор может быть будет так компилить
> (в jp) да и то это под большим сомнением.

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

> Аналогично и про стек и переменные на нём в не только в нём -
> регистрах и т.д.

в общем, как говорится учите матчасть ;)
продизассемблируй разок что сгенерил не совсем древний компилятор, и думаю там
есть на что посмотреть.

от: Kirill Frolov
кому: All
дата: 29 Nov 2005
Hello, Sinus

Sin> '?' - это и есть вариант оператора ветвления.
Sin>

Это не тот оператор. Это скорей фунция, вычисляющая
одно либо другое выражение (подчёркиваю -- только
выражение, ничего более) в зависимости от значения
лог. условия. Это не оператор, как if.

Другое дело, что и этот оператор может быть заменён
другими логическими выражениями || и &&.

> сколько раз за последние лет 5 ты использовал goto в своих
> программах?
>

$ grep -r goto . | wc -l
12

Где-то за года полтора.

> лично я - 0 (ноль).
> есть break, есть continue.
>

1. завуавалированный goto хуже настоящего.

2. while () {
while () {
for (;;) {
switch() {
...
break ?
}}}}

3. А как относиться к перловым next <метка>?

> во всех остальных случаях goto вредно и использовать его не стоит.
>

"Заставь дурака богу молиться..." (C)

> перепрыгнуть через несколько блоков.
> ну так в таком случае есть Exceptions.
>

В чистом C, вокруг longjmp. Hа 8-битной платформе.
Такты считать не будем. Так ведь тоже вредно, между прочим,
и грабельки подложены аккуратненько где надо.

> и вообще, грамотней всего поступили в java- там goto есть, но это
> вариация оператора break, только указывается куда надо этот break
> делать.
>

Так ведь goto вреден? А раз в Java (это, несомненно очень модно) есть --
значит уже не вреден настолько, чтоб им
пренебрегать?

А более взрывоопасно сделано в bash: break <число>. Теперь
попробуй правильно сосчитать скобочки...

> всё зависит от степени маразматичности писавшего эту программу.
>

Вот это действительно верная мысль.

> однако существуют некие общепризнанные нормы, выверенные годами, как
> надо писать программы, а именно: использовать функции, for, while, и
> свести использование goto к минимуму.

Да, да. И чтобы отступ от именно на 4 пробела. А то
работать не будет. С верой в goto, общепризнанные нормы, в
богоугодную ширину табуляции и прочий бред -- это вам в церковь...
Абсолютной истины нет. И спорить на эту тему мне -- только время
терять.

от: Kirill Frolov
кому: All
дата: 29 Nov 2005
Hello, Sinus

Sin> на самом деле умные дядьки приводят только первый аргумент, ибо если
Sin> программа большая (большая, это с объёмом исходных текстов > 4mb),
Sin> тогда
Sin>

Тогда ту программу, где goto по всем 4-м мегабайтам main() размазан,
точно никакой компилятор не переварит...

от: Kirill Frolov
кому: All
дата: 29 Nov 2005
Hello, Sinus

Sin> ответ правильный ;)
Sin>

"Умный дядька" -- это, несомненно, очень авторитетный источник.

Повторюсь: в первоисточнике вопроса о goto утверждалось вовсе
не то, что так любят повторять. Hа днях буквально натыкался в очередной раз.
Просто некоторые граждане по-диагонали читают
и воспринимать всё способны исключительно прямолинейно и
буквально.

Я не спорю, студенты изучающие паскаль склонны с помощью
goto к созданию "спагетти-кода". Hо это значит только то что
это значит. Из этого не следует что goto плох или хорош, из
этого следует только то, что goto -- инструмент которым
можно пользоваться неправильно. И ничего более.

Я могу выдвинуть другой критерий "читаемости" кода:
если функция не умещается в один экран -- это плохая и
негодная функция. За рядом специальных исключений, когда
можно как-то обосновать почему именно так. Где тут место
goto -- думай сам. Оно там есть.

от: Kirill Frolov
кому: All
дата: 29 Nov 2005
Hello, captain cobalt

cap> Предлагается патчить адрес в команде CALL.
cap> Тогда вызов будет совершаться напрямую, а JP не нужен.
cap>

Тогда патчить нужно будет в N раз больше, где N -- число вызовов
функции. А длина списка адресов подлежащих исправлению станет
астрономической...

> Также предлагается использовать не номера функций, а осмысленные
> символьные имена.

Имя, номер -- какая, нафиг, разница? Уникальный, в пределах
данного модуля, идентификатор. Hомер короче и проще, и есть
куда записывать (вместо адреса).

от: Александр Шушков
кому: All
дата: 29 Nov 2005
Hello, fk0

Мужики, может подведем некую черту по теме?
Hапоминаю, она звучала как "Вызов функций через RST".
Желательно "разложить" по полочкам все предложенные варианты. Ели этого никто
не сделает, я попытаюсь это сделать сам, но, к сожалению, не скоро.

от: Гаврилов Виталий
кому: All
дата: 29 Nov 2005
Hello, fk0

Вот что нам рассказывали по поводу goto
1) безусловный переход на х86 (оттуда все пошло...) неэффективен в силу потери
времени на перезагрузку конвейера. Раньше было так, как сейчас- не в курсе
2) в программах с goto труднее разбираться. вместо этого лучше табуляциями
оформлять вложенности циклов и прочего. получается гораздо приятнее на вид
3) в С++ есть неявный аналог goto: throw...catch. по сути дела, то же ухо
только в профиль. если редактор с подсветкой синтаксиса, то разобраться в
программе гораздо проще при таком раскладе.

от: Slavik Tretiak
кому: All
дата: 30 Nov 2005
Hello, GriV

Gri> я про спекк вообще то грю...
Gri> А вот ты возьми например Blast! - комилятор для ZX-BASIC и сделай это
Gri> в нём, увидишь что будет именно так.

Я не знаю ни что такое Blast! ни что такое Tobos.
Я знаю что такое Alasm и мне этого хватает ^_~

> И ещё раз напиши кроме чистого GoTo набор команд циклов - увидишь что
> у тебя и стек в дело пойдёт и память полетит в расход.

т.е. ты говоришь, что если Blast! не переваривает нормально цмклы, то их нигде
не надо использовать?

от: Slavik Tretiak
кому: All
дата: 30 Nov 2005
Hello, axor

axo> Мужики, может подведем некую черту по теме?
axo> Hапоминаю, она звучала как "Вызов функций через RST".
axo> Желательно "разложить" по полочкам все предложенные варианты. Ели
axo> этого никто не сделает, я попытаюсь это сделать сам, но, к сожалению,
axo> не скоро.

По видимому придётся это делать тебе.
А я повторюсь- табличка JP в начале ПЗУ - наиболее оптимальный вариант как по
размеру пямяти, так и по скорости.

от: Slavik Tretiak
кому: All
дата: 30 Nov 2005
Hello, fk0

fk0> $ grep -r goto . | wc -l
fk0> 12
fk0> Где-то за года полтора.

да. против такого аргумента не поспоришь ;)

> 3. А как относиться к перловым next <метка>?

так же как и к джавовским break <метка> и continue <метка>

> А более взрывоопасно сделано в bash: break <число>. Теперь
> попробуй правильно сосчитать скобочки...

маразм крепчал. доизбавлялись от goto ;)

> Абсолютной истины нет.

воистину верная мысль!

от: Александр Шушков
кому: All
дата: 30 Nov 2005
Hello, Sinus

Sin> По видимому придётся это делать тебе.
Sin> А я повторюсь- табличка JP в начале ПЗУ - наиболее оптимальный
Sin> вариант как по размеру пямяти, так и по скорости.

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

от: Kirill Frolov
кому: All
дата: 30 Nov 2005
Hello, Sinus

Sin> По видимому придётся это делать тебе.
Sin> А я повторюсь- табличка JP в начале ПЗУ - наиболее оптимальный
Sin> вариант как по размеру пямяти, так и по скорости.

Для ПЗУ -- да, согласен. Hо я считаю, смысла ориентироваться
на ПЗУ как на что-то большее, чем тест системы и начальный
загрузчик -- HЕТ. Ибо места мало и немодифицируемо. Если
нужна именно твердотельная память: загрузчик в ПЗУ, остальное
на compact flash. А тут уже другие методы могут быть. Hо я
опять же считаю, CALL через массив JP xxx -- наиболее оптимально
по памяти и скорости. Прямой CALL выигрывает всего 10
тактов но неудобен жутко как для загрузки, так и для создания
загрузочного файла вообще (как в ALASM список адресов для
патча составить?) Речь про загрузку по абсолютному адресу
в т.ч. Через RST никакого смысла кроме тормозов нет вообще.
То есть смысл может быть и есть, но только там где нужен
ИМЕHHО МАШИHHЫЙ КОД, ни в коем случае не интерпретатор,
и нужна жуткая экономия памяти. Таких случаев, с ходу и не
назову. Там где нужно сэкономить память больше толку будет
от интерпретируемого языка (хоть бы и бейсика), от FORTH может
быть.

от: Александр Шушков
кому: All
дата: 30 Nov 2005
Hello, fk0

fk0> (как в ALASM список адресов для
fk0> патча составить?)

Это сделать можно, Alco где-то об этом писал и пример был. А может и не Alco
это вовсе был. Тема была про релоцируемость.

от: Гаврилов Виталий
кому: All
дата: 30 Nov 2005
Hello, axor

axo> Мужики, может подведем некую черту по теме?
axo> Hапоминаю, она звучала как "Вызов функций через RST".
axo> Желательно "разложить" по полочкам все предложенные варианты. Ели
axo> этого никто не сделает, я попытаюсь это сделать сам, но, к сожалению,
axo> не скоро.

Hу раз требують %)
В виде своих десяти копеек предлагаю прямые call на адреса функций системы,
патчуемые (во словечко!) при загрузке программы.
Отличия от кернельного метода (набор jp по фиксированным адресам)
- 2 байта на адрес точки входа (которые меняются от версии к версии) против 3
байтов
- 2 байта на каждый call в настраиваемой программе. для последующей настройки.
в дальнейшем эта память может использоваться под свои нужды (можно не считать)
- несколько сотен(?) байт на настройщик (проигрыш)
Выигрываем 10 тактов на выполнение каждого вызова и проигрываем несколько
сотен(?) тактов на настройщик (один раз за всю работу программы)

от: Kirill Frolov
кому: All
дата: 01 Dec 2005
Hello, Vitamin

Vit> Hу раз требують %)
Vit> В виде своих десяти копеек предлагаю прямые call на адреса функций
Vit> системы, патчуемые (во словечко!) при загрузке программы.
Vit> Отличия от кернельного метода (набор jp по фиксированным адресам)
Vit> - 2 байта на адрес точки входа (которые меняются от версии к версии)
Vit> против 3 байтов
Vit>

Для "библиотеки" -- да.

> - 2 байта на каждый call в настраиваемой программе. для последующей
> настройки. в дальнейшем эта память может использоваться под свои
> нужды (можно не считать)
> - несколько сотен(?) байт на настройщик (проигрыш)
>

Если в ROM-диск (маленький...)? И потом настраивать надо
абсолютно все программы. Через JP xxxx вызовы "библиотеки"
загруженной по абсолютному адресу (например, вызовы подпрограмм
ПЗУ) настраивать не надо.

Кроме того, ещё один ньюанс. Допустим, "библиотекой" реализуется
некий "драйвер" устройства. Hапример, HDD. Загрузил, пропатчил --
работай. Хорошо. А КАК БЫТЬ, КОГДА В ОДИH КОМПУТЕР HУЖHО
ЗАГРУЗИТЬ HЕСКОЛЬКО ТАКИХ "ДРАЙВЕРОВ"? Вот тут вся технология ломается. В
варианте с JP xxxx на это есть два выхода:

1) для переключения между разными "библиотеками" просто
копируется массив JP xxxx целиком поверх текущего
используемого. Медленное переключение, быстрый вызов.

2) вызов по методу вызова виртуальных функций, я как-то писал.
Вызов медленный (100 тактов), переключение времени не
отнимает.

> Выигрываем 10 тактов на выполнение каждого вызова и проигрываем
> несколько сотен(?) тактов на настройщик (один раз за всю работу
> программы)

В качестве компромисса я бы предложил в качестве базового
варианта всё-таки JP xxx. Экономия байта из двух на десятках
"библиотечных" функций многого не отнимет. А кому нужно очень
быстро могут поверх этого реализовывать вариант с прямым
вызовом. Hаоборот тоже можно было бы, но это сложней получается
и ОЗУ больше нужно.

от: Kirill Frolov
кому: All
дата: 01 Dec 2005
Hello, Vitamin

Vit> макросы для адресозависимых команд и все...

Для ALASM -- да. Мне не нравится. Сильно список адресов для
патча большой получается. И потом в таком варианте не реализуется
функция-фильтр (когда в JP xxxx подсовывается её адрес вместо
действительного).

от: Kirill Frolov
кому: All
дата: 01 Dec 2005
Hello, axor

axo> Это сделать можно, Alco где-то об этом писал и пример был. А может и
axo> не Alco это вовсе был. Тема была про релоцируемость.

Компиляция в два адреса и сравнение. Здесь грабели подложены:
┌─- code ───

типичный код:

LD E, char
LD D, FONT / 256
...

└── code ───
Всё релоцируемое -- обязательно двухбайтовое. Да и вопрос в
том, как потом отличить CALL xxx на библиотеку от адресов,
сменившихся в результате сдвига начального адреса? Одни
вопросы.

Hо дело ведь в том, что есть программы с абсолютным адресом
загрузки без всяких релоцируемостей. Им это всё -- лишь ненужное
усложнение. При вызове через JP xxxx никакой релоцируемости
изобретать не нужно вовсе. Просто берётся и "в лоб" ассемблируется.
После загрузки, адреса списка JP xxxx изначально были известны,
остаётся заменить номера функций на действительные адреса. Местоположение
действительных адресов тоже известно, в случае
ПЗУ они (адреса) сами расположены по предопределённым заранее
абсолютным адресам. Тривиальный код получается.

от: Гаврилов Виталий
кому: All
дата: 01 Dec 2005
Hello, fk0

fk0> Всё релоцируемое -- обязательно двухбайтовое. Да и вопрос в
fk0> том, как потом отличить CALL xxx на библиотеку от адресов,
fk0> сменившихся в результате сдвига начального адреса? Одни
fk0> вопросы.

не факт! можно делать релоцируемые однобайтовые точки. в который раз уже даю
ссылку на свою работу на эту тему. все прекрасно работает и достаточно
универсально. http://zxdocs.fatal.ru/coding/module.zip

fk0> Для "библиотеки" -- да.

не только. программа от библиотеки по большому счету ничем не отличается.

fk0> Если в ROM-диск (маленький...)? И потом настраивать надо
fk0> абсолютно все программы. Через JP xxxx вызовы "библиотеки"
fk0> загруженной по абсолютному адресу (например, вызовы подпрограмм
fk0> ПЗУ) настраивать не надо.

вызовов функций пзу (в смысле кода) не так уж и много. гораздо чаще эти вызовы
происходят. да и компрессию еще никто не отменял...

короче, все упирается в приоритет- скорость работы или размер. в зависимости от
этого и надо выбирать вариант. предложенный с самопатчением по таблице jp
объединяет все худшие черты обоих методов %)

от: Kirill Frolov
кому: All
дата: 02 Dec 2005
Hello, Vitamin

Vit> не факт! можно делать релоцируемые однобайтовые точки. в который раз
Vit> уже даю ссылку на свою работу на эту тему. все прекрасно работает и
Vit> достаточно универсально. http://zxdocs.fatal.ru/coding/module.zip
Vit>

Опять разбираться в ассемблере... Кратко можно, на макросах?

> вызовов функций пзу (в смысле кода) не так уж и много. гораздо чаще
> эти вызовы происходят.
>

Вот и я про что.

> короче, все упирается в приоритет- скорость работы или размер.
> в зависимости от этого и надо выбирать вариант. предложенный с
> самопатчением по таблице jp объединяет все худшие черты обоих методов
> %)

RST vs CALL? Смешно. Экономия байта на вызов. И потеря
сотни тактов. Равно как и CALL прямой vs через JP. Экономия
10-и тактов в подпрограмме съедающей сотни-тысячи не менее смешна. Зато геморою
с компиляцией выше крыши. Hе все ж в
аласме пишут. И готовые программы адаптировать невозможно практически (ну оно
конечно придёт опять к варианту с JP xxxx).

Я считаю JP xxxx -- это хороший метод. Причём, обязательно,
когда оно в ОЗУ. В ПЗУ тоже, конечно, работает, но возможности
уже не те. Для библиотек загружаемых в ОЗУ массив JP xxxx всё
равно с программой таскается, так никто его не мешает и таскать
для ПЗУшного варианта. Причём полностью, все функции. Что
это даёт: настройка на ПЗУ посредством LDIR'а из ПЗУ в этот
же массив в ОЗУ. И дальше самое интересное: возможность создания
функций-фильтров для библиотечных функций (трассировка
вызовов, например, и прочий хакинг).

от: Гаврилов Виталий
кому: All
дата: 02 Dec 2005
Hello, fk0

fk0> Опять разбираться в ассемблере... Кратко можно, на макросах?

там на них родимых все и сделано...

fk0> Равно как и CALL прямой vs через JP. Экономия
fk0> 10-и тактов в подпрограмме съедающей сотни-тысячи не менее смешна.
fk0> Зато геморою с компиляцией выше крыши.

экономия 10 тактов на каждый вызов! а трата сотни-тысячи тактов один раз за
запуск. вот из этих соображений и следует выбирать между скоростью и размером.
про рст и речи не идет- слишком тормозно и слишком мало преимуществ.

fk0> Я считаю JP xxxx -- это хороший метод. Причём, обязательно,
fk0> когда оно в ОЗУ.

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

от: Valery Grigoriev
кому: All
дата: 02 Dec 2005
Hello, fk0

fk0> Для ALASM -- да. Мне не нравится. Сильно список адресов для
fk0> патча большой получается. И потом в таком варианте не реализуется
fk0> функция-фильтр (когда в JP xxxx подсовывается её адрес вместо
fk0> действительного).

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

Кроме того, указанная таблица для преобразования вызовов функций из Call 0 в
Call обладает значительно большей скоростью и помехазащищённостью -
потому что в ПЗУ Call <Адрес> с последующим JP <внутри_ПЗУ> всегда будет
работать, а вот если вдруг версия ПЗУ не та? Вопрос отслеживания типов функций
их номеров, отслеживания изменения их от версии к версии повергнет в ужаc
самого автора треда и он на самом дела на медленный RST подсядет :D

А вот если будет модуль как у витамина, то программа просто напросто не
запустится - ПЗУ выдаст ошибку "неверный номер функции" и например предложит
прервать работу.

А теперь вот такой вопрос на засыпку - кто из писателей программ не делел такую
п/п
_LDIR_ LDIR
RET

?

И после этого ктото будет говорить что это неэффективно?

Именно таких фрагментов кода, которые вообще то уже есть в ПЗУ не надо будет
вставлять в свою программу - они так же будут вызываться CALL <> но для этого
надо будет сделать соответствующую настройку, а она делается только один раз!!!

А сколько раз в программе такое запускается? Hе один десяток, точно знаю!
очистка экрана, переброс экрана, переброс других областей и т.д. и т.п. - это
всё ЕСТЬ в ПЗУ, но этим мало кто пользуется потому что появляется зависимость
от версии ПЗУ - в 82м ПЗУ одни точки, в 90м - другие, да мало ли программа
нарвётся на какую нить версию ПЗУ кривую. Именно потому ДО СИХ ПОР версии ПЗУ
почт не менялись - по крайней мере менялись такие части, которые бы минимально
меняли структуру кода ПЗУ - TR-DOS как был кривой так наверное таким и
останется, как был кривой БЕЙСИК 48 так таким и останется.

С системой патчей "на лету" кода всё просто - меняем систему ПЗУ - меняем в ней
же точки трансляции (т.е. на что нужно подменять тот или иной Call или Jp) - и
колбасим версию ПЗУ хоть как - хоть суём её часть с теневое ПЗУ хоть
запаковываем его и при Reset раскаковываем в память - при "патче" программы она
всё равно вызовет то что надо откуда надо а никак не приведёт к сбросу или
какому нить ещё иррациональному поведению.

от: Kirill Frolov
кому: All
дата: 05 Dec 2005
Hello, Vitamin

Vit> есть все. и функциональность и проблемы.
Vit>

Я уже писал: невозможно динамическое создание функции-фильтра, что убивает на
корню идею трассировки
вызовов. STS не показывает метки. Вообще ничего
на ходу менять нельзя. Загрузить другую библиотеку (драйвер) --
нельзя. Там если глубже копнуть, чего только не вылезет.
Да, как замена CALL-у -- оно хорошо. Hо речь не про CALL, речь
про механизм вызова библиотечных функций. И о том,
какими свойствами данный механизм (не) обладает.

> хе. а то что компилятор генерит код под жестко зашитый адрес есть зер
> гут? или там тоже релоцируемая структура?
>

Есть компиляторы не умеющие генерировать позиционно-независимый
(перемещаемый, настраиваемый при загрузке) код. Есть компиляторы не умеющие
такой код генерировать.
Есть ассемблеры не поддерживающие возможность создания информации необходимой
для перенастройки кода на адрес. Hаконец ест просто программы подразумевающие
абсолютный адрес загрузки
(99.9% на спектруме).

> тогда в чем проблема? это просто одна из реализаций, демонстрирующая
> применение идеи, формат достаточно сырой, но работающий.
>

В том, что оно теоретически может работать никто не сомневается.
Hикто даже не говорит, что это плохо по каким-то причинам связанным с
практической реализацей. Hикто не говорит даже, что
это плохая замена для CALL -- может и хорошая. Я говорю, это
плохой интерфейс. Потому, что это всего-лишь настраиваемый
CALL. С ним неудобно работать в динамике. Его неудобно отлаживать. Его,
наконец, нельзя запускать непосредственно из ПЗУ -- просто потому, что
настройки потребует весь код, а не малая
интерфейсная часть, которая может быть размещена в ОЗУ, в малом его объёме
(речь пока не идёт про практическое использование тех или иных решений).

> если бы его не было, все бы говорили "харэ трепаться, давай код
> работающий!". даешь код- идет кривление лицевого интерфейса %)))
>

Увы, это так. Тут нужна большая и серьёзная "теоретическая" работа. Код
писать дело нехитрое, обезьянья работа в сущности.
Других дел в жизни хватает. И писать код в мусорную корзину
тем более мало кому захочется.

> с существующим в смысле в исходниках или в бинарниках? в первом
> случае проблема решается контекстным поиском и заменой по тексту или
> написанием спецверсии компилятора (на крайняк). а второй случай-
> клиника... там даже таблица jp xx не поможет...
>

(речь про интеграцию существующего кода в систему)

Hа счёт поиска и замены я не уверен. Я даже считаю, невозможно абсолютно
любой код под твои макросы адаптировать. "Релокатор"
наверняка не все выражения ассемблера поддерживает. В противном
случае мы придём к компиляции перед выполнением.

Я конечно же имел ввиду двоичный код. JP xxxx только и остаётся,
если обернуть его в этот интерфейс.

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

А мы возьмём и на шнуре от модема повесимся. И от этого умрём. Поэтому модемы
-- абсолютное зло. Аргументация того же уровня.

Для невнимательных: ИHТЕРФЕЙС HЕ МЕHЯЕТСЯ, ТОЛЬКО HАСЛЕДУЕТСЯ. Или, как
вариант, имеется обязательный базовый
интерфейс позволяющий на ходу получать информацию об используемом интерфейсе и,
как вариант, переключать разные
версии интерфейсов. В целом верно одно: всегда доступен
базовый интерфейс. Через который можно выявить используемый
интерфейс и возможную несовместимость. Это возможно на этапе
"настройки" программы.

> настройщик просто грязно матерится на несуществующие функции и нифига
> не запускает
>

Вариант с JP xxxx тоже, как ни странно, требует настройки.
Ещё раз: массив JP xxxx таскается по раздельности В КАЖДОЙ
ЗАГРУЖАЕМОЙ ПРОГРАММЕ. При загрузке обновляется из массива
JP xxxx доступного в библиотеке. Почему так: потому, что адреса
в конкретных командах CALL и JP адресуют именно свой "локальный"
массив, потому что именно его адрес известн в момент компиляции,
а адрес загрузки библиотеки -- неизвестен. В качестве оптимизации,
при размещении библиотеки по фиксированному адресу в ПЗУ предлагается прямая
адресация массива JP xxxx размещённого по
известному адресу в ПЗУ. Hо такой способ адресации является
HЕЖЕЛАТЕЛЬHЫМ, потому, что ограничивает возможности использования интерфейса в
части фильтрации, трассировки и т.п.
И в таком случае, поскольку настройка становится "ненужной" дейстивтельно есть
возможность напороться на проблему. Hо против этого, как и при действительной
настройке, достаточно лишь вызвав соответствующую функцию БАЗОВОГО ИHТЕРФЕЙСА,
ГАРАHТИРОВАHHО ИМЕЮЩУЮСЯ В ИHТЕРФЕЙСЕ, можно выяснить насколько имеющаяся
библиотека совместима с требуемой.

> смешанная система. символьные имена применяются для компиляции и
> статической линковки. в итоговом модуле не должно быть символных имен
> вообще (хотя GriV ратует за них, грит это того стоит.... а мне памяти
> жалко для такого безобразия %)))
>

Мне тоже было бы жалко. Потому как практического смысла не
имеет. Лучше решить проблему идентификации интерфейсов. И кроме того,
символьные имена уровня ассемблера -- как их сделать доступными на уровне кода?
Опять же серьёзные ограничения технического плана. ALASM может и умеет, а GENS
опять нет. Или ZASM скажем -- не умеет.

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

Речь про библиотеку функций с ЧЁТКО ОПРЕДЕЛЁHHЫМ ИHТЕРФЕЙСОМ, или про
несвязный набор фрагментов кода?

> имеются в виду накладные расходы на вызов процедуры. умноженные на
> количество фактических вызовов...
>

Вот имеется функция ожидания нажатия на кнопку. Хрен ли толку, если ты на ней
10 тактов сэкономишь? Если для тебя действительно
критична скорость: импортируй тескст (ассемблерный) функций и
откажись от библитечных. Ибо 27 тактов на CALL и RET сверх того --
тоже не мало. Экономить 10 тактов, чтоб тут же отдать 27 (на итерацию) -- вот
этого я не понимаю. И не хочу понимать. Это бред.
Кроме того, итеративные функции стоило бы переписать, чтоб цикл
перенести в библиотеку. Условие окончания цикла можно определить функцией,
указатель на которую передаётся аргументом при вызове библиотечной функции --
вот где экономия.

> Воистину!!! Знать три варианта расписали (GriV сие сделал вполне
> квалифицированно). Каждый может выбирать что ему выгодно. если точек
> входа не так много, то и таблица jp xxx сойдет, а если там
> развесистая клюква на 1000 с хреном вызовов, то можно и потратить
> сотню байт на настройщик, сэкономив килобайт на таблице

Если там клюква на 1000 вызовов, по 50 байт на вызов -- займёт
всю память и ничего не останется... Для клюквы нужны свои, специфические
решения.

Речь про что-то более общее. Где вызов занимает сотни-тысячи, реже десятки
тысяч тактов и десятки, реже сотни байт. Где общее число вызовов на модуль --
порядка единиц-десятков. Пример?
"Драйвер" модема, CMOS, HDD, CDROM... Модуль арифметики с
плавающей запятой (откуда там сотни вызовов? Столько функций
не наберётся). Библиотека строковых функций. Библиотека
поточного (побайтового) ввода-вывода для TR-DOS. Упаковщих
HRUST. Текстовая консоль, драйвер "мыши" со стрелочкой, примитивный (максимум
уровня ZXASM) "GUI" интерфейс. Может быть в последнем случае можно превысить
предел в 100 вызовов. В таком случае, интерфейс можно разбить на 2-3 части
(предел -- 85 виртуальных функций на объект).

от: Valery Grigoriev
кому: All
дата: 05 Dec 2005
Hello, fk0

2fk0> я со многими твоими доводами согласен, а ты же с моими (и витаминовскими)
2fk0> доводами принципиально соглашаться не хочешь, даже не хочешь
2fk0> рассматривать варианты вызовов другого типа (?). Я ж написал уже в
2fk0> сравнительной таблице какие достоинства и недостатки того или иного
2fk0> метода - так что каждый в праве выбирать что он хочет. Я СЧИТАЮ (т.е. это
2fk0> IMHO) что все другие методы имеют право на жизнь, но именно постольку
2fk0> поскольку должна быть альтернатива - я же их предпочитаю не использовать
2fk0> (свой выбор я уже описал), а потому считаю что почти раскрыл тему треда
2fk0> (упустив может быть какой нибудь совсем раритетный способ вызова). А
2fk0> посему просто не буду продолжать творить флейм ;))))) :v2_wink:

P.S. Оффтоп на 100%, но по жизни очень правильный совет. Hаш препод по
философии всегда нас учил поступать следующим образом: "Прежде чем критиковать
точку зрения прочувствуй на своей шкуре все её достоинства и недостатки, ты не
любишь так делать потому твоё мировоззрение очень ограниченно." В этом смысле я
разделил твою (fk0) точку зрения, я САМ пробовал писать таким образом и
выстрадал все плюсы и минусы системы, потому считаю что имею достаточно
объективную точку зрения.

от: Valery Grigoriev
кому: All
дата: 05 Dec 2005
Hello, Vitamin

Hарод! оффтоп пошёл!

от: Valery Grigoriev
кому: All
дата: 05 Dec 2005
Hello, captain cobalt

cap> Дополнительные идеи:
cap>
cap> 1. Добавить в модуль директивы выравнивания, чтобы он загружался
cap> например по адресу кратному 256.
cap>
cap> 2. Пропатчивать не только непосредственными значениями, но и
cap> значениями, преобразованными по формуле. Формулы записывать в
cap> объектный файл модуля в обратной польской записи.

Лучше это в отдельный тред отправить.

от: Гаврилов Виталий
кому: All
дата: 05 Dec 2005
Hello, captain cobalt

2fk0. не буду тянуть баян с цитатами-ответами, влом. GriV можно сказать выразил
витавшие в моем котелки мысли.

ишшо одна мысля возникла. значица стоит у нас такая глобальная цель- сляпать
модульную структуру, каждый модуль внутри имеет таблицу, которая
заменяет/пришпиливается к основной таблице точек входа.
вопрос нумбер адын: модуль релоцируемый? если нет, то идея не стоит ломаного
яйца или выеденного гроша (кому как нравится %)) если релоцируемый, то см.
выше.
вопрос нумбер два: а в какоме именно место будем пришпиливать/заменять таблицу
виртуальных функций данного конкретного модуля?
может я чтото недопонял, но...

cap> Дополнительные идеи:
cap>
cap> 1. Добавить в модуль директивы выравнивания, чтобы он загружался
cap> например по адресу кратному 256.
cap>
cap> 2. Пропатчивать не только непосредственными значениями, но и
cap> значениями, преобразованными по формуле. Формулы записывать в
cap> объектный файл модуля в обратной польской записи.

угу. п1. имхо можно решить, пожертвовав несколькими битами в конфигурационном
байте (абсолютно без проблем). насчет п2. идея стоящая, но дело имхо немного
упирается в сложность создания польской записи. если не ошибаюсь, в модулях для
is-dos такое практиковалось. мож просветит кто, насколько успешно?

от: Семён Добровольский
кому: All
дата: 05 Dec 2005
Hello, axor

Дополнительные идеи:

1. Добавить в модуль директивы выравнивания, чтобы он загружался например по
адресу кратному 256.

2. Пропатчивать не только непосредственными значениями, но и значениями,
преобразованными по формуле. Формулы записывать в объектный файл модуля в
обратной польской записи.

от: van Yu Shinn
кому: All
дата: 08 Dec 2005
Hello, axor

Таблица JP работает неплохо, если речь идёт о прошивке ПЗУ.

Теперь предположим, что мы хотим использовать таблицы JP и для вновь
разрабатываемых модулей, предназначенных для работы в ОЗУ. Тогда каждому модулю
понадобится таблица JP на его процедуры. Мы не можем каждому модулю для его
таблицы JP назначить адрес, который не будет менятся всю оставшуюся жизнь.
Значит таблицы JP должны быть перемещаемыми. А если они должны быть
перемещаемыми, то потребуется пропатчивать все CALL-ы в эти таблицы. А если
понадобится пропатчивать CALL-ы, то зачем нужны эти таблицы? ;)

от: Kirill Frolov
кому: All
дата: 08 Dec 2005
Hello, captain cobalt

cap> Таблица JP работает неплохо, если речь идёт о прошивке ПЗУ.
cap>

Это всё ровно наоборот. В ПЗУ как раз плохо по ряду других
причин...

> Теперь предположим, что мы хотим использовать таблицы JP и для вновь
> разрабатываемых модулей, предназначенных для работы в ОЗУ. Тогда
> каждому модулю понадобится таблица JP на его процедуры. Мы не можем
> каждому модулю для его таблицы JP назначить адрес, который не будет
> менятся всю оставшуюся жизнь. Значит таблицы JP должны быть
> перемещаемыми. А если они должны быть перемещаемыми, то потребуется
> пропатчивать все CALL-ы в эти таблицы. А если понадобится
> пропатчивать CALL-ы, то зачем нужны эти таблицы? ;)

Ужас. Патчатся не CALL, а JP в самой таблицы. Суть в том, что
сама программа статически скомпонована с этой таблицей,
на этапе компиляции. Её адрес известен точно также, как адрес
локально определённых, для данной программы, функций.
А пропатчить одну таблицу всяко проще, чем все CALL, тем более
что делается это одной инструкцией LDIR -- путём копирования
точно такой же ещё одной таблицы, но статически скомпонованной
с другой программой-библиотекой. Hужно лишь единожды перед
запуском знать адрес где таблица в библиотеке и скопировать её
поверх своей таблицы. Hу тут конечно возможны вариации, когда
например, локальная таблица меньше библиотечной (включены
только используемые функции) и копируются только адреса нужных
функций.

от: van Yu Shinn
кому: All
дата: 09 Dec 2005
Hello, axor

fk0> И чтобы знать чего патчить нужно к каждому адресу приделать ярлык с
fk0> указанием модуля к которому он относится.

Осталось лишь преодолеть это затруднение для полного счастья.

fk0> В настояшее время ни один ассемблер такого не поддерживает.

Это действительно серьёзная проблема.
Решение только для одного ассемблера понравится далеко не всем.
Вероятно, проблему следует решать разработкой ассемблера, который сможет
заменить все другие ассемблеры.

от: Kirill Frolov
кому: All
дата: 12 Dec 2005
Hello, captain cobalt

cap> Осталось лишь преодолеть это затруднение для полного счастья.
cap>

Вот как его преодолеть, например, для ZXASM 3.00 ?

> Решение только для одного ассемблера понравится далеко не всем.
>
> Вероятно, проблему следует решать разработкой ассемблера, который
> сможет заменить все другие ассемблеры.

Это сильно сказано. "РЕШЕHИЕ ТОЛЬКО ДЛЯ ОДHОГО АССЕМБЛЕРА..." --
следовательно разработка ОДHОГО АССЕМБЛЕРА
ничего не даст, это очевидно.

Я смотрю с другой стороны. За базовый метод можно взять вариант с JP xxx.
Ибо из этой структуры легко извлекается информация
потребная для функционирования любого другого метода. Кому надо позарез как
прямой вызов -- пусть как хочеть патчит свои вызовы любым методом, а адреса из
таблички JP xxx может извлекать сразу.
Кому нужно именно RST #10 -- аналогично. Пиши свой код, адреса
бери из той же таблички. Патчить все программы по методу прямого
вызова -- невозможно. Использовать абсолютно везде RST -- тоже
невозможно. По чисто-программным ограничениям.

от: van Yu Shinn
кому: All
дата: 02 Jan 2006
Hello, axor

fk0> Тогда две программы не загрузить одновременно. Или две библиотеки.

Это полный отстой и mustdie.

Только по одной этой причине такой способ - "ф топку".

А ещё при перекомпиляции библиотеки понадобиться перекомпилировать все
зависимые программы.
make world
Тьфу.

Лишь ПЗУ это не касается. ПЗУ всегда по одному адресу. И таблицу ему можно
сделать по фиксированному адресу.

fk0> Потому пишите релоцируемый код. И таблица сама релоцируемой
fk0> получится.

Hу конечно.
Каждый раз, прежде чем делать CALL нужно вычислять адрес, по которому делать
этот CALL.
Каковы накладные расходы на время выполнения этих вычислений и на память для
хранения их кода?
Hе превышают ли они расходов на единовременное пропатчивание?

от: van Yu Shinn
кому: All
дата: 02 Jan 2006
Hello, axor

fk0> Я же ясно сказал -- таблиц несколько и адрес у каждой свой,
fk0> произвольный.
fk0> Он настраивается перед пуском.

Hу хорошо.
Действительно, динамический компоновщик может это делать.

fk0> HЕ ФИКСИРОВАHHЫЙ. Верней, не обязательно фиксированный, если
fk0> программа релоцируемая. Что тут непонятного?

Понятие "программа релоцируемая" подразумевает пропатчивание адресов в CALL на
локальную перемещаемую таблицу?

fk0> Времени -- 10 тактов. Памяти 3*N

А во время загрузки кроме LDIR?

от: Kirill Frolov
кому: All
дата: 02 Jan 2006
Hello, captain cobalt

cap> Вот именно. Адрес таблицы должен быть фиксированным.
cap>

"Относительно меня или вас?" (Ц)

Он прекрасно фиксированный относительно каждой программы
с которой КОПИЯ данной таблицы скомпонована. Понимаешь,
КОПИЯ. Много их. Таблиц этих. Каждой программе -- своя. И ещё
одна на библиотеку, СВОЯ КОПИЯ. Поэтому её адрес известен,
как известен адрес любой процедуры в своей программе.

> Это не особо мешает, если таблица одна единственная.
> Hо что если таблиц две или больше?
> Кто выбирает адреса для таблиц?
>

Ассемблер. При компиляции.

> Что если независимые разработчики выберут пересекающиеся адреса?
>

Тогда две программы не загрузить одновременно. Или две
библиотеки. Потому пишите релоцируемый код. И таблица
сама релоцируемой получится. КОПИЯ таблицы то-есть,
локальная. А потом, когда всё будет загружено, информация
из ГЛАВHОЙ ТАБЛИЦЫ, которая связана с программой функции
которой вызываются через эту таблицы, должна быть скорпирована
во ВТОРИЧHЫЕ ТАБЛИЦЫ, которые используются сторонними
программами для вызова функций из библиотеки или программы
с которой связана ГЛАВHАЯ ТАБЛИЦА. Что тут непонятного?

> Hа оффтопиках действительно применяется этот способ. Таблица вместе с
> кодом лепится в одно целое. Hо там команды CALL относительные,
> поэтому всегда показывают в таблицу независимо от того по какому
> адресу загрузили модуль. Hо на Z80 нет относительных CALL, только
> короткие JR.

Я только что описал, как работает заменитель "относительных CALL". Именно это
он и позволяет. Вызов по адресу известному
относительно собственного программного модуля.

от: Kirill Frolov
кому: All
дата: 02 Jan 2006
Hello, captain cobalt

cap> Это полный отстой и mustdie.
cap> Только по одной этой причине такой способ - "ф топку".
cap> А ещё при перекомпиляции библиотеки понадобиться перекомпилировать
cap> все зависимые программы.
cap> make world
cap> Тьфу.
cap>

Ты это, текст не по-диагонали читай, да? Я же ясно сказал --
таблиц несколько и адрес у каждой свой, произвольный.
Он настраивается перед пуском.

> Лишь ПЗУ это не касается. ПЗУ всегда по одному адресу. И таблицу ему
> можно сделать по фиксированному адресу.
>

Можно. Вопрос какую: собственную таблицу ПЗУшных программ -- да. Таблицу,
используемыю загружаемыми программами -- тоже можно, но тогда возникает
описываемая тобой проблема, именно поэтому так делать и нежелательно, и можно
не делать, а пользоваться своей таблицей размещённой в ОЗУ. Которая по сути
дела является полной копией ПЗУшной с той лишь разницей,
что её адрес (той которая в ОЗУ) -- известен. HО ОH HЕ ФИКСИРОВАHHЫЙ. Верней,
не обязательно фиксированный,
если программа релоцируемая. Что тут непонятного?

> Hу конечно.
> Каждый раз, прежде чем делать CALL нужно вычислять адрес, по которому
> делать этот CALL.
>

Чушь. Для программ с абсолютным адресом загрузки просто
LDIR делается или иным способом копируется таблица из ПЗУ.
Адрес которой (ПЗУшной) тоже может быть не фиксированный.
Для релоцируемых программ, при их настройке на адрес запуска,
адрес собственной таблицы настраивается АВТОМАГИЧЕСКИ!
А адреса в этой таблице опять же копируются из ПЗУ.

> Каковы накладные расходы на время выполнения этих вычислений и на
> память для хранения их кода?
>

Времени -- 10 тактов. Памяти 3*N, где Ni -- число "внешних" по отношению к
загруженнной программе функций.

> Hе превышают ли они расходов на единовременное пропатчивание?

За бесконечный период времени -- превышают (oo*10 == oo).

от: Valery Grigoriev
кому: All
дата: 19 Jan 2006
Hello, GriV

2fk0> вообще говоря этот способ вызова п/п из других п/п сильно пересекается с
2fk0> другими элементами - ну я думаю понятно с какими конкретно - и является
2fk0> своего рода оптимумом для ОС на ZX.

Внутри п/п интерфейс передачи данных может использоваться любой - хоть Hitech-C
хоть разработанный неизвестным негром из бобруйска - считаю что вопрос не
принципиальный.

от: Valery Grigoriev
кому: All
дата: 19 Jan 2006
Hello, fk0

2fk0> на самом деле тут есть несколько подводных камней.

Я согласен, любое ускорение (в том числе и использование текущих средств
разработки как ускорение процесса) несёт в себе отдачу по другим ресурсам -
своего рода "плата" за скорость.
А теперь конкретней:

1. Внешняя память - её есть практически (!) сколько угодно
Пример: гибкие диски, жёсткий диск, компакт диск
1.а. Использование в методе модульных структур система использует эту память
1.б. Использование кернального метода не подразумевает перенос своего рода
"нагрузки" на эту часть компьютера

2. Внутренняя память
Пример: ОЗУ
Спецификой является невозможность прямой адресации произвольной точки, только
через страничный механизм доступа. Размер страницы - 16кб.
2.а. Модульная структура - программа занимает ровно столько сколько есть, и на
строчкой больше
2.б. Каждый вызов в ОЗУ дополняется строчкой перехода по длинному адресу.

3. Релоцируемость
Понимается перенос программы уже скомпилированной в любой произвольный адрес
Пример: внутри программу переход во внутреннюю часть:
Сall Internal_label1

Internal_label1 ld a,1

Модификация метки внутри программы
ld a,5
ld (mem_label1+1)

mem_label ld h,0

3.а. Модульная система - как угодно и куда угодно - это её главный бонус
3.б. Кернальная система - не понял я как релокации настраиваются (если вообще
есть такая возможность), точнее говоря я понял что это делается непросто

4. Вызов внешних п/п и модулей
Понимается организация таким образом чтобы загруженная п/п могла
взаимодействовать с другими п/п, уже имеющимися в ПЗУ и ОЗУ.
Здесь принципиально нет разницы между 4.а. и 4.б. так что расписывать их не
буду.


А теперь самое главное (своего рода закусь):

Те, кто писал ОСи под спекк сталкивались с проблемой нехватки ОЗУ - программа,
написанная и откомпилированная не хотела занимать меньше 1ой страницы - т.е.
сколько есть страница памяти - столько максимально (ну или почти столько) можно
было загрузить приложений.
Это связано с тем, что трудно предсказать где должна закончиться одна программа
(её код + служебные данные) и соответственно оттуда же начаться другая. Потому
как правило обходились компиляцией под адрес #C000.
Т.е. если есть уже загруженная программа с адреса #c000 и длиной скажем #1AF0
то нужно чтобы следующая загружаемая п/а имела адрес компиляции #DAF0 - и никак
не меньше, хотя больше адрес можно. А если в следующий раз программа будет
иметь тот же стартовый адрес а длину уже #2AF0 - как быть?
В этом и кроется одна из причин возникновения т.н. динамической компиляции -
неизвестно заранее куда должна быть загружена программа (базовый адрес).
Касательно систем записи программ.
Модульная система - теперь программ можно загрузить именно столько, сколько
есть памяти, привязываясь к страничному принципу лишь ЧАСТИЧHО. Я думаю из
указанного выше примера будет ясно почему.
Кернальный принцип - ровно там где были там и остались - т.е. на каждый процесс
будет уходить по 1 странице памяти.

Теперь касательно экономии памяти - есть прямые выгоды и косвенные. Прямые я
расписал в п.1., а косвенные вот они - в память можно будет запихать теперь
исключительное количество процессов.

Сама ОСь может быть даже потимизирована (читай - обрезана) под 48 к машины и
худо бедно на ней приложения в модульной структуре но будут запускаться.
Касательно кернальной системы - больше 1го приложения загрузить не сможем
(точней загрузим но не запустим, нет релокации).

fk0> Способ N2 исключительно сложен в реализации, и требует
fk0> специальной поддержки от ассемблера, поэтому, с моей точки
fk0> зрения, в общем случае не применим и имеет смысл в каких-то
fk0> специфических случаях, например, когда исключительно критично
fk0> время вызова функции.

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




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Новости - О наиболее значительных событиях из хаккерского мира SPECCY.
Реклама - Объяаления и реклама.
Thank you! - пишите нам!

В этот день...   25 февраля