Вход

Просмотр полной версии : Ядро с 32 битами и виртуализацией



Bolt
09.01.2020, 23:03
С хард-процессором не получилось, но появилось понимание что и как сделать. Попробую софт-процессор.

На основе своего "эмулятора-интерпретатора" пишу на Паскале симулятор. Привязан он не к программной обработке, а ближе к FPGA, поэтому в нём IF-ов ещё больше. Сначала реализую набор команд Z80, потом добавлю 32 бита, а как появится окончательная картина внутренней архитектуры буду переносить на Verilog.

32 бита будут точно не через префиксы, это будет отдельная система команд, использующая те же регистры (BC, DE, IX, A...). Также, возможно, в режиме совместимости с Z80 весь префикс ED будет обрабатываться программно, то есть для него будет вызываться обработчик, который в режиме интерпретатора обработает команду и вернёт управление. Будет медленнее, но учитывая что там и так надо половину опкодов эмулировать (порты, прерывания) можно пожертвовать скоростью ради ускорения 32 бит.

На данный момент в симуляторе получается в среднем около 6 тактов на один байт кода. В том числе обращения к памяти данных считаю 1 такт на байт при попадании в кэш, оно скорее всего так и будет. Частоту пока не знаю, но по предварительной оценке в Quartus 100 МГц должен осилить. До eZ80 с его 1 тактом/байт на частоте 50 МГц не дотягивает, но это будет точно быстрее Z80 на 20 МГц.

В общем, в новый год с новым проектом :)

Mick
10.01.2020, 09:16
Идея интересная. У меня были раньше более приземленные желания. Всего лишь софт процессор - 16 битный Z80. То есть расширить стандартные инструкции до полноценной работы с 16 битными данными.

Bolt
11.01.2020, 11:06
Расширяем регистры BC, DE, HL, IX, IY до 32 бит.
По возможности делаем набор ортогональным, чтобы можно было что-то типа "ld IX,(HL+34)", или "add BC,(DE)".
Битов для кодирования таких инструкций надо много, поэтому некоторые регистры и операции с ними делаем быстрыми, однобайтными. В остальном - не сдерживаем полёт фантазии.

Далее, например, в стандартном наборе "add, adc, sub, sbc, and, xor, or..." инструкция сравнения мне кажется лишней, а вместо неё просится что-то типа andn.
and - маска, оставить в первом операнде только те биты, которые во втором операнде равны 1.
xor - инвертировать биты.
or - установка битов.
andn - "and not", сбросить в первом операнде биты, которые во втором операнде равны 1.

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

Возможно, я не умею пользоваться флагом переполнения, но смешанное арифметическое сравнение signed и unsigned ломает мозг. Давайте сделаем четыре инструкции cpuu, cpus, cpsu, cpss.

Можно оставить sll в режиме Z80 как есть для совместимости, но в новом наборе исправить ошибку.

Префикс повтора, как в x86, не предлагать, он вообще никак не вписывается. Можно сделать 32-битный djnz.

Ну, огромное желание добавить mul и div даже не обсуждается.

Что ещё добавить, изменить?

NEO SPECTRUMAN
11.01.2020, 12:11
andn - "and not"
а это ищо что за х86 аутизм
когда есть nand

ееще бы eor\exor придумали

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


Что ещё добавить, изменить?
условное выполнение
add nz bc,de
зачем?
шоб было :)

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


Расширяем регистры BC, DE, HL, IX, IY до 32 бит
и если мы что то расширяем
то не уподобляемся мерзкому 8080
и расширяем в z80 стайл

тоесть новые 32 бит регистры

BCDE
DEHL - получается перегруженно
или
BCBC' - тоже не фантан и будет не удобно юзать

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


конечно
можно было бы
EBC
EDE
EHL

но тогда старшие 2 байта
придется выкалупывать для работы как с 8 бит числами
для чего потребются новые команды...
или просерание процессорного времени

такая же херня в мерском х86
AH AL есть и все хорошо
но далбше
AX (AXH уже нету)
EAX (EAXH тоже нету)


вощем нужно подумать как это правильно сделать






НАПРИМЕР

завести второй набор 16 бит регов для старшой половины extended 32 битных регов
BCH
DEH
HLH
или
HBC
HDE
HHL

32бит команды ЖЕ в стиле
add EBC,EDE
которые ПО СУТИ
add HBCbc, HDEde

младшие же 2 байта
будут сходу доступны старым командам z80
к старшим придется обращатся новыми командами

как вариант2 (ВМЕСТО) не уподоблятся х86

и завести второй набор регистров для младших 16 бит

BCL
DEL
HLL

преиущества такие

add ehl,ebc
равносильно
add HLhll,BCbcl
и по влиянию на флаги
сопоставимо с
add hl,bc
и знак у нас сохраняется!!!!

тоесть в любой момент времени
мы можем дрыгать точностью вычислиений
add hl,de
add ehl,ebc
mul ehl,eix
ld bc,hl - и так например отбросили младшие не значимые 16 бит

zebest
11.01.2020, 12:40
Что ещё добавить, изменить?
Елси и изобретать велосипеды, то можно хоть колеса одного диаметра делать??
https://docs.google.com/spreadsheets/d/1Nmxx0RG28bpMtmUYVaomRWnvBi4ilUd4qqWOM_V5AGE/edit#gid=1809398086
там вкладка для следующего есть, да и в доках они кое-что писали, что реализовали.
Да и ужасм вроде поддерживает новые инструпции.
*мимо проходил

Bolt
11.01.2020, 13:14
Елси и изобретать велосипеды, то можно хоть колеса одного диаметра делать??
https://docs.google.com/spreadsheets/d/1Nmxx0RG28bpMtmUYVaomRWnvBi4ilUd4qqWOM_V5AGE/edit#gid=1809398086
там вкладка для следующего есть, да и в доках они кое-что писали, что реализовали.
Да и ужасм вроде поддерживает новые инструпции.
*мимо проходил
У "следующего" есть интересные инструкции, но где там 32 бита? У него концепция совсем другая. У меня, скорее, 32-разрядный процессор с аппаратным эмулятором Z80, и вообще я задумывался о простом RISC и трансляции "на лету", но нашёл способ проще.

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


есть nand

Есть, и означает "not (A and B)", а у нас "A and (not B)".



условное выполнение
add nz bc,de
зачем?
шоб было :)


Условное выполнение используется чтобы конвейер не сбивать переходами. Будет конвейер - будет условное выполнение, а так оно действительно "шоб было".



и если мы что то расширяем
то не уподобляемся мерзкому 8080
и расширяем в z80 стайл


Не, не пойдёт. Плохой это стайл, особенно в плане скорости. На кристалле можно обойтись одним транзистором, а в FPGA синтезатор такие дурные схемы рисует, что частота сразу резко падает.



но тогда старшие 2 байта
придется выкалупывать для работы как с 8 бит числами
для чего потребются новые команды...
или просерание процессорного времени


"Гоги, а оно тебе зачем?" :) В смысле старшие байты. Просерание времени всё равно будет, потому что или все условно по 5 тактов, или некоторые по 3, а экзотика по 8. Старшие байты это по-моему экзотика.

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



add ehl,ebc
равносильно
add HLhll,BCbcl
и по влиянию на флаги
сопоставимо с
add hl,bc
и знак у нас сохраняется!!!!

тоесть в любой момент времени
мы можем дрыгать точностью вычислиений
add hl,de
add ehl,ebc
mul ehl,eix
ld bc,hl - и так например отбросили младшие не значимые 16 бит

Вот тут я вообще нить потерял.

Понимаешь, если в FPGA делать вот совсем всё на всё, то получается мультиплексор на тыщи ячеек и с огромной задержкой, в результате ядро будет работать на частоте 10 МГц. Что, в принципе, и наблюдаем почти во всех Z80-"корках".

То есть опять "либо то, либо это". Либо делаем огромный мультиплексор с возможностью подать на АЛУ всякие комбинации регистров 8/16/32 бит и получаем тормоза на всём, либо делаем тупой мультиплексор без сдвигов, получаем быстрые 32 бита и потерю одного такта при эмуляции старых 8-битных инструкций. Долго объяснять, но вот у меня такая загогулина получилась.

Но я подумаю куда это впихнуть можно. Если в итоге доступ будет быстрее чем альтернативы типа "srl BC,16" - сделаю. Swap word и swap byte устроит? :)

andrews
11.01.2020, 13:22
С хард-процессором не получилось, но появилось понимание что и как сделать. Попробую софт-процессор.

На основе своего "эмулятора-интерпретатора" пишу на Паскале симулятор. Привязан он не к программной обработке, а ближе к FPGA, поэтому в нём IF-ов ещё больше. Сначала реализую набор команд Z80, потом добавлю 32 бита, а как появится окончательная картина внутренней архитектуры буду переносить на Verilog.

32 бита будут точно не через префиксы, это будет отдельная система команд, использующая те же регистры (BC, DE, IX, A...). Также, возможно, в режиме совместимости с Z80 весь префикс ED будет обрабатываться программно, то есть для него будет вызываться обработчик, который в режиме интерпретатора обработает команду и вернёт управление. Будет медленнее, но учитывая что там и так надо половину опкодов эмулировать (порты, прерывания) можно пожертвовать скоростью ради ускорения 32 бит.

На данный момент в симуляторе получается в среднем около 6 тактов на один байт кода. В том числе обращения к памяти данных считаю 1 такт на байт при попадании в кэш, оно скорее всего так и будет. Частоту пока не знаю, но по предварительной оценке в Quartus 100 МГц должен осилить. До eZ80 с его 1 тактом/байт на частоте 50 МГц не дотягивает, но это будет точно быстрее Z80 на 20 МГц.

В общем, в новый год с новым проектом :)только не забудьте RTC и BT или WiFi и поддержку microSD в тот же кристалл. RISC точно бы упростил и архитектуру и конвейер. Быстродействия 50 МГц z80 точно не надо, если будут 32 битная арифметика и логика, достаточно 25 МГц для таких экранов.

Bolt
11.01.2020, 13:38
Да, и учтите что 32 бита это не просто арифметика, а ещё и 24-разрядный адрес.

У процессора как минимум два режима работы:
1. "Нативный", бинарно несовместимый с Z80, но имеющий плюшки типа 24-разрядной адресации.
2. "Эмулятор Z80", в котором он работает как быстрый Z80 и, если разрешено, через префикс имеет доступ к 32-битным инструкциям.
Запрет нужен чтобы проц не падал при запуске старого софта, в котором могут встретиться такие же комбинации недокументированных байтов.

Плюс ещё будет "kernel" и "user". В "kernel" можно всё, в "user" запрещён доступ к портам и т.п. Пусть приложение с железом работает через драйвер.

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


только не забудьте RTC и BT или WiFi и поддержку microSD в тот же кристалл. RISC точно бы упростил и архитектуру и конвейер. Быстродействия 50 МГц z80 точно не надо, если будут 32 битная арифметика и логика, достаточно 25 МГц для таких экранов.
andrews опять про технологии XXI века... :)

Нет, 25 МГц не достаточно. Экран тоже будет другой.

Ynicky
11.01.2020, 14:59
В 2016 году занимался аналогичным проектом.
Но не доделал. Хотя кое-что уже работало.
Например, по каналу RS232 из PC посылал файлы .scr в плату с FPGA с выводом на экран монитора.
Могу реанимировать проект, если есть интерес.
Вот описания на процессор и СнК.
https://cloud.mail.ru/public/XfgP/6ymYQ5bfU
https://cloud.mail.ru/public/2swe/2Mg4A9qfC

Lethargeek
11.01.2020, 16:03
Есть, и означает "not (A and B)", а у нас "A and (not B)".
не спорьте, эта операция обычно называется bic (BIt Clear) :v2_dizzy_botan:

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

Ynicky, о, ты живой :D в личку загляни

NEO SPECTRUMAN
11.01.2020, 16:17
Вот тут я вообще нить потерял.
ну дык
старшая половина 32битного регистра
лежит в 8битных регистрах H и L
в H за одно и знак в 7-бите

младшая половина 32битного регистра
лежит в новом 16 битном регистре HLL ну или LHL

все команды работающие с 32 бит регами
оперируют с HL и LHL как с одним регистром EHL

все старые команды z80
могут без проблем брать HL (старшую половину да еще со знаком)
и отбрасывать дробную часть LHL
или же вообще брать один H и работать по 8-ми битному

тоесть при переходе к работе с 8\16 битными числами старыми командами z80 из 32 битного режима
у нас НЕ ОСТАЮТСЯ ОГРЫЗКИ (младшие байты) от цифр
у нас остается числа деленные на 256 и со знаком
(другими словами просто со сниженной точностью)

мы можем работать с ними старыми командами
а потом снова новыми 32битными

то есть
add ede,ebc
add $01230000,$11111111
ede= $12341111
а тут старый 16 битный
add hl,de
add $0001,$1234
de = $1235 (старшая значимая половина со знаком доступна старым командам)

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


Есть, и означает "not (A and B)", а у нас "A and (not B)".
о боже:v2_dizzy_facepalm:
теперь будем знать

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

LeoN65816
11.01.2020, 16:33
Bolt, в плане концепции расширения разрядности проца с сохранением совместимости посмотри на примере 6502->65816 (W65C816S), и даже есть концепт 32-хбитного W65C832S (но, к сожалению, не реализован в серии).

