Заполнение Y реально может быть не просто ненужным, но и времязатратным процессом.
вроде как [DST] читать не нужно только для load, что определяется по опкоду (лучше нулевому) и без TI
и второй раз в операциях типа [DST=SRC],[SRC=DST] — ну так там уже прочитано в первый раз
Я сильно над этим не задумывался еще, но т.к. есть 16 однооперандных инструкций (и они никак не обязаны быть теми же что и 16 двухоперандных), то возможно там будет и dec/inc-1/2/3/4 и возможно что-то еще.
лично я между любым кол-вом дополнительных инкрементов и обменом всегда выберу обмен
уже только ради одного обмена со стековым указателем, даже если больше не понадобится никак 8)
Если TI=1, то общая схема инструкции следующая:
1. SRC загружается в X
2. DST загружается в Y
это можно делать одновременно, и TI не нужен
если не понадобится Y, так не понадобится
Так и есть — это пожалуй единственная «бесполезная» комбинация аргументов которая осталась и действительно сделать её каким то особым случаем выглядит привлекательно.
Но лично мне не нравится, что ломается общая схема работы Simpleton-а.
почему ломается? считай просто, что схема общая у тебя такая:
1) может быть, записать PC в стек
2) DST ?= SRC
и нет проблем :D
Ну зависит от задачи. Например, если первый элемент нам подходит — переходим к его хвосту, а если нет — пропускаем до следующего элемента.
это только если по первому слову ясно (кстати, у тебя какой порядок слов-байтов? little/big endian?)
но если нужно элемент проверить целиком и условно пропустить следующий, то inc1+inc2 ничем не лучше add3
Главное откуда оно возникло — это минимизация и ускорение реализации CALL,
Кстати, лучшим вариантом именно минимизации с ускорением может быть отдать под однокомандный call в принципе небессмысленный, но на практике вряд ли нужный случай «op [PC],src» — то есть dst=PC всегда прямая адресация, а ID=1 в этом случае будет означать «перед выполнением сохранить PC в стек». Подумай, так ли тебе нужно писать что-то в следующий опкод.
помимо этого может быть применён для, допустим, быстрой адресации переменных близких к вершине стека
переменные нормально надо располагать, и не понадобятся лишние инкременты :D
(а еще лучше адресацию [R+imm], но она не лезет в такую схему)
может быть полезен при итерировании по массивам с таким размером ячейки и тому подобное.
ИТЕРИРОВАНИЕ подразумевает использование каждого элемента, то есть последовательный доступ ко всем его частям, то есть одинарный инкремент :P
также не совсем понимаю смысл необходимости флага TI — почему бы всегда не производить копирование в X,Y?
Не стоит того чтобы из-за этого ломать систему команд.
почему «ломать»? обобщить!
Даже в ЯВУ swap как правило записывается проброской данного через tmp,
что крайне бесит, так же как отсутствие циклических сдвигов
ну пробросим через регистр, несущественно
это инкремент на 2 несущественно, а нерациональный расход регистра — существенно
Это довольно эзотерично чтобы мне захотелось таким пользоваться в жизни. ;D
это в том числе вызов по таблице, рядовой случай
Как и inc1 это экономия на imm в ряде случаев когда это часто нужно.
inc2 лично мне приятно, экономит код, данные и скорость в массе случаев
в какой массе? перечисли несколько хотя бы разумных случаев? вот как раз на 8-битках иногда могло еще быть полезно, потому что основные типы данных разных размеров, но здесь-то одинаковые они! притом польза, даже если где-то она и будет, не «в два раза», а всего-то навсего в (N+1)/N от всех циклов доступа в итерации
Когда load [PC] (на одном операнде X рассмотрим) отрабатывает он сперва в X грузит PC, а уже потом из-за бита indirect замещает X на [X]. Это совсем не то же самое что сперва загрузить в X [PC++], а потом заменить X на [X].
что совсем не повод так бездарно тратить номер регистра
load imm = load [pc++]
load [imm] = load [[pc++]]
второй случай можно закодировать через номер регистра флагов
(потому что, ну кому и зачем мб нужен косвенный доступ через флаги))
Swap регистров обычно нужен когда они существенно неортогональны и ради аккумулятора надо перестановки делать. Тут такой острой необходимости нет.
например, обмен координат в однонаправленном алгоритме
сортировка пузырьком с условным обменом
(ты же хочешь операции с памятью?)
Регистров на самом деле хватает чтобы сделать memmove/memcopy полностью на регистрах, а это я считаю эталонным кодом для проверки на нехватку регистров.
а я считаю это разновидностью подхода «и так сойдёт» :P
Такую оптимизацию можно проделать в существующей системе команд и без swap:
; вызов процедуры
R3 =+2 PC; адрес возврата (inc2) (команда без immediate)
PC = proc_addr; вызов процедуры
; возврат из процедуры
PC = R3
нет! это совершенно не то же самое! например:
load R3,adr1
(вычисления)
(условный переход)
load R3,adr2
(вычисления)
swap PC,R3
Чем же он уродлив?
тем, что повторяет функцию универсального сложения с любой константой
собс-но, даже инкремент на 1 — пережиток неортогональных восьмибитных процов
(но он хотя бы нужен бывает часто, и его наличие мб оправданным)
у обмена же гораздо шире функционал
В эти годы уже просто для поддержки CP/M могли целый процессор Z80 засадить в какой нибудь Commodore XXL (не помню точно модели) — какая уж тут гонка за примитивизмом?
засадить могли, а в нормальную производительность z80 не шмогли
и это тормозное в cp/m режиме угрёбище справедливо было непопулярно
тем более, что и стоило вдвое дороже c64 того же года
Я уже говорил — команда типа SWAP BYTES внутри регистра (опять таки с логикой Y = swap bytes of X) делает такой сценарий маловероятным. Ну и вообще соединять комп с устройством которое плюётся данными быстрее чем или даже соизмеримо чем скорость с которой процессор отрабатывает memfill — это сомнительно и вряд ли такое вообще есть/было на практике.
на практике поддерживали 8 и 16 бит девайсы одновременно очень недешёвые песюки
причём в них не было полного отказа от 8 бит
Immediate сам может быть тоже indirect и таким образом операции можно проводить непосредственно над памятью не загрязняя регистры. С чтением через PC так уже не получится
это почему еще? сам же пишешь:
значение в X замещается значением ячейки памяти по адресу X
то есть load [PC],[PC] это X=[PC++]; Y=[PC++]; X=[X]; [Y]=X
что эквивалентно load [imm1],[imm2]
Т.к. регистров действительно немного,
вооот, в том числе еще поэтому нужен swap, а у тебя и регистров меньше, и свопа нет
а в самом обобщённом случае Link Register таки нужно пихать в стек,
только частных случаев очень много и эффект заметный от ускорения
плюс содержимое линк-регистра может служить указателем на данные процедуры
(после обработки данных с инкрементом получается корректный адрес возврата)
то делать так всегда имхо проще для системы команд
а для «компа мечты» должно быть эффективней и удобнее, а не «проще»
В ней есть только одна операция X ?= Y с лёгкими вариациями. Swap уже не пролазит и не знаю как его протащить без вреда для внутренней красоты.
ну, уродливый inc2 протащил же :)
Только до тех лет когда начали делать 128Кб-ные машины.
с чего такой вывод? это просто посадить на ту же шину больше микросхем
а не разрабатывать весь микропроцессорный комплект, а не только проц
Да ну, не вижу ни трудностей ни тем более невозможностей.
например, девайс подкидывает байты высоким темпом, нужно успевать выгребать и складывать их, а складывать ты можешь только словами; будешь выделять блок вдвое больше, а потом корячиться с утаптыванием? + удорожание в любом случае
Вот можно было бы и послушать, если есть конкретные образы и идеи. :)
Зря лишил себя одного регистра из-за непосредственного операнда, всё равно его читаешь через PC, вот и кодируй (с индиректом) тоже через PC. И зачем адрес возврата обязательно пихать в память? В рисках, например, он пишется в (обычно специально выделенный) регистр, это дело вызываемой процедуры, сохранять в памяти его или нет. Можно сделать и через РОН, тут тебе пригодилась бы команда обмена, то есть вызов процедуры выглядит так load R,adr + swap PC,R и возврат load/swap PC,R (если обошёлся без стека).
Отказ полный от 8 бит может выглядеть восхитительно удачным только сегодня. Для 80-х все шины ширины 16 (и все вспомогательные микрухи!) в домашней машине дороговато, плюс доступную восьмибитную периферию трудно или невозможно использовать. И по мне, тогда уж лучше Форт-процессор на них сажать. Это уже был бы компик моей мечты))
Для образовательных целей объективно лучше подходил Лого, который, собственно, и был для этого разработан. Но почему-то воспринимался как игрушка для самых маленьких, хотя на деле был куда удобнее, выразительнее, мощнее и ближе к нормальному языку, чем любой бейсик. Вот такая несправедливость.
Тащемта все бейсики тех времён разной степени паршивости ерундень. Теоретически можно было упихать в 16k нормальный структурный интерпретатор, без номеров строк, с независимыми процедурами, локальными переменными итд. Но инерция и цейтнот у наёмных разработчиков не позволили.
win10home и смотрю, в разных браузерах по-разному
где-то ничего (ни промежутка даже) на месте прямоугольников
вероятно, так бы выглядеть и должно, но откуда взялся мусор невидимый
в 2 раза не выйдет, и не мечтай)) даже в самом удобном буфере, без учёта времени переброски, без ограничений на размер кода, с рисованием set и одновременно с двух концов, с экономией проверки после ступеньки, без учёта дополнительных расходов на цикл и хвост — теоретический предел 23+ такта на пиксель (для вертикалей)
если так, то у меня сейчас (лучший случай/худший случай/среднее)
467/610/538; 774/1013/893; 1221/1609/1415
но средневзвешенное дб поменьше среднего, особенно для линий короче 9 точек
так как самый худший случай (и близкие) статистически довольно редки
также думаю поменять местами ветки на входе
не ускорит, так хоть сузит разброс немного
протестирую и выложу исходники после этого
И еще. Как уже говорил на форуме, если сцена сложная, из большого кол-ва коротких линий, и в 25-50 fps заведомо не уложится, может оказаться выгоднее рисовать в буфере с удобной раскладкой и стеком перебрасывать на экран, что в итоге может получиться быстрее и по памяти примерно столько же вместе с буфером (и даже меньше, если вычесть второй экран)
Ну хз-хз, может быть, такой задачи там и не ставили, но в двух других примерах от call до постановки первой точки проходит ~400 и ~490 тактов, у меня в среднем ~330 (и это не старался еще особенно). Сам алон упоминал как рекорд около 270 емнип, но там, вероятно, цикл намного проще и медленней.
Где ты там увидел 16k?? Чуть больше восьми, если точно — 8635 байт на саму процедуру линии (и даже вместе с демо-кодом нет и 9k). Из них вход 169 байт и 2k заняли таблицы, которые все можно сократить на четверть за счёт небольшого замедления входа, а на освободившееся место распихать рисующие куски. То есть можно в 8k утоптать вполне. И нет, размер не мог удвоиться хотя бы потому, что ветка без ступенек короче, а еще для хвоста часть кода можно объединить.
у тебя сейчас возможно лишь 16 различных instr, а могло быть 32-(x<16)
во всех случаях «существенного отличия» экономнее использовать другой instr
и второй раз в операциях типа [DST=SRC],[SRC=DST] — ну так там уже прочитано в первый раз
лично я между любым кол-вом дополнительных инкрементов и обменом всегда выберу обмен
уже только ради одного обмена со стековым указателем, даже если больше не понадобится никак 8)
если не понадобится Y, так не понадобится
почему ломается? считай просто, что схема общая у тебя такая:
1) может быть, записать PC в стек
2) DST ?= SRC
и нет проблем :D
это только если по первому слову ясно (кстати, у тебя какой порядок слов-байтов? little/big endian?)
но если нужно элемент проверить целиком и условно пропустить следующий, то inc1+inc2 ничем не лучше add3
из-за этого двухстековые схемы и шитый код намного реже применимы, чем могло быть
переменные нормально надо располагать, и не понадобятся лишние инкременты :D
(а еще лучше адресацию [R+imm], но она не лезет в такую схему)
ИТЕРИРОВАНИЕ подразумевает использование каждого элемента, то есть последовательный доступ ко всем его частям, то есть одинарный инкремент :P
также не совсем понимаю смысл необходимости флага TI — почему бы всегда не производить копирование в X,Y?
почему «ломать»? обобщить!
что крайне бесит, так же как отсутствие циклических сдвигов
это инкремент на 2 несущественно, а нерациональный расход регистра — существенно
это в том числе вызов по таблице, рядовой случай
в какой массе? перечисли несколько хотя бы разумных случаев? вот как раз на 8-битках иногда могло еще быть полезно, потому что основные типы данных разных размеров, но здесь-то одинаковые они! притом польза, даже если где-то она и будет, не «в два раза», а всего-то навсего в (N+1)/N от всех циклов доступа в итерации
load imm = load [pc++]
load [imm] = load [[pc++]]
второй случай можно закодировать через номер регистра флагов
(потому что, ну кому и зачем мб нужен косвенный доступ через флаги))
например, обмен координат в однонаправленном алгоритме
сортировка пузырьком с условным обменом
(ты же хочешь операции с памятью?)
а я считаю это разновидностью подхода «и так сойдёт» :P
нет! это совершенно не то же самое! например:
load R3,adr1
(вычисления)
(условный переход)
load R3,adr2
(вычисления)
swap PC,R3
тем, что повторяет функцию универсального сложения с любой константой
собс-но, даже инкремент на 1 — пережиток неортогональных восьмибитных процов
(но он хотя бы нужен бывает часто, и его наличие мб оправданным)
у обмена же гораздо шире функционал
засадить могли, а в нормальную производительность z80 не шмогли
и это тормозное в cp/m режиме угрёбище справедливо было непопулярно
тем более, что и стоило вдвое дороже c64 того же года
на практике поддерживали 8 и 16 бит девайсы одновременно очень недешёвые песюки
причём в них не было полного отказа от 8 бит
то есть load [PC],[PC] это X=[PC++]; Y=[PC++]; X=[X]; [Y]=X
что эквивалентно load [imm1],[imm2]
вооот, в том числе еще поэтому нужен swap, а у тебя и регистров меньше, и свопа нет
только частных случаев очень много и эффект заметный от ускорения
плюс содержимое линк-регистра может служить указателем на данные процедуры
(после обработки данных с инкрементом получается корректный адрес возврата)
а для «компа мечты» должно быть эффективней и удобнее, а не «проще»
ну, уродливый inc2 протащил же :)
с чего такой вывод? это просто посадить на ту же шину больше микросхем
а не разрабатывать весь микропроцессорный комплект, а не только проц
например, девайс подкидывает байты высоким темпом, нужно успевать выгребать и складывать их, а складывать ты можешь только словами; будешь выделять блок вдвое больше, а потом корячиться с утаптыванием? + удорожание в любом случае
пока некогда :)
Отказ полный от 8 бит может выглядеть восхитительно удачным только сегодня. Для 80-х все шины ширины 16 (и все вспомогательные микрухи!) в домашней машине дороговато, плюс доступную восьмибитную периферию трудно или невозможно использовать. И по мне, тогда уж лучше Форт-процессор на них сажать. Это уже был бы компик моей мечты))
где-то ничего (ни промежутка даже) на месте прямоугольников
вероятно, так бы выглядеть и должно, но откуда взялся мусор невидимый
467/610/538; 774/1013/893; 1221/1609/1415
но средневзвешенное дб поменьше среднего, особенно для линий короче 9 точек
так как самый худший случай (и близкие) статистически довольно редки
также думаю поменять местами ветки на входе
не ускорит, так хоть сузит разброс немного
протестирую и выложу исходники после этого