andrews
11.01.2020, 16:44
andrews опять про технологии XXI века... :)
Нет, 25 МГц не достаточно. Экран тоже будет другой.
без этого не будет доступа к интернету, а тогда это по нынешеним временам игровая приставка, а не комп! "Другой экран" не должен весить тяжело >16k, иначе это потянет вниз другие характеристики компа. Вернее придется увеличивать объем всех видов памяти, пропускную способность шин, интернет-трафик

Lethargeek
11.01.2020, 16:46
а зачем нужна логическая операция?
выдавать 1 только
если первый операнд 1 а второй 0
с ходу не вижу применения
например, для битолюбства с вычислением автомасок

Bolt
11.01.2020, 19:49
add ede,ebc
add $01230000,$11111111
ede= $12341111
а тут старый 16 битный
add hl,de
add $0001,$1234
de = $1235 (старшая значимая половина со знаком доступна старым командам)

Теперь понял. На фиксированную точку похоже.

Быстро прыгать туда-сюда между 16 и 32 битами не планировалось, а адресация по младшим битам получается с интересной фичей, которую пока ещё не додумал.
Старые команды больше для совместимости, не для написания новых программ.

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

Фиксированную точку тоже потом применим :)

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


Bolt, в плане концепции расширения разрядности проца с сохранением совместимости посмотри на примере 6502->65816 (W65C816S), и даже есть концепт 32-хбитного W65C832S (но, к сожалению, не реализован в серии).
Что-то сразу не понял, потом надо будет вдумчиво почитать.
У него, кстати, andn называется rep:


SEP #%00010000 ; set bit 4 for 8-bit index registers.

REP #%00010000 ; clear bit 4 for 16-bit index registers.


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


В 2016 году занимался аналогичным проектом.
Но не доделал. Хотя кое-что уже работало.
Например, по каналу RS232 из PC посылал файлы .scr в плату с FPGA с выводом на экран монитора.
Могу реанимировать проект, если есть интерес.
Вот описания на процессор и СнК.
https://cloud.mail.ru/public/XfgP/6ymYQ5bfU
https://cloud.mail.ru/public/2swe/2Mg4A9qfC
Ого.
Это с нуля разработано или за основу было взято какое-то ядро? На какой частоте это работало? Регистры были в регистрах или использовалась встроенная в FPGA память?

NEO SPECTRUMAN
11.01.2020, 20:24
Теперь понял. На фиксированную точку похоже.
ну до
только есть проблема
при прыгании из 16 бит в 32
нужно будет занулять младшие 16 бит которые будут содержать результаті предыдущих 32 бит вычислений
или же игнорить и накалпивать иногда ошибки


как подобная трабла ведет себя на х86 я не помню
там тоже должна быть ситуация когда после 16 бит вычислений
старшие 2 байта какого нибудь EAX по идеи содержат cовсем не то
а вот будут ли они автоматом занулятся
или останутся как есть я чот забыл...

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


У него, кстати, andn называется rep:
у 6502 принято называть команды
рандомными обязательно 3-мя буквами чтоб усложнить запоминание :)

єто еще худе чем 8080

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


Быстро прыгать туда-сюда между 16 и 32 битами не планировалось,
ну если вешать 32 битные команды
после префиксов
и оставлять весь набор старых команд

то 8 битные команды будут самими производительными\легкове ными
и будут использоватся может даже чаще
а 32 бит только при необходимости


если же будет по образу и подобию х86
то постоянное прыгание из 32 в 16\8 уже может и не будет эффективным
да и памяти будет жрать на переключение...

Ynicky
11.01.2020, 20:28
Ого.
Это с нуля разработано или за основу было взято какое-то ядро? На какой частоте это работало? Регистры были в регистрах или использовалась встроенная в FPGA память?
За основу был взят мой же мультимедийный RISC процессор, разработанный с нуля.
Проект rz80soc работает на частоте 50 МГц. Регистры - это регистры, а не память.

Bolt
11.01.2020, 21:21
Ynicky, а Z80 как эмулируется? Табличными переходами с выполнением соответствующих RISC-команд?

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



ну если вешать 32 битные команды
после префиксов
и оставлять весь набор старых команд

то 8 битные команды будут самими производительными\легкове ными
и будут использоватся может даже чаще
а 32 бит только при необходимости

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

Как я себе представляю запуск чего-либо на этом "комбайне".
Есть старый софт, игры и прочее. Есть новый.
Новый пишется в режиме "32", работает быстро, использует все фичи. Например, ускорение работы с графикой, если оно будет.
Для запуска старого софта есть программа-эмулятор. Она загружает в своё 24-разрядное адресное пространство образ ROM и прочее, и делает переход на ROM-ные 0000h, переключив режим на "Z80". Далее всё работает как обычно, но на in/out отрабатывает trap, вызывается процедура из эмулятора, которая что-то загружает в аккумулятор и делает переход на следующую инструкцию.
То есть "Z80" - это не основной режим, а аппаратное ускорение эмуляции.
Как написано у Ynicky: "При проектировании данного микропроцессора ставилась задача разработки конвейеризированного процессора ... с учетом минимального программно-аппаратного эмулирования микропроцессора z80."

Ynicky
11.01.2020, 21:45
Ynicky, а Z80 как эмулируется? Табличными переходами с выполнением соответствующих RISC-команд?

Вот кусочек кода эмуляции z80:


...
global __main
align 256
__main:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lea r1,0x00010000 ;SP
lea r30,0x00000000 ;QR
lea r25,0x00010000 ;Base address of Basic48.rom
lea r26,0x00020000 ;Base address of IN/OUT ports

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_z80_Reset:
lea r19,0xFFFFFFFF
mov r32,r19 ;B
mov r33,r19 ;C
mov r34,r19 ;D
mov r35,r19 ;E
mov r36,r19 ;H
mov r37,r19 ;L
mov r38,r19 ;F
mov r39,r19 ;A
mov r40,r19 ;I
mov r41,r19 ;R
mov r42,r19 ;IXh
mov r43,r19 ;IXl
mov r44,r19 ;IYh
mov r45,r19 ;IYl
movi r46,0x00010000 ;SP
mov r47,r0 ;PC
mov r48,r19 ;B'
mov r49,r19 ;C'
mov r50,r19 ;D'
mov r51,r19 ;E'
mov r52,r19 ;H'
mov r53,r19 ;L'
mov r54,r19 ;F'
mov r55,r19 ;A'
mov r56,r0 ;IFF1
mov r57,r0 ;IFF2
mov r58,r0 ;IM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_z80_Cycle:
add r19,r47,r25 ;PC + Base Address r25
lbu r19,(r19)
addi r47,r47,1
slli r19,r19,2
lea r18,_z80_OPC
add r19,r19,r18
jr r19

_z80_OPC:
br _z80_00
br _z80_01
br _z80_02
br _z80_03
br _z80_04
br _z80_05
br _z80_06
br _z80_07
br _z80_08
br _z80_09
br _z80_0A
br _z80_0B
br _z80_0C
br _z80_0D
br _z80_0E
br _z80_0F
br _z80_10
br _z80_11
br _z80_12
br _z80_13
br _z80_14
br _z80_15
br _z80_16
br _z80_17
br _z80_18
br _z80_19
br _z80_1A
br _z80_1B
br _z80_1C
br _z80_1D
br _z80_1E
br _z80_1F
br _z80_20
br _z80_21
br _z80_22
br _z80_23
br _z80_24
br _z80_25
br _z80_26
br _z80_27
br _z80_28
br _z80_29
br _z80_2A
br _z80_2B
br _z80_2C
br _z80_2D
br _z80_2E
br _z80_2F
br _z80_30
br _z80_31
br _z80_32
br _z80_33
br _z80_34
br _z80_35
br _z80_36
br _z80_37
br _z80_38
br _z80_39
br _z80_3A
br _z80_3B
br _z80_3C
br _z80_3D
br _z80_3E
br _z80_3F
br _z80_40
br _z80_41
br _z80_42
br _z80_43
br _z80_44
br _z80_45
br _z80_46
br _z80_47
br _z80_48
br _z80_49
br _z80_4A
br _z80_4B
br _z80_4C
br _z80_4D
br _z80_4E
br _z80_4F
br _z80_50
br _z80_51
br _z80_52
br _z80_53
br _z80_54
br _z80_55
br _z80_56
br _z80_57
br _z80_58
br _z80_59
br _z80_5A
br _z80_5B
br _z80_5C
br _z80_5D
br _z80_5E
br _z80_5F
br _z80_60
br _z80_61
br _z80_62
br _z80_63
br _z80_64
br _z80_65
br _z80_66
br _z80_67
br _z80_68
br _z80_69
br _z80_6A
br _z80_6B
br _z80_6C
br _z80_6D
br _z80_6E
br _z80_6F
br _z80_70
br _z80_71
br _z80_72
br _z80_73
br _z80_74
br _z80_75
br _z80_76
br _z80_77
br _z80_78
br _z80_79
br _z80_7A
br _z80_7B
br _z80_7C
br _z80_7D
br _z80_7E
br _z80_7F
br _z80_80
br _z80_81
br _z80_82
br _z80_83
br _z80_84
br _z80_85
br _z80_86
br _z80_87
br _z80_88
br _z80_89
br _z80_8A
br _z80_8B
br _z80_8C
br _z80_8D
br _z80_8E
br _z80_8F
br _z80_90
br _z80_91
br _z80_92
br _z80_93
br _z80_94
br _z80_95
br _z80_96
br _z80_97
br _z80_98
br _z80_99
br _z80_9A
br _z80_9B
br _z80_9C
br _z80_9D
br _z80_9E
br _z80_9F
br _z80_A0
br _z80_A1
br _z80_A2
br _z80_A3
br _z80_A4
br _z80_A5
br _z80_A6
br _z80_A7
br _z80_A8
br _z80_A9
br _z80_AA
br _z80_AB
br _z80_AC
br _z80_AD
br _z80_AE
br _z80_AF
br _z80_B0
br _z80_B1
br _z80_B2
br _z80_B3
br _z80_B4
br _z80_B5
br _z80_B6
br _z80_B7
br _z80_B8
br _z80_B9
br _z80_BA
br _z80_BB
br _z80_BC
br _z80_BD
br _z80_BE
br _z80_BF
br _z80_C0
br _z80_C1
br _z80_C2
br _z80_C3
br _z80_C4
br _z80_C5
br _z80_C6
br _z80_C7
br _z80_C8
br _z80_C9
br _z80_CA
br _z80_CB
br _z80_CC
br _z80_CD
br _z80_CE
br _z80_CF
br _z80_D0
br _z80_D1
br _z80_D2
br _z80_D3
br _z80_D4
br _z80_D5
br _z80_D6
br _z80_D7
br _z80_D8
br _z80_D9
br _z80_DA
br _z80_DB
br _z80_DC
br _z80_DD
br _z80_DE
br _z80_DF
br _z80_E0
br _z80_E1
br _z80_E2
br _z80_E3
br _z80_E4
br _z80_E5
br _z80_E6
br _z80_E7
br _z80_E8
br _z80_E9
br _z80_EA
br _z80_EB
br _z80_EC
br _z80_ED
br _z80_EE
br _z80_EF
br _z80_F0
br _z80_F1
br _z80_F2
br _z80_F3
br _z80_F4
br _z80_F5
br _z80_F6
br _z80_F7
br _z80_F8
br _z80_F9
br _z80_FA
br _z80_FB
br _z80_FC
br _z80_FD
br _z80_FE
br _z80_FF

_z80_00: ;NOP
;;; nop
br _z80_Cycle
_z80_01: ;LD BC,nn
add r17,r47,r25 ;+ Base Address r25
lbu r33,0(r17) ;C
lb r32,1(r17) ;B
addi r47,r47,2
br _z80_Cycle
_z80_02: ;LD (BC),A
slli r17,r32,8 ;B
or r17,r17,r33 ;C
add r17,r17,r25 ;+ Base Address
sb r39,(r17)
br _z80_Cycle
_z80_03: ;INC BC
slli r17,r32,8 ;B
or r17,r17,r33 ;C
addi r17,r17,1
srai r32,r17,8 ;B
andi r33,r17,0xFF ;C
br _z80_Cycle
_z80_04: ;INC B
addi r32,r32,1
br _z80_Cycle
_z80_05: ;DEC B
subi r32,r32,1
br _z80_Cycle
_z80_06: ;LD B,n
add r17,r47,r25 ;+ Base Address r25
lb r32,(r17)
addi r47,r47,1
br _z80_Cycle
...

Но я его до конца не отладил.

Bolt
11.01.2020, 21:58
как подобная трабла ведет себя на х86 я не помню
там тоже должна быть ситуация когда после 16 бит вычислений
старшие 2 байта какого нибудь EAX по идеи содержат cовсем не то
а вот будут ли они автоматом занулятся
или останутся как есть я чот забыл...

Останутся. Беззнаковые можно обрезать при помощи "and EAX,0x0000ffff", для знаковых есть команды (http://sysprog.ru/post/16)

HardWareMan
12.01.2020, 05:56
Ynicky, а почему все регистры по сбросу в FF?

Ynicky
12.01.2020, 08:55
Ynicky, а почему все регистры по сбросу в FF?
Не все. PC, IFF1, IFF2, IM - в 0.
Сейчас не помню, но где-то вычитал, что они по включению питания в z80 имеют эти значения.

Lethargeek
12.01.2020, 15:11
Не все. PC, IFF1, IFF2, IM - в 0.
няз IR тоже надо занулить, а FFFF достаточно AF и SP, остальные не определены
это в случае нормального резета, а то есть еще и "особенный"
http://www.primrosebank.net/computers/z80/z80_special_reset.htm

Bolt
15.01.2020, 23:09
Симулятор вытягивает 10 МГц. Z80 почти готов, ПЗУ стартует, надо клавиатуру прикрутить.

Пока с новыми инструкциями мыслю "ортогонально", не экономя байты, потом буду пытаться уменьшать и ускорять.

"add BC,0x12345678" занимает 8 байт. Такты пока не считаю вообще :)

Схема "классическая": B, C, вместе BC, плюс старшие байты, 2 штуки.
Инструкции Z80 имеют доступ к B, C, BC. Новые инструкции по умолчанию имеют доступ к 32-битному BC, префиксами можно урезать до 16 и 8 бит. Доступ к регистру B и к старшим байтам буду потом по потребности думать отдельно.

Возник вопрос.
Как назвать 32-битные регистры?

EAA, EBC, EDE, EHL, EIX... (extended)?
WAA, WBC, WDE, WHL, WIX... (wide)?
QAA, QBC, QDE, QHL, QIX... (quad byte)?

b2m
16.01.2020, 10:05
Как назвать 32-битные регистры?
Если хочется аналогию с интелом, то extended:
bh B
bl C
bx BC
ebx EBC

svofski
16.01.2020, 10:11
Или логично, как в Neon-e:


[S0 S1 S2 S3]
[ D0 ][ D1 ]
[ Q0 ]

NEO SPECTRUMAN
16.01.2020, 12:03
EAA, EBC, EDE, EHL, EIX... (extended)?
я за extended

да и q w менее удобны для набора

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


EAA,
вот это выглядит несколько странно

тк у А нет 16 битного....
ну кроме AF

может просто EA ?
ну и нужно думать как работать со 2-м байтом ааAa
который не адресуется ни второй половиной аа
ни средствами старых команд z80

и нужно этот 16битный АА как то обзывать
детекчу у х86 w для обозначения word'
но WA смотрится не очень

ну и нужен какойнить AH...

Bolt
16.01.2020, 18:55
Если хочется аналогию с интелом

Да там и так куда ни поверни - везде Интел получается :)


Или логично, как в Neon-e:

Хочется сохранить названия регистров.


я за extended
...
может просто EA ?


Может.



ну и нужно думать как работать со 2-м байтом ааAa
который не адресуется ни второй половиной аа
ни средствами старых команд z80
и нужно этот 16битный АА как то обзывать


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



ну и нужен какойнить AH...



Да там и так куда ни поверни - везде Интел получается :)


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

А вообще зачем нужен этот AH? Ну я ещё понимаю BC использовать как два 8-битных, или EBC как два 16-битных...

Можете пример привести где это потребуется?

NEO SPECTRUMAN
17.01.2020, 08:11
А вообще зачем нужен этот AH? Ну я ещё понимаю BC использовать как два 8-битных, или EBC как два 16-битных...

Можете пример привести где это потребуется?


а сходу наверно нет

будет затык если понадобится обработать 8 битными командами второй младший байт
который не получится отколупать из 32 битного числа


если подумать роль 16 битного А
выполняет пара hl
может и 32 битные пусть работают только с ebc ede ehl ?
может еще добавить регов

blackmirror
17.01.2020, 21:40
Если народ уже добрался до команд длиной по 8 байт, то есть некоторые способы более компактного кодирования. Будем считать, что адрес и данные имеют разрядность по 16 бит и попробуем запихать в них 4 команды (про байты для примера забудем). Первая идея, это сделать 4 перезагружаемых таблицы и по группе в 4 разряда выбирать команду в длинном формате (или сразу микрокод). Но это много ресурсов и мало толка, поэтому попробуем закодировать команды честно. Сделаем 4x2 устройств, каждое выполняет свою операцию и записывает результат в регистр - он же первый операнд для следующей операции, то есть его кодировать не нужно. Для максимальной ортогональности вторым операндом будет любое из устройств и на его кодирование нужно 3 бита. Последний бит будет будет выбирать какое из двух устройств работает в данном такте, а какое отдыхает. Если вторым операндом оказывается это же устройство, то может выполняться какая-то специальная операция, к примеру обнуление регистра для устройства OR или установка в -1 для AND. Список операций выполняемых устройствами может быть к примеру таким:
SUB1/ADD
SUB2/NOR
LD3/ST
LD4/LDI/JMP(условный)
Операции ADD/SUB добавляют/вычитают что-то к своему регистру, при ссылке на себя каких-то особых действий не требуется. NOR просто пример побитовой операции, вместо него и ADD вполне может оказаться более полезным что-то другое. Операции LD в свой регистр загружают данные из памяти, а адрес берут из указанного в команде регистра, особых действий при ссылке на себя для них тоже не требуется, хотя можно что-то и придумать. Для операции ST не хватает второго операнда, поэтому операнд указывает сохраняемые данные, а адрес будем брать к примеру из SUB1. Дополнительно данные копируются в связанный с ST регистр, на случай если кому-то вдруг опять понадобятся.
Для команды JMP ссылка на себя превращает её в команду LDI которая загрузит следующее слово программы просто как константу. Остальные значения могут, либо указывать один из флагов формируемых устройствами, а адресом берём загруженное LDI. Или флагом всегда будет перенос к примеру из SUB2, тогда адрес перехода можно будет брать из регистров.

Ну а если не экономить биты, то можно к примеру поделить 64 бита на 16 групп по 4 бита, и сделать 16 таблиц по 16 ячеек как в первом варианте. Тогда можно будет выполнять по 16 операций за такт, хотя конечно не все из них будут полезны для вычислений.

Bolt
18.01.2020, 00:40
Если народ уже добрался до команд длиной по 8 байт
Народ экспериментирует :) Есть и однобайтные.


Ну а если не экономить биты, то можно к примеру поделить 64 бита на 16 групп по 4 бита, и сделать 16 таблиц по 16 ячеек как в первом варианте. Тогда можно будет выполнять по 16 операций за такт, хотя конечно не все из них будут полезны для вычислений.
Заманчиво. Но всё это упрётся в пропускную способность шины и никакого кэша не хватит.

Не совсем понял что с чем соединить, но по-моему это называется VLIW. У него ещё одна проблема есть, кроме ширины шин: кто это программировать будет? Или человек с особой усидчивостью, или оптимизирующий ассемблер, но кто ж его напишет. Был такой процессор Transmeta Crusoe. Неплохой, но "не взлетел". Нативно программировать его было некому, а в эмуляции x86 он по понятным причинам проигрывал этому самому x86.

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

Почему инструкция 8 байт? Да она и 20 может быть при таком подходе.

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


bset (IX+BC*4),BC

Или, например, сортировка.
Сравнить два значения в массиве, номера элементов заданы в BC и DE:


cp (IX+BC*2),(IX+DE*2)


Переход по таблице:


jp (BC*4+label)


Скопировать массив, в цикле:


ld (IX++),(IY++)


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

blackmirror
18.01.2020, 23:25
Еще некоторые соображения:

Сейчас возможности плис позволяют многое делать за 1 такт, но оно может может оказаться слишком дорогим по ресурсам, если в систему команд тащить всё подряд.
Первое, что нужно - это счётчик команд и механизм его увеличения. Последним может заниматься и ALU, у него будет достаточно времени, если шины адреса и данных не разделены. Но взглянув на обмен по шине, мы быстро придём к выводу, что так жить нельзя:
Addr Prog
Addr Prog Addr Rdata
Addr Prog Addr Wdata
Addr Prog Addr Rdata Wdata
Поэтому адрес и данные нужно разделить чтобы удвоить скорость. Но мы опять с тоской посмотрим на шину, поскольку половину времени занимает чтение программы.

Далее можно сделать или отдельную память и шину для чтения или кеш для программы. Можно даже прямого отображения, когда младшие разряды адреса однозначно выбирают вполне конкретную ячейку кеша и связанный с ней флаг.
Еще потребуется поделить кеш на части и назначить каждой свой тег, который будет сравниваться со старшими разрядами адреса команды.
Если тег совпадает и стоит флаг, значит инструкция выбирается из кеша, если тег не совпадает, то он перезаписывается, флаги сбрасываются, инструкция выбирается из памяти, записывается в кеш и устанавливается её флаг.
Если хочется самомодифицируемый код, то придётся еще отслеживать запись, и хотя бы сбрасывать флаги записываемых ячеек. Можно и перезаписывать, но вдруг по этим адресам живёт ПЗУ и ничего не получится?!

Наличие небольшого кеша позволит коротким циклам читать программу только на первой итерации, после чего операции с памятью смогут выполняться на 1 такт быстрее, за счёт отсутсвия необходимости читать программу.
Еще одна полезная фича - это регистры LoopStart, LoopEnd, LoopCnt, для организации аппаратных циклов. Смысл в том, что когда счётчик команд совпадает с LoopEnd мы будем заменять его на LoopStart, если LoopCnt>0 уменьшив при этом последний. Таким образом для циклов с фиксированным числом итераций не нужна будет инструкция сравнения и команда условного перехода.

Теперь нужно определиться, сколько внутри будет шин между регистрами и ALU, если две шины чтения операндов и одна записи результата плюс мультиплексор шин памяти, то за 1 такт можно выполнять следующие операции:
Ri=Rj+Rk - шина памяти не используется
Ri=Rj+(Rk) - регистр суммируется со значением из памяти, свободных шин для модификации адреса нет
Ri=(Rj+Rk) - адрес берётся после ALU, данные читаются в регистр
R0=(Ri), Ri+=Rj - все шины ALU используется для модификации адреса, поэтому данные записываются из памяти в фиксированный регистр
(Ri)=Rj, Ri+ - регистр записывается в память, приращение адреса можно взять только из кода команды
(Ri)=R0, Ri+=Rj - все шины ALU используется для модификации адреса, поэтому пишем фиксированный регистр
(Ri)+=Rj, Ri+ - чтение/модификация/запись, два буферных регистра на шину памяти позволят параллельно с записью выполнять следующую команду формата Ri=Rj+Rk
Ri=(Rj), (Rj)+=Rk - аналогично предыдущему, но если адрес не модифицировать, можно сохранить старое значение из ячейки памяти

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

Bolt
19.01.2020, 00:39
Сейчас возможности плис позволяют многое делать за 1 такт, но оно может может оказаться слишком дорогим по ресурсам, если в систему команд тащить всё подряд.
Первое, что нужно - это счётчик команд и механизм его увеличения. Последним может заниматься и ALU, у него будет достаточно времени, если шины адреса и данных не разделены. Но взглянув на обмен по шине, мы быстро придём к выводу, что так жить нельзя:
...

Счётчик команд считает сам, никаких ALU. Указатель стека тоже. Регистры inc/dec через отдельный блок.
Взглянуть на обмен по шине... У меня SDRAM, процессор с MMU, и видеовыход VGA в той же куче, куда смотреть? :)
У процессора нет параллельной шины, есть кэш на двухпортовой памяти, с одной стороны с ней работает процессор, с другой - контроллер SDRAM. Внешняя параллельная шина может и будет, но не скоро.
Кэш кода и данных раздельно. Про теги и прочее знаю. Кэш работает, но с ошибками, отладить надо.
Запись будет отслеживаться, самомодифицируемый код должен работать. Про ПЗУ хорошая мысль, надо запомнить.



Еще одна полезная фича - это регистры LoopStart, LoopEnd, LoopCnt, для организации аппаратных циклов. Смысл в том, что когда счётчик команд совпадает с LoopEnd мы будем заменять его на LoopStart, если LoopCnt>0 уменьшив при этом последний. Таким образом для циклов с фиксированным числом итераций не нужна будет инструкция сравнения и команда условного перехода.

Я против. Чем djnz не устраивает? Давайте её сделаем для любых регистров.
А с дополнительными регистрами мало того что усложняется процессор, так потом ещё думать что с ними делать во вложенных циклах и в прерываниях. А ещё держим в уме многозадачность.



Теперь нужно определиться, сколько внутри будет шин между регистрами и ALU, если две шины чтения операндов и одна записи результата плюс мультиплексор шин памяти, то за 1 такт можно выполнять следующие операции:
...

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

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

Основных регистров будет 9: BC, DE, HL, резерв, IX, IY, IZ, I_резерв, и отдельно аккумулятор. Резервным регистрам название пока не придумал, для чего они нужны - тоже. Просто регистры. I_резерв может будет типа BP, с быстрым доступом к локальным переменным.

Система команд практически "всё со всем".
Первый операнд: A, регистр, память.
Второй операнд, если нужен: то же, и ещё константа.
Память: [BC,DE,HL]*[1,2,4]+[IX,IY,IZ]+const. Всё опционально.
Функции: те же, что у Z80.

Что из этого сделать одним байтом, а что с префиксами - пока думаю.
Что получится за один такт тоже пока не знаю, это станет понятно когда до Verilog-а доберусь.

На данный момент однобайтные:
inc/dec A/R
mov A,R
mov R,A
одноместные с аккумулятором (сдвиги, инверсия)

Думаю о push/pop. Можно и их сделать однобайтными, но они вроде не так часто используются.
Djnz тоже тогда однобайтной сделать. А может "dec / jpnz" обойдёмся?

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

Во, резервный для данных будет MN, а резервный индексный IV - vars. И можно сделать укороченный вариант опкодов для "func A,(IV+offset)".

Bolt
19.01.2020, 03:17
Попробовал. IV вообще в коде не воспринимается. То ли половинка от X, то ли римская 4. И если бегло просматривать с IX можно спутать.

Может IS? Какие ещё буквы свободны?

add A,(IS+0x34)

blackmirror
19.01.2020, 10:38
Bolt, даже если внешней шины нет, значит есть шина кеша и она для нас и играет роль памяти. Правильно ли я понимаю, что каждый порт этой памяти имеет шину адреса, прочитанных данных и данных для записи, но одновременно прочитать и записать через один порт мы не сможем?
По сравнению с djnz аппаратные циклы хороши тем, что кроме инициализации не имеют других накладных расходов, для снижения которых не требуется делать разворот в случае малого количества команд. К примеру если тело цикла из одной команды, добавление djnz снизит его производительность в 2 раза, а когда мы его развернём, нужен будет дополнительный код, чтобы определить сколько полных блоков и сколько итераций в хвосте.
Во всяких Cortex команды push и pop в качестве аргумента имеют битовую маску какие регистры сохранять/загружать, а какие пропустить. Есть команды загрузки сохранения аналогичные pop и для других регистров. И еще есть команда загружающая/сохраняющая сразу по 2 произвольных регистра то есть: Ri=(Ra) Rj=(Ra+4) Ra+=8.

Дополнительные размышления привели меня к мысли, что для обращения к памяти сложные формы адресации не требуются.
Требуется поделить банк регистров на чётные и нечётные, поставить два ALU, две шины записи и 4 шины чтения, и в коде команды указывать что подать на мультиплексоры для этих шин. Для записи в память несколько бит будут выбирать с какой из шин выдать адрес и данные. Для чтения нужно выбрать с какой шины выдавать адрес, и нужно ли прочитанные данные поместить в фиксированный регистр или выдать на шину записи. В итоге в 32 бита команды можно закодировать две регистровые операции по 12-13 бит и одну пересылку 5-6 бит, в достаточно ортогональном для удобства программирования виде.

Bolt
19.01.2020, 17:55
Правильно ли я понимаю, что каждый порт этой памяти имеет шину адреса, прочитанных данных и данных для записи, но одновременно прочитать и записать через один порт мы не сможем?

Да, правильно.



К примеру если тело цикла из одной команды, добавление djnz снизит его производительность в 2 раза

А если выполнять по 2 команды за такт, то цикл из одной команды замедлится в 3 раза :)
Нет, идея с регистрами мне не нравится. И в любом случае переход сбивает буфер инструкций (конвейер). Потом может для циклов придумаю что-нибудь.



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

Пусть будут, они не мешают. Load/store архитектура тоже имеет недостатки.



Во всяких Cortex команды push и pop в качестве аргумента имеют битовую маску какие регистры сохранять/загружать, а какие пропустить. Есть команды загрузки сохранения аналогичные pop и для других регистров. И еще есть команда загружающая/сохраняющая сразу по 2 произвольных регистра то есть: Ri=(Ra) Rj=(Ra+4) Ra+=8.



Требуется поделить банк регистров на чётные и нечётные, поставить два ALU, две шины записи и 4 шины чтения, и в коде команды указывать что подать на мультиплексоры для этих шин. Для записи в память несколько бит будут выбирать с какой из шин выдать адрес и данные. Для чтения нужно выбрать с какой шины выдавать адрес, и нужно ли прочитанные данные поместить в фиксированный регистр или выдать на шину записи. В итоге в 32 бита команды можно закодировать две регистровые операции по 12-13 бит и одну пересылку 5-6 бит, в достаточно ортогональном для удобства программирования виде.
Для такого двойного процессора программист будет писать две команды в строке, или ассемблер соберёт две в одну? Потому что проектировать процессор с блокировками и байпасами меня что-то совсем не прёт.

Разделение регистров позволяет и push сделать по маске, и два ALU с двумя операциями за такт, но я на данный момент вообще не понимаю как эту параллельность воткнуть в то, что есть. При 32-битных командах RISC всё получается просто, но надо сохранить совместимость с Z80, а он совсем не RISC, переменная длина команд всё портит. Предел, на котором по-моему надо остановиться - один байт кода за такт.

По тактам минимум получается примерно так:
mov A,R - 1 такт
mov R,A - 1 такт
mov R,R - 2 такта
func A - 1 такт
func R - 2 такта
func A,R - 3 такта
func R,A - 4 такта
func A,imm32 - 6 байт, 8 тактов
func [reg],imm32 - 7 байт, 17 тактов

Доступ к памяти пока по 1 байту, поэтому так долго. Можно, конечно, и внутреннюю шину сделать 32 бита, и не ждать окончания записи, и что-то распараллелить, всё можно. Но я тогда закопаюсь в этих деталях и до главного - запуска процессора - не доберусь.

blackmirror
19.01.2020, 19:00
А если выполнять по 2 команды за такт, то цикл из одной команды замедлится в 3 раза
Нет, идея с регистрами мне не нравится. И в любом случае переход сбивает буфер инструкций (конвейер). Потом может для циклов придумаю что-нибудь.
Конвейер сбивают непредсказанные переходы, а аппаратные циклы штука вполне предсказуемая.


Для такого двойного процессора программист будет писать две команды в строке, или ассемблер соберёт две в одну? Потому что проектировать процессор с блокировками и байпасами меня что-то совсем не прёт.
Можно либо ставить специальный знак, что к данной строке нужно прицепить команду из следующей, но мне больше нравится вариант продолжать команды в той же строке.


Разделение регистров позволяет и push сделать по маске, и два ALU с двумя операциями за такт, но я на данный момент вообще не понимаю как эту параллельность воткнуть в то, что есть. При 32-битных командах RISC всё получается просто, но надо сохранить совместимость с Z80, а он совсем не RISC, переменная длина команд всё портит. Предел, на котором по-моему надо остановиться - один байт кода за такт.
С переменной длинной может помочь только двоичная перекомпиляция, но наверно действительно лучше остановиться.

andrews
19.01.2020, 22:19
Да уж, не ASIC запускаете в тираж. Посему можно иметь в дальнейшем хоть дюжину более сложных и более совершенных версий архитектуры. Выдайте "на гора" что-то лучшее чем имеющееся и это уже будет успех!

Bolt
21.01.2020, 21:01
С переменной длинной может помочь только двоичная перекомпиляция, но наверно действительно лучше остановиться.
О двоичной перекомпиляции тоже думал, в итоге остановился на таком вот гибридном процессоре.

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


можно иметь в дальнейшем хоть дюжину более сложных и более совершенных версий архитектуры.
Ага. Только желательно сохранить совместимость по коду.

Bolt
30.01.2020, 14:06
Вариант для циклов.

Сделать не djnz, а djz. Тогда цикл можно развернуть так:


ld BC,1234
loop:
...
djz BC,exit
...
djz BC,exit
...
djz BC,exit
...
djz BC,exit
jp loop
exit:

А разворачивать может и ассемблер, типа dup.

Lethargeek
30.01.2020, 15:31
Вариант для циклов.

Сделать не djnz, а djz. Тогда цикл можно развернуть так:


ld BC,1234
loop:
...
djz BC,exit
...
djz BC,exit
...
djz BC,exit
...
djz BC,exit
jp loop
exit:

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

Bolt
30.01.2020, 16:06
Внутрь тела перейти сложнее, потому что надо получить адрес нужной итерации. Или можно проще?

Проверок осталось столько же, но сброс конвейера только один на N итераций, "jp loop".

blackmirror
30.01.2020, 18:55
Bolt, с аппаратными циклами сбрасывать конвейер вообще не требуется, поскольку видя совпадение PC с адресом последней инструкции цикла, мы уменьшим на 1 счётчик и если он не нулевой, просто загрузим в PC из другого регистра адрес его начала, а не PC+1. Этот более общий механизм, чем строковые инструкции из x86, поскольку позволяет организовывать циклы из нескольких инструкций без дополнительных накладных расходов.

Lethargeek
30.01.2020, 19:19
Внутрь тела перейти сложнее, потому что надо получить адрес нужной итерации.
а что сложного в его получении? из таблицы адресов, например

Bolt
31.01.2020, 09:29
а что сложного в его получении? из таблицы адресов, например
Можно и так, конечно.

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


Bolt, с аппаратными циклами сбрасывать конвейер вообще не требуется, поскольку видя совпадение PC с адресом последней инструкции цикла, мы уменьшим на 1 счётчик и если он не нулевой, просто загрузим в PC из другого регистра адрес его начала, а не PC+1.
Вариант нулевой. Конвейер как бы есть, но прогоняем по нему инструкции по одной. Самый наипростейший вариант, но и самый медленный.

Вариант первый. Грузим в конвейер с одной стороны, забираем с другой, на каждом переходе ждём N тактов для заполнения. Вариант простой, но потеряется много тактов, особенно на коротких циклах.

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

Вариант третий. Делаем предсказание переходов. Плюс один блок памяти, сотня-другая ячеек в ПЛИС, но выигрываем в скорости на переходах. Вариант достаточно сложный, если заморочиться с алгоритмом предсказания. Но djnz так же выполняется, как и раньше, пусть даже за один-два такта.

Вариант четвёртый, с регистрами. Лишних тактов на djnz не тратится, но тут я вообще не понимаю как это реализовать. Нет, если тупо гнать данные, сравнивая PC и перепрыгивая на другой адрес, то всё просто. Но что делать с прерываниями внутри цикла? Какой адрес возврата сохранять в стек? Какое значение читать из регистра-счётчика (оно будет меньше, чем должно быть для текущей выполняемой инструкции)? Как перегружать поток при выходе из цикла?

andrews
31.01.2020, 11:42
Но что делать с прерываниями внутри цикла? Какой адрес возврата сохранять в стек? Какое значение читать из регистра-счётчика (оно будет меньше, чем должно быть для текущей выполняемой инструкции)? Как перегружать поток при выходе из цикла?на прерывание завести что-то параллельное, запретив вложенные прерывания? Конечно не полный параллелизм, а до завершения циклов. Или делать останов до завершения обработки прерывания. Если только нет недопустимых инструкций и прерывания при недопустимом обращении к памяти(порту).

Bolt
31.01.2020, 19:25
andrews, планируются и недопустимые инструкции, и прерывания при недопустимых обращениях.

Три режима: ядро, пользователь, и эмуляция Z80. В режиме ядра можно всё, в режиме пользователя запрет на некоторые команды, в режиме эмуляции перехват обращения к портам для эмуляции железа. Программа в режиме эмуляции по идее вообще не должна обнаружить, что она не на железе выполняется.

Ещё может быть нераспределённая память. То есть приложению выделяется конечно же не 16 мегабайт, а сколько оно попросит (и сколько есть), в адресном пространстве будут "дырки". Можно игнорировать запись и читать 00h, а можно вызывать прерывание.

blackmirror
31.01.2020, 19:39
Вариант четвёртый, с регистрами. Лишних тактов на djnz не тратится, но тут я вообще не понимаю как это реализовать. Нет, если тупо гнать данные, сравнивая PC и перепрыгивая на другой адрес, то всё просто. Но что делать с прерываниями внутри цикла? Какой адрес возврата сохранять в стек? Какое значение читать из регистра-счётчика (оно будет меньше, чем должно быть для текущей выполняемой инструкции)? Как перегружать поток при выходе из цикла?
В самом простом для понимания варианте есть:

DoLoop LoopEnd, LoopCnt
LoopStart: - реально не требуется так как команда запуска цикла может вычислить эту метку сама
...
LastInst
LoopEnd: - метка первой команды за циклом(для простоты понимания)
Инструкция запуска цикла адрес следующий инструкции записывает в LoopStat, а в LoopEnd и LoopCnt пишет переданные в параметрах значения. Когда PC становится равным LoopEnd мы уменьшаем счётчик и если еще не 0, то перезаписываем PC из LoopStart, иначе продолжаем выполнять код как обычно. Проблема здесь только в том, что мы либо теряем такт на перезапись PC, либо снижаем скорость выборки программы чтобы успело установиться новое значение PC при выдаче его на шину. Именно по-этому метку нужно перенести на последнюю инструкцию, тогда компаратор PC c LoopEnd скажет нужно ли заменить его на LoopStart (при условии что счётчик не 0).

Реально действия последней команды ничем не отличаются от остальных, за исключением того, что к ним добавляется уменьшение счётчика и выбор что записывать в PC. То есть, если переход в прерывание выполняется перед последней командой цикла, в стек пойдёт её адрес. Если после, то сохраняется адрес первой команды за циклом, если счётчик как раз обнулился, иначе сохраняется LoopStart. Ни эти регистры на прерывание, ни прерывание на регистры никак не влияют, поскольку нужно вернуться из прерывания и дойти до LoopEnd, чтобы этот механизм снова сработал. Если последней командой цикла стоит вызов подпрограммы, мы должны сохранить в стек либо адрес после цикла, либо LoopStart. Из такого цикла можно делать условные переходы, главное потом вернуться обратно внутрь.

Если мы хотим запустить вложенный цикл, то нам требуется сохранить LoopStart, LoopEnd и LoopCnt, а после его окончания восстановить. Или можно поручить это команде которая запускает цикл, а восстановление будет производиться при обнулении счётчика. Хотя для ситуации когда цикл не вложенный, и перед запуском счётчик и так был 0 это будет лишней работой. Может, есть смысл сделать два вида команды запуска цикла с сохранением предыдущего и без, правда тогда еще появится аппаратный флаг, чтобы указать на необходимость восстановления в конце счётчиков внешнего цикла.

Соображение по поводу конвейера и обработки прерываний: сигнал запроса прерываний должен просто заблокировать выборку следующей инструкции, заморозить PC, запретить прерывания и подсунуть в регистр инструкций команду перехода к обработчику, достаточно обычного CALL и на этом его миссия может быть завершена. Тогда вызов прерываний пойдёт по конвейеру вместе с обычными командами.

Bolt
31.01.2020, 20:43
Так...

Вот смотри. Есть у нас "конвейер". Допустим, на 5 тактов. Я понимаю как это сделать, и как сделать LoopCnt, но я не понимаю как выгребать из разных заковыристых ситуаций.

Выполняем длинный кусок без ветвлений.
Когда на выходе PC, на входе PC+4.
Если прилетело прерывание - что сохранить в стек?

Цикл из одной инструкции. Или из двух. Когда выполняем PC, на входе PC или PC-1. Что сохранять в стек?
В теле цикла используется счётчик. Выполняется инструкция на выходе конвейера, но счётчик-то уже на 4 уменьшился.
А тут ещё и прерывание. Что сохранять в стек? А что делать с тем, что уже выбрано? Что делать с счётчиком, который уже уменьшен?
Сохранять в стек адрес следующей инструкции, то есть которая на предпоследнем этапе? А если счётчик ушёл в ноль и выбрана ещё одна инструкция после цикла?
Ну сохранили адрес предпоследней, то есть инструкции в цикле, которая единственная, а счётчик уже обнулён. Куда возвращаться и что делать? Прокрутить цикл ещё 2^32 раз? Предусмотреть ещё один флаг?

Не, ну можно, например, доделать что уже попало в конвейер, потом уходить в прерывание. А в конвейере уже начался новый цикл.
И теперь это всё на HDL, плз.

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

Регистрам Loopcnt - нет.

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

Тут другая проблема актуальна.
Выбираем 5 инструкций. Но 3 выбраны, и это джамп, а 2 уже уползло в запретную страницу, и они уже не понадобятся. Это как разруливать? Запретить программисту располагать код ближе, чем на 5 байт к концу страницы?

b2m
31.01.2020, 21:27
Если прилетело прерывание - что сохранить в стек?
Пока команда не дошла до конца конвейера, значения регистров (на её этапе) могут быть неверными. Поэтому если прилетело прерывание - конвейер придётся сбросить, и сохранять будем соответственно PC.

blackmirror
31.01.2020, 21:27
Bolt, по поводу разруливания заковыристых ситуаций основное соображение такое:
Прерывание нужно отправлять в конвейер как обычную инструкцию(запретив при этом другие), нет смысла ради него что-то бросать, поскольку быстрее в прерывание мы не попадём.
А вот при исключении конвейер нужно очистить, что сильно упрощается, если инструкции что-то записывают только на последней стадии. Всё, что меняется на предыдущих стадиях придётся тащить через конвейер, чтобы при исключении иметь возможность восстановить состояние.
А вот для доступа в запрещённые страницы придётся снабжать буферные регистры тегами, сигнализирующими о такой ситуации, чтобы исключение возникало, только если эти данные действительно потребуются.
Это всё соображения с точки зрения программирования. Не знаю как, видимо это случилось не сразу, но разработчики смогли заставить работать аппаратные циклы у монстра, выполняющего до 4 скалярных + 4 векторных инструкций за такт (векторные регистры по 64 байта, есть инструкции выполняющие по 32 умножения для double или 128 для float). Правда переставлять инструкции он не умеет и если хоть одна чего-то ждёт, тормозить будет весь конвейер, в общем программировать его тот еще ад.

Smalovsky
31.01.2020, 22:05
Мне понравилась ветка Ynicky.
https://zx-pk.ru/threads/31254-yadro-s-32-bitami-i-virtualizatsiej.html?p=1041654&viewfull=1#post1041654
https://zx-pk.ru/threads/31254-yadro-s-32-bitami-i-virtualizatsiej.html?p=1041762&viewfull=1#post1041762
Где он предложил для замены z80 новую RISC-архитектуру. Корнечно, я не настаиваю на этой идее, что она самая правильная, но считаю её хорошей альтернативой. Может, не цепляться за комманды z80,а сделать RISC-архитектуру наиболее подходящую для эмуляции z80? Что-то подобное было реализовано исторически в амиге, где на смену м680000 пришёл поверписи. Можно сделать такую RISC-архитектуру, которая напоминала бы своими подходами z80 для удобства программистов переходящих с z80, но не отягощённую повтором специфики z80 точь в вточь. Главное условие - хорошая эмуляция z80 и удобство перехода на неё программистов привыкших к z80.
Для начало можно обкатать процессор предложенный Ynicky. Посмотреть, что можно усовершенствовать, а что убрать. Хотя я заметил, что процессор Ynicky не имеет каких-то подходов общих с Z80. Это создаст трудность перехода на него у программистов привыкших к z80.

Прочёл док на процессор Ynicky. Недостаток - раздельня память комманд и данных. А для компьютера надо общую память для комманд и данных. Поэтому для использования этого процесса необходимо подкорректировать архитектуру в сторону общей памяти комманд и данных.

Bolt
01.02.2020, 02:37
Прерывание нужно отправлять в конвейер как обычную инструкцию
Во, точно.


Не знаю как, видимо это случилось не сразу, но разработчики смогли заставить работать аппаратные циклы у монстра, выполняющего до 4 скалярных + 4 векторных инструкций за такт (векторные регистры по 64 байта, есть инструкции выполняющие по 32 умножения для double или 128 для float). Правда переставлять инструкции он не умеет и если хоть одна чего-то ждёт, тормозить будет весь конвейер, в общем программировать его тот еще ад.
Вот именно, не сразу. А я только начал.
О какой архитектуре идёт речь?


Это всё соображения с точки зрения программирования.
С точки зрения программирования нафантазировать можно что угодно, а вот реализация этих фантазий может быть очень сложной.
Как получилось с eZ80 - а давайте по опкоду посчитаем длину инструкции. Простая вроде бы задача. Ага, щаззз! Кстати, можешь сам попробовать, там интересно ;)

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


Мне понравилась ветка Ynicky. Где он предложил для замены z80 новую RISC-архитектуру.
Мне тоже понравилась, но не только сама идея, она прям напрашивается, а ещё то, что она была доведена до рабочего состояния.


Можно сделать такую RISC-архитектуру, которая напоминала бы своими подходами z80 для удобства программистов переходящих с z80, но не отягощённую повтором специфики z80 точь в вточь. Главное условие - хорошая эмуляция z80 и удобство перехода на неё программистов привыкших к z80.
Не надо удобства. Переходить на неё программистам не придётся.

Делаем RISC-архитектуру, которая способна работать как интерпретатор Z80. И пофиг на систему команд.
На эту архитектуру сверху наваливаем 32 бита. Как ни крути - получим нечто похожее на Z80. Пофиг на бинарную совместимость.
Пишем интерпретатор в командах этого RISC и запихиваем в ПЗУ.

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

Тут надо две картинки: "мы поставили процессор в процессор, чтобы ты мог программировать процессор, когда программируешь процессор" и "we need to go deeper..." :)

Lethargeek
01.02.2020, 03:04
Делаем RISC-архитектуру, которая способна работать как интерпретатор Z80. И пофиг на систему команд.
ничего не делаем, берём обычный арм, распределяем память на 8-битную спектрумовскую и 32-битную армовскую
каждому 8-битному опкоду в спековской памяти сопоставляем 32-битный опкод вызова процедуры в армовской
при перезаписи байта в спековской памяти (кроме одного спецзначения) изменяем и опкод процедуры
иии... всё, мы "расширили z80" на сколько нам угодно любых 32-битных команд :D

Bolt
01.02.2020, 03:16
Lethargeek, а потом в 128-м переключается страница иии... всё, надо копировать 64 килобайта? :)

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

Lethargeek
01.02.2020, 04:20
а потом в 128-м переключается страница иии... всё, надо копировать 64 килобайта?
вот с чего бы? сопоставлены-то полные адреса


А ещё перезапись байта меняет не только один опкод, а до четырёх инструкций сразу.
ну и что? армопроцедуры могут это учитывать (они вообще всё могут лучше z80))


Не знаю где об это можно споткнуться, но сам факт...
ну вот, когда узнаешь, и приходи :v2_dizzy_bye:

andrews
01.02.2020, 12:06
Делаем RISC-архитектуру, которая способна работать как интерпретатор Z80. она ценна сама по себе, потому что те ядра, которые бесплатно шли к Altera и Xilinx-ам как-то не пользовались особой популярностью для ретро-компов.

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


берём обычный армкакой именно? 1-й, 4-й, 11-й. И разве разработчиков этих ядер волновала возможность реализации на них z80 и Spectrum-ов. Любое ненужное излишество это вентили, вентили, вентили. Вот люди в России не знают чем загрузить ранее приобретенную фабрику на 130 нм чипы и даже выбивают деньги на 65 нм под Эльбрусы. Так что если ASIC влезет в 130 нм то это низкая себестоимость. Потом я не согласен, что более сложная архитектура или архитектура "черного ящика" жизнеспособна. В любом случае это ограничит сферу ее применения. А так делать значит обрекать ее на жизнь недолгую и непопулярную. "Делай проще, дурашка" самый верный принцип! Можно и другие критерии для себя сформулировать и им следовать в разработке.

blackmirror
01.02.2020, 13:01
О какой архитектуре идёт речь?
Пока она находится на этапе изготовления опытных образцов и в интернете про неё кроме названия нифига нет. Что будет дальше тоже сложно сказать, может будет выпущена небольшая партия, а может примут решение перед этим что-то еще доработать. По частоте и пропускной способности памяти она конечно отстаёт от интела, по теоретической вычислительной мощности примерно сопоставимо, но всё зависит от задачи и степени её оптимизации. Кроме умножения матриц, которое очень полезно для нейронных сетей, там есть некоторые фичи присутствующие у видеокарт, к примеру внутренняя память делится на 32 банка и можно выполнять по 32 обращения каждый такт(элементы по 1, 2 или 4 байта). Интел в последних версиях процессора может читать по 8 независимым адресам, правда если будут кеш промахи это будет долго, у внутренней памяти в этом смысле есть плюсы. Реально данные поступят через 8 тактов, но если это учесть, то к примеру для ветвления по 3х уровневому дереву, можно получить среднюю производительность 1 дерево/такт. При этом для каждого узла читается по два весовых коэффициента и два индекса в массиве, затем читаются элементы массива, перемножаются и сравниваются, после чего выбирается левое или правое поддерево, ну а в финале выбирается лист с цифрой, которые должны суммироваться чтобы выяснить в какой момент нужно прервать цикл.


С точки зрения программирования нафантазировать можно что угодно, а вот реализация этих фантазий может быть очень сложной.
Нужно соблюдать баланс между возможностями аппаратуры и удобством программирования, последнее очень важный фактор для успеха любой архитектуры. У монстра к примеру DMA для полной загрузки шины требует выравнивания пересылок на 16 байт, а потом оказывается что нужно сдвинуть строки во внутренней памяти, чтобы избежать конфликтов банков и это удваивает объём кода и учетверяет время для его отладки.


Как получилось с eZ80 - а давайте по опкоду посчитаем длину инструкции. Простая вроде бы задача. Ага, щаззз! Кстати, можешь сам попробовать, там интересно
У меня был написан на ассемблере дизассемблер инструкций реального и защищённого режимов X86 включая MMX, но когда началось SSE2 с префиксами стало очень весело, и вот тут я это дело забросил. А для Z80 нам требуется пропустить все префиксы, те которые влияют на декодирование инструкции превратить в набор дополнительных бит, добавить опкод, после чего из таблицы мы должны получить длину. Можно решать эту задачу и параллельно, если число префиксов ограничено, то классифицируем все префиксы, при примеру 1, 2, 3 или 0 - "не префикс", собираем эти биты вместе, далее из таблицы вытаскиваем длину префиксов, и адрес таблицы длин для опкодов.

Bolt
01.02.2020, 13:34
она ценна сама по себе, потому что те ядра, которые бесплатно шли к Altera и Xilinx-ам как-то не пользовались особой популярностью для ретро-компов.
Не могу оценить её ценность, но скорее всего это в итоге будет специализированный микрокод. Для Z80 эмулятор с некоторыми допущениями сейчас занимает 2 килобайта.

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


А для Z80 нам требуется пропустить все префиксы, те которые влияют на декодирование инструкции превратить в набор дополнительных бит, добавить опкод, после чего из таблицы мы должны получить длину. Да в Z80 всё просто, я про eZ80. У него не только префиксы меняют длину, но и внутренний режим. А режим переключается переходами с префиксом, уходом в обработчик прерывания, а ещё режим может восстанавливаться из стека. Вот на чтении из стека я сломался :) Не знаю, может и можно это отловить.

andrews
01.02.2020, 13:57
Для Z80 эмулятор с некоторыми допущениями сейчас занимает 2 килобайта.а ядро сейчас в виде эмулятора или уже на fpga?

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


умножения матриц, которое очень полезно для нейронных сетей
все операции с матрицами и векторами используются и в манипуляциях с игровыми объектами и в 2d и в 3d, но это может быть и не внутри ядра, если игровое поле большое все-равно придется лазать по памяти. Тут "тяжелые" изображения сильно все усугубляют. Поэтому для "легких" нужно свой видеопроцессор разрабатывать, хоть с физикой, хоть без.

Bolt
01.02.2020, 14:08
Нужно соблюдать баланс между возможностями аппаратуры и удобством программирования, последнее очень важный фактор для успеха любой архитектуры.
Я не про то, не про удобство программирования.

Можно нафантазировать любую систему команд, которая сможет считать вдоль, поперёк, а ещё по диагонали и стоя в гамаке :) Потом так же тупо "в лоб" описать это на HDL, не думая что там получится в "железе". А получится там 5000 cell-ов и частота 30 МГц.
А можно, увидев такое непотребство, как-то переделать, что-то выкинуть, что-то добавить, и получить 1000 cell-ов и 100 МГц.

И дело не столько в характеристиках, сколько вот в этой... неупорядоченности, что ли. Да тот же Z80. Номер регистра берётся и из битов 0..2, и из битов 3..5, и от префиксов зависит, и исключения из нумерации, и младший бит отбрасывается, и чего там только нет. По флагам картина такая же. Кто сможет коротко описать как меняются флаги Z80, даже документированные? Это играйте, это не играйте, а здесь рыбу заворачивали. Давайте напишем case на 100 строк, синтезатор разберётся.

Поэтому я против аппаратных циклов. Они очень усложняют то, что уже есть. Дожать один такт, создав себе кучу проблем, и потом героически их решать? Нет, я сначала сделаю как могу, хоть 10 тактов на эту djnz, потом буду думать как ускорить.

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


а ядро сейчас в виде эмулятора или уже на fpga?
В виде эмулятора, с некоторыми допущениями, кое-что написано именно как программа, не как эмулятор. Эмуляция эмуляции :) не знаю как объяснить.

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

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

2 килобайта это тот самый микрокод, который для внутреннего ядра.

Lethargeek
01.02.2020, 15:39
какой именно? 1-й, 4-й, 11-й.
да практически любой, кроме микроконтроллеров-обрезков, лишь бы скорости хватило (с arm7* примерно?)


И разве разработчиков этих ядер волновала возможность реализации на них z80 и Spectrum-ов. Любое ненужное излишество это вентили, вентили, вентили.
а какая разница, что их волновало и сколько вентилей? нас должна цена с доступностью волновать

blackmirror
02.02.2020, 15:56
Допустим разрядность элемента массива равна разрядности слова банка, каждый банк имеет отдельную шину адреса и данных. Если мы поделим память на 4 банка и конкретный банк будут выбирать 2 младших бита адреса, то нет никаких проблем прочитать 4 подряд идущих элемента, начиная с любого банка. Если шаг между элементами будет кратен 2, к примеру в массиве лежат двумерные координаты, а обновить нам требуется только X, половина банков у нас будет простаивать, а другая трудиться в два раза дольше. Если в массиве лежат трёхмерные координаты, то обновить X можно без проблем задействуя все 4 банка, да и вообще проблем не будет со структурами из нечётного числа слов. А вот когда структура станет кратна 4м словам, то при обновлении X трудиться придётся одному банку, пока другие будут простаивать.

Решений здесь существует несколько:
1) Растащить поля структуры по отдельным массивам, в структуру поместить адреса этих массивов, чтобы по индексу можно было собрать нужную структуру из разных массивов.
2) Дополнить структуру до нечётного числа слов, что несколько затратно по памяти для мелких структур.
3) Самый интересный вариант - сделать больше банков, сохранив в целях совместимости количество шин.

Для последнего варианта можно поделить каждый банк на 5 более мелких(всего 20 банков), и добавить 5 ортогональных шин. Если младший бит шага между элементами нечётный, запросы идут по 4м шинам как обычно, а если чётный, значит их нужно перенаправить на ортогональные шины. Здесь правда возникает проблема, если шаг будет кратен и тому и другому, к примеру 10 или 20. В этом случае придётся вернуться ко 2 варианту, но затраты памяти здесь уже будут 10% или 5%, а не 50% как в случае структур из двух слов. Можно рассмотреть и схемы разбиения 8x9 или 16x17, но для них всех нужен быстрый алгоритм вычисления остатка при делении на числа вида 2^n+1, у кого какие соображения по этому поводу? Скорее всего это будет что-то типа признака делимости на 11 для десятичной системы, но насколько сложная схема получится для железа?

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

PS: В общем мысль про CRC пришла к выводу, что имеея 2^n банков достаточно разбить адрес на группы по n бит после чего их все проксорить. Таким образом можно будет параллельно обращаться к элементам с любым шагом и в среднем нагрузка на банки будет равномерной. Есть правда минус в том, что для каждого банка нужна очередь запросов, и при конфликтах время получения всех данных будет удлиняться. Плюс метода в том, что при изменении количества банков нужно только скорректировать количество параллельно обрабатываемых элементов, а переделывать размещение данных не потребуется.

Bolt
03.02.2020, 10:20
blackmirror, что-то я вообще не понял про что это.

blackmirror
03.02.2020, 20:39
Это про много ядер обновляющих поля структур в общем поле памяти из нескольких банков в случае если размер структур кратен суммарной ширине банков и конфликты которые при этом возникают. Правда рассуждать можно проще, если 2^n банков не позволяют эффективно разруливать конфликты при структурах кратных 2^s, значит банков нужно нечётное количество, к примеру 2^n+1. C crc связываться не стоит, там остаются размеры структур на которых всё будет плохо, самый действенный вариант это делать банков больше чем ядер, но очереди всё равно нужны.

omercury
03.02.2020, 21:35
плюшки типа 24-разрядной адресации.

"add BC,0x12345678" занимает 8 байт.
Как часто нужны числа более 16 миллионов? Для математики? А для индексов с 24-битной адресации не очень, посему они вполне могут быть одноцикловыми (4 байта) - например, старший байт - команда, а 3 младших - константа.


Инструкция запуска цикла адрес следующий инструкции записывает в LoopStat, а в LoopEnd и LoopCnt пишет переданные в параметрах значения. Когда PC становится равным LoopEnd мы уменьшаем счётчик и если еще не 0, то перезаписываем PC из LoopStart, иначе продолжаем выполнять код как обычно.
Простите, а что в это время делает процессорное ядро? Ждёт освобождения PC, ведь это же ядра епархия?
И какой тогда смысл в аппаратных циклах?
Или всё-таки что-то делает?
Тогда причём здесь PC ?
В народе этот узел называется DMA и к процессорному ядру никакого отношения не имеет.
Вот когда понадобится - тогда и добавится. :)


Когда на выходе PC, на входе PC+4.
Если прилетело прерывание - что сохранить в стек?
Дык захват прерывания происходит в самом конце выполняемой в данный момент инструкции, посему, разумеется, PC+длина инструкции (или что там будет результатом её выполнения).


А получится там 5000 cell-ов и частота 30 МГц.
А можно, увидев такое непотребство, как-то переделать, что-то выкинуть, что-то добавить, и получить 1000 cell-ов и 100 МГц.

Давайте напишем case на 100 строк, синтезатор разберётся.
32-битная команда будет поболее 100 строк...
Сииильно поболе.. :)

Но это тоже не тупик.
Кейсов ведь может быть более одного и работать они могут вполне себе параллельно.
Для примера потеоретизируем для однобитных сигналов.
Упрощённо, каждый логический блок имеет 8 входов + входы расширения, на каждый вход/выход стоит свой ключ, + коммутация сигналов внутри блока - тоже ключи, пусть для простоты будет 2 последовательных ключа, итого 4. Пусть каждый имеет задержку в 0,5 наносекунды. Для того, чтоб построить мультиплексор на 100 входов, нам нужно задействовать 100/8=12,5 (13) последовательно включённых блока, получив в результате задержку в 13*4*0,5=26 условных наносекунд.
Либо поставить, например, 5 двадцативходовых мультиплексоров впараллель и один, объединяющий их на выходе. Итого на круг получим 20/8=2,5 (3) + 1 = 4 последовательных ячейки по 4 ключа = 4*4*0,5=8 условных наносекунд или трёх с четвертью кратное ускорение мультиплексора... Расплата - "лишний" мультиплексор на выходе.
Чисто теоретически. :)
Усложнение не всегда ведёт к замедлению.
Но это тоже не догма.
Самым медленным устройством у нас является память (или УВВ, если угодно, не суть), она задаёт быстродействие, а внутрях оно может выполнять один МЦ и за несколько тактов, только небольшими кусочками, опять же по конвейеру, для пользователя это прозрачно. А это уже экономия ячеек.

blackmirror
03.02.2020, 21:54
Простите, а что в это время делает процессорное ядро? Ждёт освобождения PC, ведь это же ядра епархия?
И какой тогда смысл в аппаратных циклах?
Или всё-таки что-то делает?
Тогда причём здесь PC ?
Аппаратные циклы начинают свою работу на этапе выборки инструкции, когда PC выбираемой команды совпадает с последней командой цикла. Они работают параллельно с выполнением команд и не ждут окончания их выполнения, а сразу говорят, что далее выбирать команды следует с начала. Хотя для них могут быть ограничения в виде невозможности иметь последней командой цикла переход или вызов подпрограммы с непредсказуемым адресом. Иными словами который читается из памяти/вычисляется/зависит от вычисляемого условия. Смысл их в том, что для организации цикла не нужно выполнять декремент и переход, не нужно разворачивать циклы - можно писать максимально коротко не увеличивая нагрузку на кеш команд. С их помощью можно организовать цикл любой нужной длины, а не только в 1 команду из числа тех, что поддерживают такой префикс.

omercury
03.02.2020, 22:15
для них могут быть ограничения в виде невозможности иметь последней командой цикла переход или вызов подпрограммы с непредсказуемым адресом. Иными словами который читается из памяти/вычисляется/зависит от вычисляемого условия. Смысл их в том, что для организации цикла не нужно выполнять декремент и переход, не нужно разворачивать циклы - можно писать максимально коротко не увеличивая нагрузку на кеш команд. С их помощью можно организовать цикл любой нужной длины, а не только в 1 команду из числа тех, что поддерживают такой префикс.
Единственное "но" - неизвестно, сколько времени будет выполняться такой цикл, и, соответственно, почти гарантировано нарушение последовательности выполнения программы...
Вроде бы и по-сути DMA, но с червоточинкой...

blackmirror
03.02.2020, 22:22
omercury, выполняется это цикл столько, сколько в него засунули команд, а к dma он отношения не имеет.

Bolt
03.02.2020, 22:50
Это про много ядер обновляющих поля структур в общем поле памяти
А, понятно. То есть оптимизируем уже не ядро, и даже не два ядра, а МНОГО ядер :)

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


Как часто нужны числа более 16 миллионов? Для математики? А для индексов с 24-битной адресации не очень, посему они вполне могут быть одноцикловыми (4 байта) - например, старший байт - команда, а 3 младших - константа.А кто сказал, что циклы 4-байтовые? :) Про сокращённые константы и так понятно, просто указал предельный случай.

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


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

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

omercury, говоря "аппаратные циклы" blackmirror имеет в виду такую штуку, которая добравшись до определённого адреса далее выбирает не PC+1, а некий другой адрес. Не какая-то djnz, а само, сразу. Да, похоже на DMA, но заталкивает байты он не в звуковую карту, а в блок выполнения команд. Но эта штука лично у меня вызывает много вопросов.

omercury
03.02.2020, 23:08
Кажется, в арифметике ошибка, с логикой тоже не совсем согласен, но в целом мысль понятна.
Дык об уде и речь, рыбу никто не обещал. :)


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


Не какая-то djnz, а само, сразу. Да, похоже на DMA, но заталкивает байты он не в звуковую карту, а в блок выполнения команд.
Из описания как раз и понял, что "аппаратные циклы" как раз пропускают циклы программные, "пододвигая" в конвейере инструкции и распараллеливая процессы аппаратными примочками. Единственная клубничка на тортик - без средств разрешения конфликтов...

blackmirror
04.02.2020, 22:58
Пришла тут мысль, что регистровый файл может быть вообще из одного только счётчика команд. Все команды при этом состоят из 4х слов: операция, первый операнд, второй операнд, результат. Все операнды и результат представлены своими адресами, то есть фактически мы получаем доступ к переменной по её адресу. Память для кода и данных общая, то есть если нужно, правим и код программы. Правда требуется выделить особый адрес обозначающий обращения к счётчику команд, чтобы можно было делать переходы.
Идея вроде бы бредовая, но когда команды в конвейере ждут чтения своих операндов выглядят они примерно так же.

Bolt
18.02.2020, 04:50
Вопросы.

Первый.
По-моему я его уже задавал, но ответ не помню.
Почему rlc и rlc, имея в названии букву "c", прокручивают 8 бит, а rl и rr, не имеющие этой буквы, прокручивают 9 бит вместе с флагом переноса? Где логика?

Второй.
Флаги у Z80 конечно интересно устанавливаются, но в них есть скрытая система. Но вот cpl (инверсия аккумулятора) из этой системы выбивается, после неё перенос всегда должен быть установлен. Если кто в курсе, объясните почему она не меняет флаг переноса. В этом есть смысл? Или тоже "не баг, а фича"?

Третий.
Что если упростить эту систему флагов? Если, например, всегда будут меняться все флаги, смогут ли работать старые программы?

Lethargeek
18.02.2020, 12:49
Почему rlc и rlc, имея в названии букву "c", прокручивают 8 бит, а rl и rr, не имеющие этой буквы, прокручивают 9 бит вместе с флагом переноса? Где логика?
потому что c в данном случае значит cyclic


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


В этом есть смысл? Или тоже "не баг, а фича"?
наверно, быстрое получение -1 из 0 для операций типа reg+carry-1 или хитрых сдвигов каких-нибудь
ведь и установка уже есть, сбрасывающих перенос команд хватает и так


Что если упростить эту систему флагов? Если, например, всегда будут меняться все флаги, смогут ли работать старые программы?
всегда - это когда? ld тоже? :) но даже без неё работать точно не смогут, в частности, неполным влиянием inc/dec на флаги часто пользуются

Bolt
18.02.2020, 15:09
потому что c в данном случае значит cyclic

Ух, ё... Да, вот теперь логика есть.



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

Нет. Это не битовая операция, потому что установлен флаг N, а его устанавливает только вычитание. Это "0x00-A-carry", carry=1. И при этом она выбивается из системы, как будто у неё отдельная блокировка на изменение этого флага.



всегда - это когда? ld тоже? :) но даже без неё работать точно не смогут, в частности, неполным влиянием inc/dec на флаги часто пользуются

Нет, ld без флагов :) И про inc/dec тоже знаю.
В общем, не могу расставить инструкции для нового процессора. То ли там какой-то облом есть, который я не вижу, то ли мой перфекционизм мешает :)

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

Пример облома, который можно не заметить.
Вот тебе фигурки (типа тетриса), надо заполнить ими прямоугольник. На третий день раскладывания возникает мысль: а тупо по площади они туда умещаются или нет? :)

Lethargeek
18.02.2020, 15:26
Нет. Это не битовая операция, потому что установлен флаг N, а его устанавливает только вычитание.
ну, допустим... но тогда...


Это "0x00-A-carry", carry=1.
при чём тут carry? тогда уж 255-a (что, опять же, по идее должно сбрасывать перенос)

Bolt
18.02.2020, 15:47
тогда уж 255-a (что, опять же, по идее должно сбрасывать перенос)
Нет, потому что она устанавливает перенос H.

Lethargeek
18.02.2020, 15:51
Нет, потому что она устанавливает перенос H.
так это и при сброшенном C бывает

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

и раз один перенос уже работает нестандартно, тогда почему бы и не второй? (да и N, если уж на то пошло)

Bolt
18.02.2020, 16:36
0xFF - A
При каком значении A появится перенос h? Никогда.

0x00 - A - carry
При каком значении A появится перенос h? Всегда.

Lethargeek
18.02.2020, 19:02
0x00 - A - carry
еще раз: ну при чём тут carry? когда результат от его значения не зависит

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

и у тебя там ДВА вычитания, так результатом первого или второго должен быть H?

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

если первого, то при a=1 не появится, если второго, то появится только для a=0

blackmirror
18.02.2020, 19:03
Bolt, уж не помню что за зверь мне попадался, но у него сложение только устанавливало флаг переноса, и никогда не сбрасывало, а вот вычитание только сбрасывало и никогда не устанавливало. Были у него еще и команды сложения и вычитания с учётом переноса, но их логика была еще более странная для понимания. А еще там были однобитовые операции, если вы думаете, что они использовали флаг переноса, то облом - для них был предусмотрен отдельный флаг. Самое осмысленное, что он позволял делать это скопировать за две команды некоторый бит из одного регистра в другой. А вот если требовалось скопировать с инверсией, то это делалось только через какой-то хак, поскольку напрямую этот флаг инвертировать было нельзя.

Bolt
19.02.2020, 20:01
еще раз: ну при чём тут carry? когда результат от его значения не зависит

и у тебя там ДВА вычитания, так результатом первого или второго должен быть H?

У меня два, а в АЛУ одно.
Если подать на первый вход 0x00, на второй вход подать число, и в младший разряд ещё сигнал переноса "из ниоткуда", на выходе получим инвертированное значение и переносы во всех разрядах. Не знаю как именно это происходит в Z80.

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

blackmirror, меня старые процессоры вообще поражают своей "CISC-овостью", а x86 в этом плане уже никто даже не пытается догнать, все RISC что-то делают :)

Lethargeek
19.02.2020, 20:02
Если подать на первый вход 0x00, на второй вход подать число, и в младший разряд ещё сигнал
...то это нифига не равносильно дополнительному вычитанию единицы


ещё сигнал переноса "из ниоткуда"
в огороде бузина, а ПОСТОЯННЫЙ бит НИОТКУДА почему-то называется ПЕРЕНОСОМ :v2_dizzy_facepalm:

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

(потому что гладиолус, наверно)))

Bolt
19.02.2020, 22:03
...то это нифига не равносильно дополнительному вычитанию единицы
И чему же оно равносильно?

Lethargeek
19.02.2020, 22:52
И чему же оно равносильно?
как подсказывает нам кэп, вычитанию "n OR 1", которое равно самому же этому числу n при его нечётности

Smalovsky
19.02.2020, 22:53
Тема застопорилась на недоразумениях. Что скажет мэтр Ynicky?
За счёт чего вы будете вводить 32 битные коды операций? Специального префикса ( типа ld a,a)?
В AMD 64 за префикс взяли команду inc рег. Можно взять за префикс ненужную команду, а дальнейшие кодировку операций в корне поменять.
Я ж говорил, не надо точь в точь. Главное, чтобы программисту легче было переходить. Можно расширить аккумулятор до 32 бит, и сохранять его в стеке без флагового регистра. Флаговый регистр расширить до 32 бит, и то же отдельно от аккумулятора сохранять в стеке. Пары bc, de, hl сделать младшими словами 32 битных rbc, rde, rhll. Добавить ещё каких r4-r7. Добавить для контроля восьмибитных программ команды pushbc-pushhl-pushrw(сохранение слова из регистра),push alf( сохранение в стеке слова аналога af), ну и poprw соответственно.
Покажите свою регистровую модель.

Короче, надо делать разные системы команд для различных режимов. Вот механизм перехода из режима в режим. Процессор начинает работу в 32 битном режиме сразу. Если нужно перейти в восьмибитный режим, то устанавливает специальный бит перехода в 8 битную систему в флаговом 32 битном регистре и совержает jp. В восьмибитном режиме процессор выполняет только 8 битный код, расширение регистров отключено. Регистры b,c,c,e,h,a работают как независимые 8 битные регистры. Процессор работает в этом рожиме до спецкоманды trap32( например, ld b,b) и когда эта команда встречается, сохраняет 16 битный адрес возврата в стеке, переходит на специальный вектор прерывания, но уже в 32 битном режиме( флаг 8 битного режима сбрасывается), а оттуда можно уже заново дать ему задание в каком режиме дальше работать. Главное правило - системы команд режимов несовместимы! Процессор не может выполнять смесь команд из 8 и32 битных команд.

Bolt
20.02.2020, 00:33
Фантазия пока гуляет семимильными шагами, но на вопросы могу ответить.

За счёт чего вы будете вводить 32 битные коды операций? Специального префикса ( типа ld a,a)?
Абсолютно новый набор операций...
Главное, чтобы программисту легче было переходить.
...но более-менее совместимый на уровне ассемблера. В идеале хотелось бы вообще без изменений в исходном коде, но так не получится. Две основных причины - флаги (см. выше вопрос про упрощение) и 24-битная адресация.

Можно расширить аккумулятор до 32 бит, и сохранять его в стеке без флагового регистра.
Да.

Флаговый регистр расширить до 32 бит, и то же отдельно от аккумулятора сохранять в стеке.
Да, но тут пока жирный TODO.

Пары bc, de, hl сделать младшими словами 32 битных rbc, rde, rhll. Добавить ещё каких r4-r7. Добавить для контроля восьмибитных программ команды pushbc-pushhl-pushrw(сохранение слова из регистра),push alf( сохранение в стеке слова аналога af), ну и poprw соответственно.
Ну да, как вариант, основной набор и довесок для более точной трансляции старых исходников.

Покажите свою регистровую модель.
8 бит: W, A, B, C, D, E, H, L, J, K, IXH, IXL...
16/32 бита: WA, BC, DE, HL, JK, IX, IY, IZ...
Для 32 бит имена те же, но мнемоники с суффиксом, "ld.l BC,(DE)". Но можно и букву к имени регистра добавить.
BC, DE, HL, JK - двойные. IX, IY. IZ - индексные. WA - аккумулятор.

Короче, надо делать разные системы команд для различных режимов.
Два режима. Основной - 8/16/32 бита, совместимый с Z80 по исходному коду. Плюс режим эмуляции Z80/ZX. Или, если удастся, вместо второго режима динамическая трансляция кода.

Вот механизм перехода из режима в режим.
Ой, вот тут стоп, не надо меня сбивать с мысли :)
Дальнейшее описание в целом похоже. Только не "8 и 32", а "старый и новый". Чтобы не путаться, в новом же тоже есть 8 бит.
Ещё круче будет если удастся сделать динамическую трансляцию, тогда второй набор вообще не нужен.

omercury
20.02.2020, 20:24
Это "0x00-A-carry", carry=1.

тогда уж 255-a

0xFF - A
При каком значении A появится перенос h? Никогда.

0x00 - A - carry
При каком значении A появится перенос h? Всегда.

у тебя там ДВА вычитания

У меня два, а в АЛУ одно.

Пацаны, не поверите - НИ ОДНОГО...

Вот смотрите, например, 74хх83 - микросхема полного сумматора.
А назовите-ка пожалуйста партнумбер микросхемы ВЫЧИТАТЕЛЯ...
И для чего нужен дополнительный код?

Lethargeek
20.02.2020, 20:32
omercury, микросхемы нет, а операция - есть :p а я вообще про формулу говорил

omercury
20.02.2020, 20:44
а я вообще про формулу говорил
Да это понятно, что с тебя, погромиста, взять. :)


omercury, микросхемы нет, а операция - есть :p
Нету...

Ещё раз:
для чего нужен дополнительный код?

Lethargeek
20.02.2020, 20:58
Нету...
есть и производится над регистрами


Ещё раз:
еще раз: и с кодом есть, и без кода

omercury
20.02.2020, 22:18
есть и производится над регистрами
Вместо вычитания производится сложение с дополнительным кодом.

Вспоминай матчасть.

Lethargeek
20.02.2020, 23:47
Вместо вычитания производится сложение с дополнительным кодом.
это уже не с регистрами производится, а там внутре пусть хоть из таблицы выборку делает

omercury
21.02.2020, 07:33
это уже не с регистрами производится, а там внутре пусть хоть из таблицы выборку делает

Вот я и рассказываю не как это выглядит, а как происходит на самом деле.
Надеялся, что понимание процессов объяснит, наконец, "странное" поведение флагов.
Куда там!
Озарения не случилось...

Lethargeek
21.02.2020, 12:09
Вот я и рассказываю не как это выглядит, а как происходит на самом деле.
ну вот и пиши тогда, если знаешь, что на самом деле происходит внутре z80 (а не посторонних тараканов каких-нибудь)


Озарения не случилось...
ты не умничай, ты пальцем покажи (c)

omercury
21.02.2020, 17:56
ну вот и пиши тогда, если знаешь, что на самом деле происходит внутре z80 (а не посторонних тараканов каких-нибудь)

Lethargeek, ты смешной... :)
Так происходит во всех тараканах, включая Z80.

Lethargeek
21.02.2020, 18:00
Так происходит во всех тараканах, включая Z80.
КАК "так"? ты не говорил, как

omercury
21.02.2020, 18:29
КАК "так"? ты не говорил, как
.

Вместо вычитания производится сложение с дополнительным кодом.

Lethargeek
21.02.2020, 18:43
Вместо вычитания производится сложение с дополнительным кодом.
вычесть из нуля любое ненулевое число установит carry
сложить ноль с допкодом вообще любого числа - сбросит carry
прокомментируй

omercury
21.02.2020, 18:59
вычесть из нуля любое ненулевое число установит carry
сложить ноль с допкодом вообще любого числа - сбросит carry
прокомментируй


C is set if borrow; reset otherwise
Example: If the Accumulator contains 16H, the carry flag is set, the HL register pair
contains 3433H, and address 3433H contains 05H, at execution of
SBC A, (HL) the Accumulator contains 10H.
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=2ahUKEwi8x8PKgePnAhWHqIsKHXahAycQFjAAegQIAxAB&url=http%3A%2F%2Fwww.zilog.com%2Fforce_download.ph p%3Ffilepath%3DYUhSMGNEb3ZMM2QzZHk1NmFXeHZaeTVqYjI wdlpHOWpjeTk2T0RBdlZVMHdNRGd3TG5Ca1pnPT0%3D&usg=AOvVaw1l8KXafY1kqbsFXVh9LOOV
стр 170

05h+1(C) = 06h
neg 06h = FAh
16h+FAh=10h

Lethargeek
21.02.2020, 20:18
05h+1(C) = 06h
neg 06h = FAh
16h+FAh=10h
только вот СЛОЖЕНИЕ как в последней строчке должно устанавливать перенос
а в результате sbc (hl) он в реальности оказывается сброшен
стало быть, не пригодны твои формулы, пиши новые

omercury
21.02.2020, 20:30
только вот СЛОЖЕНИЕ как в последней строчке должно устанавливать перенос
а в результате sbc (hl) он в реальности оказывается сброшен
стало быть, не пригодны твои формулы, пиши новые

А самому подумать не судьба?
S и Z ведут себя абсолютно точно также, остальные тупо инверсные.

Lethargeek
21.02.2020, 21:45
остальные тупо инверсные
то есть от нормального сложения отличается

omercury
21.02.2020, 22:44
то есть от нормального сложения отличается

Дык код команды тоже отличается.


— Говорят, у вас жена сошла с ума? Что случилось?
— Мы поехали в горы. Там есть дивная долина, где чудесное эхо... Но жена привыкла, чтобы последнее слово оставалось за ней!

Lethargeek
21.02.2020, 22:49
omercury, так чего сказать-то хотел? и при чём тут флаги cpl?

omercury
21.02.2020, 23:23
[
при чём тут флаги cpl?
На самом деле там не только cpl выбивается из системы.


так чего сказать-то хотел?
Да вы так серьёзно рассуждали про вычитания, которых нет...
Ржачно уж больно. И так 3 дня терпел, всё ждал, кто первый вам линейкой по рукам шлёпнет. :)

Lethargeek
21.02.2020, 23:37
Да вы так серьёзно рассуждали про вычитания, которых нет...
да, а ты сейчас про что рассуждал? так и я могу выбрать достаточно малый участок схемы, на котором нет уже и сложения

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

лучше бы сюда (https://zx-pk.ru/threads/31401-obshchie-voprosy-emulyatsii-tr-dos-i-vg93.html?p=1047383#post1047383) уже заглянул и рассеял тьму невежества своим светом

Smalovsky
26.05.2020, 17:44
Bolt, как там дела с разработкой?))

Bolt
06.06.2020, 23:00
Пока отложена.
Было много сложных для меня моментов, потом окончательно запнулся на timing constraints (SDRAM нормально не работает) и решил что надо как-то отдохнуть, на улицу выйти погулять... Вышел. Погулял.

Bolt
26.07.2020, 09:10
Задумался о длине и кодировании инструкций.

Если инструкции переменной длины и попытаться читать инструкцию целиком за такт - схема выборки становится очень заковыристой.
Если инструкции по 2 байта - неплохо, но как кодировать 16/32-битные константы? Опять переменная длина?
Если по 4 байта - хорошо, можно кодировать инструкции Z80 "один в один", код при этом увеличивается примерно в 1,5 раза. Но операции вида "func reg,reg" занимают ну максимум 20 бит, и байт не используется. Чего б такого добавить, чтобы одной командой побольше действий выполнить?

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

Можно попытаться затолкать 2 штуки, и даже выполнять обе за цикл, но выигрыш будет только на какой-то аццкой арифметике, типа вычисления MD5. У вас в программах где-нибудь используются длинные последовательности из add/sub/and/xor/...?

HardWareMan
26.07.2020, 10:20
Вместо вычитания производится сложение с дополнительным кодом.
Не в курсе за Z80, но у ВМ80 именно так и есть:
https://jpegshare.net/images/d3/7f/d37f64bdb1f8f25eeaead0a8f09160f8.png
https://jpegshare.net/images/03/37/0337851b04288fb3c473fc4f4022382c.png

omercury
26.07.2020, 15:55
Не в курсе за Z80, но у ВМ80 именно так и есть:
Да оно везде так - никто в здравом уме не будет раскошеливаться на мифический "вычитатель" вместо дешёвой логической функции получения дополнительного кода.
Только разве в софтядрах по незнанию.

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


Задумался о длине и кодировании инструкций.
Если инструкции переменной длины...
Если инструкции по 2 байта...
Если по 4 байта...
Не жадничай, делай как в микроконтроллерах, память нонче недорога.


но как кодировать 16/32-битные константы?
А каков размер адресного пространства планируется?
Если меньше 4 гигабайт, то всё не так уж и плохо - скажем, если ограничится 1Tb(Хватит всем!(с)) то 2 старших разряда уйдут на кодирование инструкций загрузки аккумулятора (в пределах двух триллионов (например 1xxx.. - загрузка числа в аккумулятор).
Ещё 2 разряда можно отвести на инструкции типа CALL и JP (скажем 01xx... в этом случае 00xx... укажут на следующий тип инструкций). Или 3 разряда, если переходы и вызовы подпрограмм сделать только относительными, правда это не имеет смысла, так как нужно ещё задать направление относительно текущего адреса)).
А дальше уже по вкусу, в 28 бит влезет мнооого.

Bolt
26.07.2020, 16:45
А каков размер адресного пространства планируется?
16 мегабайт.


Не жадничай, делай как в микроконтроллерах,
Это как?

С остальными даже фантазировать не приходится, они в 32 бита укладываются, а вот "reg,reg" какая-то полупустая получается.

omercury
26.07.2020, 17:23
Это как?
Фиксированная длина инструкций.


16 мегабайт.
В предыдущем посте описан случай с 31 битом для констант и 30 битами для адресов (для 32-битных команд кратность адресации 4, доступ к памяти сохраняется побайтный - можно даже восьмибитку делать)))

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


Можно попытаться затолкать 2 штуки, и даже выполнять обе за цикл
Это уже фишки DSP, в данном случае не нужны особо, если интересно, посмотри например эти (https://www.analog.com/ru/products/adsp-2105.html#product-overview), достаточно простые и понятно расписаны.



а вот "reg,reg" какая-то полупустая получается.
Ну и пёс с ними, "лишними" битами. Зато логика работы намного проще.
Сейчас хард намного дешевле софта.
Побереги своё время.

Bolt
26.07.2020, 18:30
Побереги своё время.
Вот.

В общем нашёл решение. Там такой мелочёвки ещё набралось, можно сделать "суб-опкоды":
0x00 - nop
0x01 - func reg,reg
0x02 - ...
0x1? - func reg,imm16
0x2? - ...

Интересно, сколько в итоге займёт декодер?

omercury
26.07.2020, 19:05
Интересно, сколько в итоге займёт декодер?
Если его делать так

0x00 - nop
0x01 - func reg,reg
0x02 - ...
0x1? - func reg,imm16
0x2? - ...
то много.

Не с той стороны начал.
Разбей опкоды на биты, например:
{тип команды, код команды, источник, приёмник}
и получишь 3 декодера, работающих параллельно (по-факту не совсем параллельно, но значительно быстрей общего большого декодера, в котором будет полно длинных цепочек из элементарной логики). Да и поменьше он будет.

UPD
Помнишь ограничение в ~50МГц для t80/tv80 ?
причина как раз в этом.

Bolt
26.07.2020, 19:49
С той, с той :) Это и есть "тип команды, код команды"...

omercury
26.07.2020, 19:58
С той, с той :) Это и есть "тип команды, код команды"...

Мне понадобился всего один бит кода команды, чтоб загрузить

func reg,imm16, только imm31

Впрочем, дело твоё

Bolt
26.07.2020, 20:16
А 32-битный BC как загружать, тоже через аккумулятор? Две инструкции, 31 бит загружен, ещё и аккумулятор испорчен. А есть ещё 16-битный BC.

У меня грузятся отдельно нижние 16 бит, отдельно верхние, причём это не только ld, но и add, sub, xor... То есть 32 бита add это 16-битные add и adc, с коррекцией флагов. Или вообще один add, если 32 бита можно упаковать в 16. И эта же инструкция для 8 бит, только один байт не используется. И эта же инструкция добавлением одного бита превращается в bit, set, res любой разрядности 8/16/32. И inc/dec туда же.

omercury
27.07.2020, 00:27
А 32-битный BC как загружать, тоже через аккумулятор? Две инструкции, 31 бит загружен, ещё и аккумулятор испорчен. А есть ещё 16-битный BC.
А 32-битная организация мешает в Cortex-ах использовать 16-битные инструкции thumb?

Я просто предложил вариант, как говорится, не нравится - не ешь. :)

Bolt
28.12.2020, 00:51
Вот смотрю я на дату последнего сообщения в теме и думаю - что я делал эти 5 месяцев?!

andrews
28.12.2020, 02:06
Возможно что-то более необходимое для себя.

Bolt
28.12.2020, 10:53
Да вот именно, что всё такое нужное, а по итогу спроси что делал - и ответить нечего. Надоела уже эта чехарда приоритетов, "камень-ножницы-бумага".

andrews
28.12.2020, 11:01
Жизнь многозадачна, главное стек и память вовремя сохранять.

Bolt
30.12.2020, 11:02
Я скоро в BSoD выпаду с такой многозадачностью.

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

Задам вопрос пока в этой теме.
Кто-нибудь занимался двоичной трансляцией с Z80, и трансляцией вообще? Вот так чтобы код на лету транслировать и оно выполнялось на другом процессоре.

andrews
30.12.2020, 11:32
Это уж скорее конвертация. И в чем отличие от минималистического эмулятора, который только конвертит машинный код эмулируемого проца в последовательность кода исполнителя?

Bolt
30.12.2020, 12:29
Так я такой эмулятор и имею в виду. Надо же как-то эмулировать Z80.

Добавлять инструкции к Z80 - нет.
Эмулятор-интерпретатор - медленно.
Добавить аппаратный декодер старых инструкций - можно (см. ниже)

Пока думаю о двух вариантах:
Первый - трансляция в "нативные" инструкции и последующее выполнение, но тут много пока непонятных для меня вещей, типа самомодифицирующегося кода.
Второй по-моему вообще наркоманский - инструкция "выполнить содержимое регистра как инструкцию Z80". Если сделать неполный декодер, то он получится очень маленький и простой, остальное выполнять "эмулятором-интерпретатором". Основной цикл при этом будет "loop: ld A,(PC) ; exec A ; jp loop"

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

Или декодер не аппаратный, а программный, а из аккумулятора выполнять что-то типа микрокода.
Почему именно так - чтобы эмулятор в кэш поместился, иначе всё замедлится в разы. И в то же время не всё аппаратно, чтобы поместилось в мелкую ПЛИС. Сейчас то что есть занимает 2194 LE, но надо экономить, там ещё графика впереди.

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

А кроме графики - MMU и кэш.

andrews
30.12.2020, 14:46
Наверное согласишься, что новый проект должен давать что-то отличное от существующих и желательно конечно в лучшую сторону. А там как хочешь разбить задачу. Если делаешь более совершенный проц чем ASICи z80 это одно, а если сразу систему из которой выйдут более совершенные Спектрумы это другое. Я уже не ориентируюсь насколько далеко ушли замыслы от первоначального сделать проц, на котором без проблем работает код Паскаля.

Bolt
30.12.2020, 15:18
"Эта ваша музыка. Даёт она что-то? Нет, не даёт. А надо, чтобы давала." :)

andrews
30.12.2020, 15:52
Если делаешь хит. Ну а для души сам себе критик. Я вот за всю жизнь только пару мелодий своих успел своих записать, а в голове звучало их тысячи. Да еще и оркестрованных.

Bolt
12.01.2021, 20:12
Вопрос по адресации.

Регистры 32 бита, в адресе учитываются только 24.

(AA,BC,DE,HL,IX,IY,IZ + offset) - это понятно.

(AA,IX,IY,IZ + AA,BC,DE,HL) - нужен ли тут ещё и offset, или можно обойтись без него?

Что бы ещё придумать? Или этого достаточно?

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

И ещё: нужны ли альтернативные пары регистрам IX/IY/IZ? Которые "exx".

Bolt
22.01.2021, 01:40
Есть проблема с флагами.

Некоторые инструкции выбиваются по флагам из общей квадратно-гнездовой картины, например:
rlca, rrca, rla, rra (H,N,C)
cpl (H,N)
add HL,reg16 (H,N,C, причём H берётся из старшего байта)
inc/dec 8/16 бит

С inc/dec вообще интересно. 8-битные меняют все флаги кроме переноса, 16-битные флаги не меняют. Я понимаю почему так, но с этим надо что-то делать.

Пока мысль такая: они устанавливают флаги по-своему, но у ассемблера будет режим, в котором он после этих инструкций будет добавлять инструкцию, устанавливающую флаги как в Z80, для совместимости со старым исходным кодом.

Smalovsky
30.12.2021, 17:50
Что-то топик заглох...
Куда пропали Bolt, Ynicky, Vlad?
С Владом история вообще интересная. У него был четырехядерный процессор собственной разработки, на котором Влад сделал суперспектрум с уникальным видеорежимом высокого разрешения. Влад продемонстрировал возможности своего видеорежима в одном из топиков, где были скриншоты варика с разрешением и графикой писи.
Владу нужно поделиться деталями своего суперпроцессора с другими. Вот мне, например, интересно, расширял ли он адресацию до 24 бит?