PDA

Просмотр полной версии : Вопрос по ассемблеру Z80



Zidane
05.01.2022, 22:28
В GENS есть такой оператор DEFB. Ну так вот, у меня в них многа-многа значений (коды кириллицы). А вот как мне их в программу вытащить, при условии что метка на поле называется KRUS? В принципе в ПЗУ есть процедура вывода строк, но это же совсем не то. В принципе, переход на другой ассемблер некритичен, если это облегчит задачу. Но хотелось бы знать, как это все считать. Ну а загрузить я уж и сам смогу. Спасибо

reddie
05.01.2022, 23:08
Что требуется-то, не совсем понятно. Напечатать текст? Нужен будет, как минимум, фонт с кириллицей, причем соответствующий кодировке набитого текста. Далее меняем адрес фонта в системной переменной и вызываем ПЗУ.
Если же нужно сохранить этот текст в файл, чтобы затем где-то использовать - просто ассемблируем, а затем из Бейсика сохраняем кусок памяти с полученными данными. Начальный адрес и длину определяем по меркам, например.

Zidane
05.01.2022, 23:24
Ниеть. Нужно загрузить набор однобайтных чисел в память компьютера из DEFB, метка на массив - KRUS. Я так понял, метку надо загрузить в DE, в HL - начальный адрес загрузки и инкрементировать его по мере загрузки данных. Но не работает. На самом деле какого-то фиксированного кода нет - в процессе эксперимента. Я так спросил, может знает кто.

Первые шаги в асме, многого не знаю еще... Сейчас код выглядит следующим образом:
PUSH HL
LD B,4
LD DE, CODE
LD HL, #FB00
LOAD LD A, (DE)
LD (HL), A
INC HL
DJNZ LOAD
RET
CODE DEFB #C6,#6C,#38,#38

Основной код загрузки у меня выглядит так. Проблема в том, что не происходит перебора значений блока CODE - во все четыре ячейки памяти грузится одно число - #C6

goodboy
05.01.2022, 23:26
ld de,addr
ld hl,krus
ld bc,lenght
ldir

Reobne
05.01.2022, 23:52
не происходит перебора значений блока CODE
Из за того, что забыл написаль INC DE в цикле.

Zidane
05.01.2022, 23:54
Из за того, что забыл написаль INC DE в цикле.

Я не забыл. просто такой маневр привел GENS к краху. Странный редактор. Просто привычный. А может я сам где ошибся. Но в любом случае у меня эта попытка провалилась. На всякий случай счас еще раз попробую. Вполне мог забыть HL из стека вытащить, если это на что-нибудь влияло. В любом случае спасибо.

Вариант от GoodBoy сработал. Спасибо всем огромное. Буду использовать его, потому что короче выходит.

Bedazzle
06.01.2022, 00:35
У меня другой вопрос - почему именно Gens?

goodboy
06.01.2022, 00:38
LD HL, #FB00
LOAD LD A, (DE)
LD (HL), A

держи в уме по каким адресам находится сам Gens (вдруг ты его код своей программой затираешь)

Zidane
06.01.2022, 04:59
У меня другой вопрос - почему именно Gens?

Потому что когда я активно интересовался ассемблером, а именно 2006 год, то тогда только эта литература (а следовательно и хоть какая-то справка на редактор) и были мне доступны. Правда тогда, в силу изучения Python, ассемблер вскоре был отложен до лучших времен. Но к GENS за пару месяцев успел кое-как приспособиться.


держи в уме по каким адресам находится сам Gens (вдруг ты его код своей программой затираешь)
Ну загружаю я его ровно так как описано в одной известной книге, с адреса 25000. Даже если сам он, в варианте 51 символ в строке занимает 11 кб, то, как я прикинул, выше #8Е28 проблем бы ть не должно.

reddie
06.01.2022, 07:30
Всё же Генс не лучший выбор для реала (ну или эмуля). Небольшие программки можно и в нём, но желательно побыстрее перелезть на что-то посерьезней. Тасм, Аласм, много чего...
А приведённая в начале прога вообще не должна работать, т.к. стоит Push HL и затем после "цикла" делается Ret. В случае же с крахом Генса стоит ещё стек проверить. Мало просто загружать его по такому-то адресу, еще и Clear должен соответствовать.

goodboy
06.01.2022, 10:04
В случае же с крахом Генса стоит ещё стек проверить. Мало просто загружать его по такому-то адресу, еще и Clear должен соответствовать.
ну это скорее всего загрузчик сам делает.

reddie
06.01.2022, 10:25
это скорее всего загрузчик сам делает
Да, но в Генсе, насколько помню из своего опыта (тоже с него начинал), система распределения памяти иная, нежели в привычных нам асмах для 128К и выше машин.
Вернее, его там просто нет. Грузится Генс, следом будет текст программы, таблица меток и прочее. Никакого деления на странички, что и понятно - прога-то под 48-й Спектрум.
Поэтому не стоит делать, как писал автор темы: "выше #8Е28 проблем быть не должно" - если менять память сразу за ассемблером, гарантированно запорется исходник.

Zidane
06.01.2022, 11:39
Да, но в Генсе, насколько помню из своего опыта (тоже с него начинал), система распределения памяти иная, нежели в привычных нам асмах для 128К и выше машин.
Вернее, его там просто нет. Грузится Генс, следом будет текст программы, таблица меток и прочее. Никакого деления на странички, что и понятно - прога-то под 48-й Спектрум.
Поэтому не стоит делать, как писал автор темы: "выше #8Е28 проблем быть не должно" - если менять память сразу за ассемблером, гарантированно запорется исходник.

Опираясь на одну книжку (правда по BASIC) записал с адреса 64256 (после CLEAR 64255) и в 23607,250. И в 48-м бейсике все работает. А вот в 128 нет. Из железа только эмулятор. Ну и Ленинград тоже 48-й. Не рабочий ))
Итого - Файл без расширения, штука для Fuse. Сродни дампу памяти, как я понял. А все потому что Fuse в стандартный sna сохранять не хочет. А тапок - это то что получилось в результате симуляции записи на кассету. Грузится следующим набором команд:
CLEAR 64255
LOAD "KEYRUS" CODE 64256

POKE 23607,250 - кириллица
POKE 23607,60 - латиница

Возможно я просто пользуюсь слишком устаревшей литературой. Что касается редактора ассемблера, то готов освоить любой, лишь бы он с кассетой умел работать. Воть. Раритет я.
https://ibb.co/Wy2tmfb

Тапок https://disk.yandex.ru/d/F4S3leDAui_DYA
Дамп FUSEhttps://disk.yandex.ru/d/Fl9U9qRQB81mLQ

P.S. если дамп загрузить в FUSE и набрать RANDOMIZE USR 25000, запустится собственно GENS c исходником

Bedazzle
06.01.2022, 11:39
Но к GENS за пару месяцев успел кое-как приспособиться.

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

https://hype.retroscene.org/blog/dev/946.html
https://github.com/z00m128/sjasmplus
https://pasmo.speccy.org/
https://dotneteer.github.io/spectnetide/

Zidane
06.01.2022, 11:46
Если нет сильной ностальгии разрабатывать именно на реале, лучше перебраться на компиляцию чем-то поновее.

https://hype.retroscene.org/blog/dev/946.html
https://github.com/z00m128/sjasmplus
https://pasmo.speccy.org/
https://dotneteer.github.io/spectnetide/
Спасибо. Меня сейчас интересует чтобы работало в 64/128. Дальше пока не заглядываю, я еще не настолько Z80 знаю. )))

reddie
06.01.2022, 12:01
Меня сейчас интересует чтобы работало в 64/128. Дальше пока не заглядываю
Там выше все, что насоветовали, относится именно к написанию на Писюке. В чем-то оно и лучше, да. Но для реала (или в эмуле) желательно все-таки съехать с Генса =)
В 128-м режиме похитрее работа с памятью, особенно при участии Бейсика. Надежней сбросить в режим "48 с открытым портом" через USR 0, там проблем быть не должно, а 128К останется.
И если целью было просто создать фонт - это можно сделать даже из Бейсика, массивом DATA. Вбить в память через POKE, сохранить файл как кодовый блок - и готово.
Насчет кассет чуток замороченней, но почти любой эмулятор умеет перехватывать обращения к ленте, тут проблемы тоже решаемы. Но раз железного Спека нет - смысл зацикливаться на ленте?

Zidane
06.01.2022, 12:10
Задача стояла разобраться как и что грузится. Шрифт просто подвернулся в качестве цели.

Zidane
06.01.2022, 18:16
Попытался загрузить тот же шрифт через Бейсик.. В 128-м та же фигня, так что дело может и не в GENS. Хотя странно все это.... В общем в цикле неизбежно вылезает ошибка. Точно такая же программа перебитая в 48 грузится без проблем.

Bedazzle
07.01.2022, 00:05
Спасибо. Меня сейчас интересует чтобы работало в 64/128.

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


Дальше пока не заглядываю, я еще не настолько Z80 знаю. )))

Все мы учимся понемножку. :)

goodboy
07.01.2022, 10:23
в 48-м бейсике все работает. А вот в 128 нет
128ой бейсик это скорее надстройка к 48ому.
когда ты работаешь в 128ом редакторе практически всегда включена 7ая страница (там находятся всякие служебные буфера),
а твой шрифт грузится в нулевую.
при выполнении программы всё встаёт на свои места.

https://pic.maxiol.com/thumbs2/1641540144.1410805916.rusfont.png (https://pic.maxiol.com/?v=1641540144.1410805916.rusfont.png&dp=2)

и размер шрифта у тебя странный. обычно 96*8=768
https://pic.maxiol.com/thumbs2/1641540519.1410805916.artstudio.png (https://pic.maxiol.com/?v=1641540519.1410805916.artstudio.png&dp=2)

Zidane
07.01.2022, 20:06
128ой бейсик это скорее надстройка к 48ому.
когда ты работаешь в 128ом редакторе практически всегда включена 7ая страница (там находятся всякие служебные буфера),
а твой шрифт грузится в нулевую.
при выполнении программы всё встаёт на свои места.

https://pic.maxiol.com/thumbs2/1641540144.1410805916.rusfont.png (https://pic.maxiol.com/?v=1641540144.1410805916.rusfont.png&dp=2)

и размер шрифта у тебя странный. обычно 96*8=768
https://pic.maxiol.com/thumbs2/1641540519.1410805916.artstudio.png (https://pic.maxiol.com/?v=1641540519.1410805916.artstudio.png&dp=2)

А как его загрузить то в 128? Ну любопытно просто. Да и шрифт не мой - взял из книжки как написать игру для ZX Spectrum на бейсик. Только косяки исправил и в 16-ричный вид перевел. Просто чтобы в GENS удобнее выглядело. Там, в разрешении 51 символ в строке, после DEFB как раз до края строки 8 однобайтовых значений влезает. Правда для комментария места уже не остается. Но это так, лирика. К слову с размером шрифта я действительно мог напортачить, но вроде работает все как задумано....

reddie
07.01.2022, 20:34
А как его загрузить то в 128?
Попробовать грузить ниже страничек (#C000), ну и CLEAR переставить соответственно. Хотя он, вроде, и так внизу.

Zidane
07.01.2022, 20:38
Попробовать грузить ниже страничек (#C000), ну и CLEAR переставить соответственно. Хотя он, вроде, и так внизу.

И соответственно смещение пересчитать для POKE. Понял, спасибо, буду пробовать/

Пасяба, все запустилось.
CLEAR 29999
LOAD "" CODE 30000
10 POKE 23606,0: POKE 23607,60: REM LAT
20 POKE 23606,48: POKE 23607,116 :REM RUS.

Zidane
08.01.2022, 08:05
Еще вопрос - где достать карту памяти Specrum128? Ну или хотя бы внятное описание, как он переключает страницы и что при этом происходит. Я и сам ищу, но что-то пока никак.

LW
08.01.2022, 09:00
Хотябы тут (https://vtrd.in/book/ZX_TRDOS.ZIP) с 80й страницы.
И вообще на vtrd.in (https://vtrd.in/book.php) много полезной литературы. Почитайте.

goodboy
08.01.2022, 10:35
поскольку для 8бит #FFFF физ.предел на спеке128 через запись в порт подменяется часть памяти в диапазоне #C000-#FFFF.
на разных клонах память можно менять и более гибко #0000,#4000,#8000,#C000

reddie
08.01.2022, 11:37
Еще вопрос - где достать карту памяти Specrum128? Ну или хотя бы внятное описание, как он переключает страницы и что при этом происходит. Я и сам ищу, но что-то пока никак.

Всё изменения через порт #7FFD. Переключается верхний банк ОЗУ (16КБ) #C000-#FFFF, а также банк ПЗУ (с 0 и до экрана) между 48 (#10) и 128 (0), то бишь 4-й бит это номер ПЗУ. Также можно переключать отображаемый экран: 5-я банка либо 7-я, физически в карте ничего не меняется, но видеоконтроллер отображает одну из них как экран. Задаётся битом 3 в том же порту. Соответственно, Бейсик-48 и "стандартный" экран это число #10, а Бейсик-128 и второй экран - число 8 (хотя в реальности так никогда не делают).
Блок порта производится установкой бита 5 (#20), после чего изменения невозможны до сброса компа. Разумеется, одновременно можно записать и номера нужных страниц ОЗУ-ПЗУ-экрана, но это будет последняя запись, и работа системы будет нарушена после такого приема, если работал Бейсик-128, либо 48-й со стеком (Clear) #C000 и выше. Ну, и смена экрана на 7-ю банку означает работу вслепую, т. к. Бейсики понятия не имеют о втором экране - он используется только запускаемыми программами.
И ещё один момент: каждое прерывание Бейсик-128 щёлкает страницами. Вот отсюда и идут все проблемы с загрузкой данных выше #C000, а также с попытками управлять портом, пока жив этот 128-й Бейсик. Проще сразу делать USR 0, получая режим "48К с открытым портом", ни на какие игры и софт это не повлияет. Наоборот, некоторые даже заработают, хотя в "чистом" 128-м режиме не запускаются.

Zidane
12.07.2022, 18:43
Здравствуйте. Нуб в очередной раз в тупике. Суть вот в чем: Есть набор значений адресов (не подряд но с постоянным смещением) в которые нужно загрузить число. Если проще - распихать байты цветности по местам. Так вот, как бы мне этот набор адресов последовательно перебрать? Или практичнее будет использовать цикл, увеличивая значение адреса на величину смещения? В последнем случае программа обещает быть поменьше (начальный адрес ведь всего один), но и первый вариант интересует - вдруг пригодится.

reddie
12.07.2022, 19:26
Или практичнее будет использовать цикл, увеличивая значение адреса на величину смещения?
Все зависит от предпочтений: или цикл, но помедленней, или развертываем код, занимая память, но выигрываем в скорости.
Более-менее универсальный вариант с переменным смещением:



А=записываемое число, HL=нач. адрес записи, DE=приращение (шаг)
если повторов 256 или меньше:

MET LD (HL),A ; байт
ADD HL,DE
DJNZ MET

если больше 256 значений:

LD BC,256*.SUM+'SUM ; счетчик "наоборот"
MET LD (HL),A
ADD HL,DE
DJNZ MET
DEC C
JR NZ,MET

Zidane
12.07.2022, 21:48
Все зависит от предпочтений: или цикл, но помедленней, или развертываем код, занимая память, но выигрываем в скорости.
Более-менее универсальный вариант с переменным смещением:



А=записываемое число, HL=нач. адрес записи, DE=приращение (шаг)
если повторов 256 или меньше:

MET LD (HL),A ; байт
ADD HL,DE
DJNZ MET

если больше 256 значений:

LD BC,256*.SUM+'SUM ; счетчик "наоборот"
MET LD (HL),A
ADD HL,DE
DJNZ MET
DEC C
JR NZ,MET


Спасибо за развернутый ответ. Скорость в данном случае вторична. Программа носит самообразовательный характер

Zidane
22.07.2022, 19:41
Доброго времени суток форумчане. Залип не по детски. Есть до смешного простой код:

ORG $7530
LD A,(25000)
LD HL,25001
SUB A,(HL)
LD (25004),A
RET

И не работает... Вместо SUB ставим ADD, ADC или SBC - все отлично, вот при указанном раскладе неизменно получаю сообщение "Invalid combination of opcode and operators". перерыл все справочники какие нашел - есть такая команда. А ассемблер говорит что нет. Вот и втыкаю в формате "то ли лыжи не едут..." ну и далее по тексту. Причем ошибку выдает как в современных средствах разработки, так и в древнючем GENS... При написании руководствовался вот этим вот материалом https://sysadminmosaic.ru/msx/assembler_programming_guide-fakhrutdinov_bocharov/11, раздел 8-битной арифметики. Кстати пробовал указывать числа только в 16-ричном формате, все равно не работает.

ivagor
22.07.2022, 20:03
sub (hl)

jerri
22.07.2022, 20:11
Доброго времени суток форумчане. Залип не по детски. Есть до смешного простой код:

ORG $7530
LD A,(25000)
LD HL,25001
SUB A,(HL)
LD (25004),A
RET

И не работает... Вместо SUB ставим ADD, ADC или SBC - все отлично, вот при указанном раскладе неизменно получаю сообщение "Invalid combination of opcode and operators". перерыл все справочники какие нашел - есть такая команда. А ассемблер говорит что нет. Вот и втыкаю в формате "то ли лыжи не едут..." ну и далее по тексту. Причем ошибку выдает как в современных средствах разработки, так и в древнючем GENS... При написании руководствовался вот этим вот материалом https://sysadminmosaic.ru/msx/assembler_programming_guide-fakhrutdinov_bocharov/11, раздел 8-битной арифметики. Кстати пробовал указывать числа только в 16-ричном формате, все равно не работает.

77636

Zidane
22.07.2022, 20:15
Уже подсказали, но большое спасибо за ответ.

Zidane
23.07.2022, 18:43
Доброго времени суток. Нубу снова не спится. Озадачился вопросом, можно ли передавать 2-байтные значения из бейсик в ассемблер. И вообще, возможно ли это. В разного рода книжках вроде обозначена область VARS, но она, как я понял, используется для хранения переменных самого интепретатора, а мне надо бы то, что пользователь ввел.

Bedazzle
23.07.2022, 18:45
можно ли передавать 2-байтные значения из бейсик в ассемблер

POKE addr, value1
POKE addr+1, value2

...

ld hl, (addr)

Zidane
23.07.2022, 18:49
POKE addr, value1
POKE addr+1, value2

...

ld hl, (addr)
То есть нужно предварительно пересчитать число в 16-ричный формат?

reddie
23.07.2022, 19:08
То есть нужно предварительно пересчитать число в 16-ричный формат?

В вышеприведенном примере - да. Но есть способ попроще, хотя с некоторыми ограничениями.
Через переменную SEED Бейсика (RND- генератор). Командой Randomize NN (до 65535) заносим туда число, а в кодах забираем оттуда значение: LD RP, (23670), где RP - нужная регистровая пара, 23670/23671 - два байта числа.
Ограничения, думаю, понятны: при запуске кодов через Randomize Usr значение сменится по возврату в Бейсик. Обычно удобней запускать командой вида LET X=USR NN, по возврату X будет равно значению рег. пары BC, то есть можно передавать и обратно из кодов в Бейсик.

Reobne
23.07.2022, 20:14
Есть ещё такой метод: "DEF FN A(......) = USR addr - для работы с числами; (http://zxpress.ru/book_articles.php?id=1091)"
Бейсик, передаёт пятибайтовые числа, но когда они целые от 0 до 65535, то их легко выделить из этих пяти байтов.

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

Вот тут (https://zx-pk.ru/threads/11422-versii-igr-i-softa-najdennye-na-kassetakh.html?p=1150326&viewfull=1#post1150326) я этот метод использовал.

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

Что-то я какие-то трешовые примеры накидал.
Сейчас более чистый примерчик дам.
В бейсике пишем:

10 DEF FN a(x,y)=USR 40000
20 CIRCLE 80,80,75
30 RANDOMIZE FN a(16384,16384+256*8)

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


ORG 40000
LD HL,(5C0BH)
LD BC,4
ADD HL,BC
LD E,(HL)
INC HL
LD D,(HL) ; В DE взяли первый аргумент
LD C,7
ADD HL,BC
LD A,(HL)
INC HL
LD H,(HL)
LD L,A ; В HL взяли второй аргумент
LD BC,800h
LDIR
RET

Когда всё это дело запускаем, рисуется кружочек, а потом из средней трети экрана всё копируется в верхнюю треть.

Zidane
23.07.2022, 21:05
В вышеприведенном примере - да. Но есть способ попроще, хотя с некоторыми ограничениями.
Через переменную SEED Бейсика (RND- генератор). Командой Randomize NN (до 65535) заносим туда число, а в кодах забираем оттуда значение: LD RP, (23670), где RP - нужная регистровая пара, 23670/23671 - два байта числа.
Ограничения, думаю, понятны: при запуске кодов через Randomize Usr значение сменится по возврату в Бейсик. Обычно удобней запускать командой вида LET X=USR NN, по возврату X будет равно значению рег. пары BC, то есть можно передавать и обратно из кодов в Бейсик.

То бишь примерно так?

10 INPUT x ;Вводим нужное двухбайтное число
20 LET y=USR 30000 ;Указываем адрес старта подпрограммы ассемблер
30 RANDOMIZE x ;Помещаем число по адресам 23670/23671
40 RANDOMIZE y ;Вызываем подпрограмму ассемблера

? У меня сбрасывается бейсик. Мож я чего не так понял?
На всякий случай ниже код ассемблера который я использую. Решил пока сделать вывод на экран


ORG $7530
LD A,2
CALL 5633

LD HL,(23670)
LD DE,1
SBC HL,DE

LD B,H
LD C,L
CALL 11563
CALL 11747
RET

reddie
23.07.2022, 22:19
Строки 20 и 30 поменять местами и убрать строку 40, она бессмысленна.
То бишь сперва заносим число в SEED, а потом уже вызываем код.
Возможны ещё баги в кодовом блоке, уже забыл, что делают те две процедуры.

Reobne
23.07.2022, 22:33
А работать оно всё равно должно. Если сбрасывается, то дело техническое, испортился бейсиком машкод, не та версия бейсика или ещё что-то такое.

И ещё, вместо:
LD DE,1
SBC HL,DE

лучше писать
LD DE,-1
ADD HL,DE

Так короче, быстрее, и не влияет флаг C

Zidane
24.07.2022, 06:08
А работать оно всё равно должно. Если сбрасывается, то дело техническое, испортился бейсиком машкод, не та версия бейсика или ещё что-то такое.

И ещё, вместо:
LD DE,1
SBC HL,DE

лучше писать
LD DE,-1
ADD HL,DE

Так короче, быстрее, и не влияет флаг C

Спасибо за уточнение. Но в данном случае пока разбирается математика без флагов. Флаги разбирать буду отдельно.

reddie
24.07.2022, 08:52
ORG $7530
LD A,2
CALL 5633

LD HL,(23670)
LD DE,1
SBC HL,DE

LD B,H
LD C,L
CALL 11563
CALL 11747
RET

Глянул талмуд - это вывод числа из стека калькулятора. Но зачем так много лишних инструкций?

LD A, 2
CALL 5633
LD BC, (23670)
DEC BC ; уменьшение на 1, если оно надо
CALL 11563
JP 11747

Black Cat / Era CG
24.07.2022, 10:16
А разве не портит калькулятор HL'? Может, потому и глючит на выходе в Барсик.

reddie
24.07.2022, 11:02
А разве не портит калькулятор HL'?
Насколько помню, нет. В талмуде, по которому тогда учил асм, про сохранение HL' расписано только в рисовании линий и колец.

Reobne
24.07.2022, 14:01
Но в данном случае пока разбирается математика без флагов. Флаги разбирать буду отдельно.
Команда
SBC HL,DE
Отнимает не только DE, но ещё и флаг переноса C, он же CF.
То есть, если вдруг флаг C будет установлен, то
LD DE,1
SBC HL,DE
Отнимет не один, а два!

reddie
24.07.2022, 14:22
Отнимет не один, а два!
может, так и задумано =) иначе сама конструкция лишена смысла, проще уменьшить пару на единицу.

Zidane
24.07.2022, 19:29
Господа ))) Не нужно столько всего. Просто тихо мирно готовится ролик про математику Z80 без флагов. Вот прям совсем для таких как я. И вот предположим, в целом я действие флагов понимаю (уже успел почитать) но вот человек который первый раз пытается кодить на ассемблере, вы думаете он будет разбираться? Вспоминая себя лет 7 назад могу точно сказать что нет. Новичку нужно дать пару новых инструкций и как то заставить выучить их "на зубок", а потом уже расширять базу знаний. То бишь материал надо подать с минимально необходимым количеством новой информации - ровно столько, чтобы хватило понять следующий материал. А вы мне тут и про флаги, и про JP и вообще в дебри математики ушли. )) Я вам весьма признателен за помощь, даже не сомневайтесь, но сейчас её слишком много ) Флаги и условные/безусловные переходы это планируемая тема отдельного выпуска. А вот конкретно сейчас минимум - INC DEC ADD, SUB SBC (ну просто для 16-битного числа опкода SUB нет, насколько я понял). Отсюда и все сложности с регистрами и работой с калькулятором и процедурами ПЗУ. Это то, что запомнить можно ну вот прям в один присест. Если человек готов разбираться в этом, он, без сомнения, раскопает все, что вы сказали. Но если ковыряет только в порядке эксперимента, то все это лишнее. Главное стабильность работы.
Еще раз спасибо за помощь нубу, который делает ролики для еще больших нубов чем он сам )

Кстати есть подозрение что глючило как раз из-за пары команд LET x=USR 30000: RANDOMIZE x. Как то так.

Reobne
24.07.2022, 19:48
SBC (ну просто для 16-битного числа опкода SUB нет, насколько я понял).
Вот на это надо обратить внимание. Именно на данном этапе, пока нет понимания флагов. SBC не замена SUB! Если очень хочется SBC, как замену SUB, то перед ней нужно сбросить флаг переноса. Командой AND A или OR A или CP A.

LD DE,1
AND A ; сбрасываем перенос
SBC HL,DE ; отнимаем

goodboy
25.07.2022, 09:56
бейсик компилятор думает так-же
a=x
b=y
c=x-y


LD HL,(40074)
PUSH HL
LD HL,(40078)
PUSH HL
POP HL
POP DE
AND A
EX DE,HL
SBC HL,DE
LD (40082),HL

в этом примере кстати хорошо видно почему применение всяких ЯВУ компиляторов даёт такой унылый результат
(лишние команды жрут память/такты)

reddie
25.07.2022, 11:48
есть подозрение что глючило как раз из-за пары команд LET x=USR 30000: RANDOMIZE x
Ну эт уже конкретно прогу надо смотреть. Именно в таком виде она неправильная: сперва надо записать значение командой RANDOMIZE, а уже потом запускать код командой USR.
Просто в Спектрум-Бейсике запуск кодов обычно идет конструкцией RANDOMIZE USR, отсюда и путаница. Первым может быть почти любой оператор, требующий параметры.
Но запускается машкод именно через USR - это функция, передающая через пару BC данные, причем в оба направления. На входе BC будет равно адресу старта кодов.

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


хорошо видно почему применение всяких ЯВУ компиляторов даёт такой унылый результат
Логичней было бы объявлять тип переменных, как в Паскале или где еще, по типу "это целочисленный var до 65535 макс"
Что дало бы возможность привязать переменную к регистровой паре в большинстве случаев, и можно прямо в тексте.

Zidane
25.07.2022, 20:08
Исходя из моих знаний о работе компиляторов Basic-кода и был сделан вывод о необходимости разбираться в ассемблере "ручками", а не полагаться на инструменты. И я весьма признателен местным гуру за терпеливую помощь и ускоряющие пинки в нужном направлении.

vlad-kras
25.07.2022, 20:45
в этом примере кстати хорошо видно почему применение всяких ЯВУ компиляторов
Это какой из компиляторов такой код выдал ?

goodboy
25.07.2022, 22:25
mcoder 1983
........
HiSoft ColtCompiler (от тех-же авторов, но 1985) сделал чуть более оптимальный код

LD HL,(51569)
PUSH HL
LD HL,(51575)
POP DE
AND A
EX DE,HL
SBC HL,DE

Andrew771
25.07.2022, 22:33
в этом примере кстати хорошо видно почему применение всяких ЯВУ компиляторов даёт такой унылый результат
(лишние команды жрут память/такты)
Потому что забыли (поленились) сделать оптимизации кода, сокращение push/pop. Ради смеха прогнал в ZX Like Pascal:


program abc;
var
a,b,c,x,y: byte;
begin
a:=x;
b:=y;
c:=x-y;
end.

Выдало:


ld a,(_X)
ld (_A),a
ld a,(_Y)
ld (_B),a
ld a,(_X)
ld l,a
ld h,0
ld a,(_Y)
ld e,a
ld d,0
and a
sbc hl,de
ld a,l
ld (_C),a

Если все переменные типа word (а не byte), то еще лучше:


ld hl,(_X)
ld (_A),hl
ld hl,(_Y)
ld (_B),hl
ld hl,(_X)
ld de,(_Y)
and a
sbc hl,de
ld (_C),hl

reddie
25.07.2022, 23:53
все равно весьма далеко от чисто ассемблерного кода. примерно так это будет в асме:

LD HL,X
LD BC,Y
PUSH HL
AND A
SBC HL,BC
EX DE,HL ;DE=C=X-Y
POP HL ; восстанавливаем X

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

Lethargeek
26.07.2022, 09:20
Если все переменные типа word (а не byte), то еще лучше:
лучше было бы, если бы компилятор переупорядочил операторы

Andrew771
26.07.2022, 09:21
goodboy и reddie, а где в ваших примерах присвоение a=x, b=y и c=x-y? Я вижу только взятие x и y и их вычитание

reddie
26.07.2022, 09:44
а где в ваших примерах присвоение a=x, b=y и c=x-y?
А зачем оно в ассемблере? За редким исключением, переменные можно хранить прямо в регистрах, либо модифицировать код.
Не, если речь идет о компиляции программы на каком-то языке с кучей переменных - тогда другое дело. Но в асме никто так кодить не будет.

Andrew771
26.07.2022, 12:27
А зачем оно в ассемблере? За редким исключением, переменные можно хранить прямо в регистрах, либо модифицировать код.
Посчитал очки или координаты спрайта и будешь пол-игры в регистрах хранить? Так регистров не напасёшься :)

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


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

goodboy
26.07.2022, 12:28
наверно речь о том что

varA ld hl,#xxxx
(varA+1) это и будет адрес переменной

reddie
26.07.2022, 12:28
Посчитал очки или координаты спрайта
В таких случаях да, либо блок данных с переменными, либо прямо в коде, чтоб сэкономить память.
Но конструкции, как из компиляторов на предыдущей странице, в любом случае проигрывают чисто ассемблерной реализации.

Andrew771
26.07.2022, 22:39
За сколько напишешь на голом ассемблере такую процедуру вывода окна с динамическим списком опций из разных массивов и выбора одной из них? И стОит ли оно столько времени? Я на Паскале за один день написал и отладил:



procedure window_dialog_sub1;
begin

case w_items[w_dialog_i,1] of
view_science_1: write(s_text[w_items[w_dialog_i,2]]);
view_civ_1: write(civ_text[w_items[w_dialog_i,2]]);
view_city_1: write(c_text[w_items[w_dialog_i,2]]);
view_unit_1: write(u_text[w_items[w_dialog_i,2]],' ',u[w_items[w_dialog_i,2],u_at],'/',
u[w_items[w_dialog_i,2],u_def],'/',u[w_items[w_dialog_i,2],u_speed]);
view_building_1: write(b_text[w_items[w_dialog_i,2]]);
view_tax_1:
begin
tax_view:=(w_items[w_dialog_i,2]-1)*10;
write(tax_view,'%');
end;
end;

end;


procedure window_clear_put;

{
w_dialog_center - окно по центру
w_dialog_x - x левого верхнего угла окна, если w_dialog_center=0
w_dialog_y - y левого верхнего угла окна, если w_dialog_center=0
w_dialog_w - ширина окна
w_dialog_h - высота окна
w_dialog_color1 - цвета рамки
w_dialog_color2 - цвета окна
}

begin

if w_dialog_center=1 then
begin
w_dialog_x:=(32-w_dialog_w)/2;
w_dialog_y:=(24-w_dialog_h)/2;
end;

color(w_dialog_color2);

WindowSet(w_dialog_x,w_dialog_y,w_dialog_w,w_dialo g_h);
WindowClear;

for i2:=0 to w_dialog_w-1 do
begin
SpritePutClear(FRAME_H,w_dialog_color1,i2+w_dialog _x,w_dialog_y);
SpritePutClear(FRAME_H,w_dialog_color1,i2+w_dialog _x,w_dialog_y+w_dialog_h-1);
end;
for i2:=0 to w_dialog_h-1 do
begin
SpritePutClear(FRAME_V,w_dialog_color1,w_dialog_x, i2+w_dialog_y);
SpritePutClear(FRAME_V,w_dialog_color1,w_dialog_x+ w_dialog_w-1,i2+w_dialog_y);
end;
SpritePutClear(FRAME_LU,w_dialog_color1,w_dialog_x ,w_dialog_y);
SpritePutClear(FRAME_RU,w_dialog_color1,w_dialog_x +w_dialog_w-1,w_dialog_y);
SpritePutClear(FRAME_LD,w_dialog_color1,w_dialog_x ,w_dialog_y+w_dialog_h-1);
SpritePutClear(FRAME_RD,w_dialog_color1,w_dialog_x +w_dialog_w-1,w_dialog_y+w_dialog_h-1);

end;


procedure window_dialog;

{
Параметры процедуры window_dialog:

вход:

w_dialog_center - окно по центру
w_dialog_x - x левого верхнего угла окна, если w_dialog_center=0
w_dialog_y - y левого верхнего угла окна, если w_dialog_center=0
w_dialog_w - ширина окна
w_dialog_h - высота окна
w_dialog_items - количество элементов
w_items[] - параметры элементов: номер таблицы, номер элемента
w_dialog_text1 - заголовок 1 окна
w_dialog_text2 - заголовок 2 окна
w_dialog_color1 - цвета рамки
w_dialog_color2 - цвета окна
w_dialog_color3 - цвета окна и элементов
w_dialog_color4 - цвета окна и курсора
w_dialog_cancel - возможность отказа от выбора (1=да/0=нет)

выход:
w_dialog_case - выбранный элемент (0, если отказ от выбора)

промежуточные:
w_dialog_i1 - первый видимый элемент
w_dialog_i2 - последний видимый элемент
w_dialog_cursor - вертик.координата курсора
}


begin

w_dialog_case:=1;
w_dialog_cursor:=1;
w_dialog_i1:=1;
if (w_dialog_h-4)>w_dialog_items
then w_dialog_i2:=w_dialog_items
else w_dialog_i2:=w_dialog_h-4;

window_clear_put;

color(w_dialog_color2);

gotoxy((w_dialog_x+1)*2,w_dialog_y+1);
writeln(w_dialog_text1);
gotoxy((w_dialog_x+1)*2,w_dialog_y+2);
writeln(w_dialog_text2);

repeat

color(w_dialog_color3);

WindowSet(w_dialog_x+1,w_dialog_y+3,w_dialog_w-2,w_dialog_h-4);
WindowClear;

for w_dialog_i:=w_dialog_i1 to w_dialog_i2 do
begin
gotoxy((w_dialog_x+1)*2,w_dialog_y+3+w_dialog_i-w_dialog_i1);
window_dialog_sub1;
end;

color(w_dialog_color4);
gotoxy((w_dialog_x+1)*2,w_dialog_y+2+w_dialog_curs or);
for w_dialog_i:=1 to (w_dialog_w-2)*2 do
write(' ');
gotoxy((w_dialog_x+1)*2,w_dialog_y+2+w_dialog_curs or);
w_dialog_i:=w_dialog_case;
window_dialog_sub1;

repeat
readkey(key1,key2,key3);

if key1=81 then
if w_dialog_case>1 then
begin
w_dialog_case:=w_dialog_case-1;
w_dialog_cursor:=w_dialog_cursor-1;
if w_dialog_case<w_dialog_i1 then
begin
w_dialog_i1:=w_dialog_i1-1;
w_dialog_i2:=w_dialog_i2-1;
w_dialog_cursor:=w_dialog_cursor+1;
end;
end;
if key1=65 then
if w_dialog_case<w_dialog_items then
begin
w_dialog_case:=w_dialog_case+1;
w_dialog_cursor:=w_dialog_cursor+1;
if w_dialog_case>w_dialog_i2 then
begin
w_dialog_i1:=w_dialog_i1+1;
w_dialog_i2:=w_dialog_i2+1;
w_dialog_cursor:=w_dialog_cursor-1;
end;
end;

if key1<>0 then delay(10);

until key1<>0;

if key1=32 and w_dialog_cancel=1 then w_dialog_case:=0;

until key1=13 or w_dialog_case=0;

color(0);
clrscr;

end;


77644

Lethargeek
26.07.2022, 22:56
а на голом и не будут писать такое, макросы + шитый или байт-код

vlad-kras
27.07.2022, 19:16
То есть word по сравнению с byte использует более короткий код при математических операциях и присвоении результата, довольно неожиданно. Затраты на хранение переменных покрываются размером кода даже при одиночном использовании, при последующих использованиях X,Y уже не надо тратиться на word вместо byte, а код для word опять будет короче.



a,b,c,x,y: byte;

Выдало:


ld a,(_X)
ld (_A),a
ld a,(_Y)
ld (_B),a
ld a,(_X)
ld l,a
ld h,0
ld a,(_Y)
ld e,a
ld d,0
and a
sbc hl,de
ld a,l
ld (_C),a

Если все переменные типа word (а не byte), то еще лучше:


ld hl,(_X)
ld (_A),hl
ld hl,(_Y)
ld (_B),hl
ld hl,(_X)
ld de,(_Y)
and a
sbc hl,de
ld (_C),hl

Zidane
27.07.2022, 21:28
В вышеприведенном примере - да. Но есть способ попроще, хотя с некоторыми ограничениями.
Через переменную SEED Бейсика (RND- генератор). Командой Randomize NN (до 65535) заносим туда число, а в кодах забираем оттуда значение: LD RP, (23670), где RP - нужная регистровая пара, 23670/23671 - два байта числа.
Ограничения, думаю, понятны: при запуске кодов через Randomize Usr значение сменится по возврату в Бейсик. Обычно удобней запускать командой вида LET X=USR NN, по возврату X будет равно значению рег. пары BC, то есть можно передавать и обратно из кодов в Бейсик.

Эксперимент не прекращается. И вот с последней частью вышла запарка. Хотя я разобрался. Оказалось, что при вызове подпрограмм ПЗУ для вывода 16-битного значения на экран через стек калькулятора (11563 и 11747) они вычищают рег. пару BC. Ну и в итоге по возврату в бейсик мы видим в ней 0. А вот если их убрать, то все нормально. Всем спасибо за помощь, я еще зайду ). Просто для вас, возможно, такие действия со стороны процессора очевидны, а вот я в ступор минут на 20 впал )

Andrew771
27.07.2022, 21:46
То есть word по сравнению с byte использует более короткий код при математических операциях и присвоении результата, довольно неожиданно
Да, потому что ZX Like Pascal использует 16-битную математику при расчетах. Поэтому приходится сначала преобразовывать byte в word. Однако, если используются длинные массивы, то byte выгоднее по памяти, если не требуется word.

Zidane
01.08.2022, 18:55
Доброго времени суток. Снова вопрос нуба. Циклы на ассемблере организуются только через DJNZ? Ну я имею ввиду, что для организации цикла длиной более 255 итераций нужно создавать вложенные циклы? Или можно, к примеру, грузить в регистровую пару DE число итераций, затем грузить содержимое регистра D в аккумулятор и сравнивать его с регистром E. Пока сравнение ложно, флаг Z равен 0. И тогда JR NZ,nn снова запускаем выполнение блока кода? Или где-то я ошибся?

reddie
01.08.2022, 19:18
грузить в регистровую пару DE число итераций, затем грузить содержимое регистра D в аккумулятор и сравнивать его с регистром E
Обычно так и делают, только не сравнение, а OR - LD BC/DE/HL,NN: DEC Rx: LD A,R_high: OR R_low: JR NZ,REPEAT
Можно и без использования аккумулятора, но тогда значение нужно пересчитать вручную/средствами ассемблера, на выходе будет что-то типа

LD DE,NN
....
DEC E
JR NZ,REPEAT
DEC D
JR NZ,REPEAT


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

0xDEAD
01.08.2022, 20:03
Так ещё можно:


ld de, 1000 ; будем делать 1000 итераций

ld b,e ; подготовка регистров
dec de ; подготовка регистров
inc d ; подготовка регистров
ld hl,0 ; после выполнения цикла, в hl должно быть значение 1000
Loop:
inc hl
djnz Loop
dec d
jp nz,Loop


О, вот тебе и раз - опоздал. Ну и ладно. :D

Zidane
01.08.2022, 20:15
В общем решил так:

LD BC,nn ;количество итераций, 2 байта
LD HL,nn ;число с которым выполняются операции
LOOP
LD A,n ;константа в регистр
; Код программы
DEC BC
LD A,B
OR C
JR NZ,LOOP

Если использовать CP C, то выполняется почему то 253 итерации. И все. А вот с OR пошло как надо. Всем спасибо )

reddie
01.08.2022, 20:16
А еще можно так =)) при условии, что итераций меньше 32768

LD DE,NN
....
DEC DE
BIT 7,D
JR Z,REPEAT

значение флага будет равно значению бита, т.е. когда бит 7=0 (число>=0 но меньше 32768) - сработает переход по Z
ну и счетчик надо заранее уменьшать на 1, поскольку при нулевом значении DE цикл все еще будет крутиться


сли использовать CP C, то выполняется почему то 253 итерации


да, нужно использовать именно OR - нулевой результат будет тогда, когда оба регистра равны нулю

Bedazzle
02.08.2022, 12:49
В общем решил так:

LD BC,nn ;количество итераций, 2 байта
LD HL,nn ;число с которым выполняются операции
LOOP
LD A,n ;константа в регистр
; Код программы
DEC BC
LD A,B
OR C
JR NZ,LOOP

Если использовать CP C, то выполняется почему то 253 итерации. И все. А вот с OR пошло как надо. Всем спасибо )

LD A,n не имеет смысла, т.к. чуть ниже идёт LD A,B

Ну и попробуем сравнить:




ld bc, 1000
loop:
dec bc ; 6t
ld a,b ; 4t
or c ; 4t
jr nz, loop ; 12t
; итого 26 тактов


или так



ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)


или так



ld bc, 1000
loop:
dec c ; 4t
jr nz, loop ; 12/7t
dec b ; 4t
jr nz, loop ; 12/7t
; итого 32 такта (чуть меньше, т.к. последний виток быстрее переход)


и предложенное выше



ld de, 1000
loop:
dec de ; 6t
bit 7,d ; 8t
jr z, loop ; 12t
; итого 26 тактов



Первый вариант быстрее, и чуть короче, но использует регистр А.
Третий вариант медленнее второго, но зато не привязан к B, можно использовать два любых регистра.
Четвёртый не привязан к А, но имеет ограничение на 32765 значений.
Что лучше - выбирать нужно по конкретной задаче.

Reobne
02.08.2022, 14:47
ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)
На самом деле 13+11/256. Потому что 255 из 256 раз djnz цикл заворачивает с затратой 13 тактов, и лишь один раз из 256 до jr дело доходит



ld bc, 1000
loop:
dec c ; 4t
jr nz, loop ; 12/7t
dec b ; 4t
jr nz, loop ; 12/7t
; итого 32 такта (чуть меньше, т.к. последний виток быстрее переход)
Аналогично 16+11/256

Bedazzle
02.08.2022, 15:55
На самом деле 13+11/256. Потому что 255 из 256 раз djnz цикл заворачивает с затратой 13 тактов, и лишь один раз из 256 до jr дело доходит

Аналогично 16+11/256

Это если мы знаем, что у нас 1000 циклов, а если будет 50 или 100, то получится иная раскладка.

Zidane
02.08.2022, 16:38
LD A,n не имеет смысла, т.к. чуть ниже идёт LD A,B

Очень даже имеет, так как в начале программы используется константа которая не меняется. Собственно в моей программе туда грузим вполне себе конкретный байт цветности, а потом выгружаем его в ячейку памяти. Вообще, поскольку константа не меняется, можно было бы её напрямую грузить в вычисляемую ячейку памяти, но программа была уже написана и работала, так что... :))

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

Dart Alver
02.08.2022, 22:23
или так

Код:


ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)


Пример немного с ошибкой по числу итераций - нужно LD CB,1000 [+256] а не LD BC,1000 .
Ну а поскольку такой команды нет , то надо выкручиваться вручную или макросом )))
И учитываем что если младьший байт числа итераций не 0 , то старший нужно увеличить на 1

Reobne
02.08.2022, 22:29
а если будет 50
Ну да, я в среднем и приблизительно написал. Если мы знаем сколько конкретно циклов будет, то можем конкретно посчитать такты.


ld bc, 1000
loop:
djnz loop ; 13/8t
dec c ; 4t
jr nz, loop ; 12/7t
; итого 29 тактов (чуть меньше, т.к. последний виток быстрее переход)
Например 50 циклов. Считаем. 49 раз djnz завернёт за 13 тактов. Плюс один раз djnz 8t, dec 4t и jr 7t.
49*13+8+4+7=50*13-13+8+4+7=650+6=656
За цикл будет 656/50=1312/100=13,12
Всё равно 13 с мелочью. Но не 29.

Bedazzle
02.08.2022, 23:36
Ну да, я в среднем и приблизительно написал. Если мы знаем сколько конкретно циклов будет, то можем конкретно посчитать такты.


Согласен.
Кстати, если заменить JR на JP, можно ещё ускорить.

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


Пример немного с ошибкой по числу итераций

Да, правильно подмечено.

drbars
03.08.2022, 08:34
Один из способов нестандартного применения команды CPI для организации цикла.


ld hl,0
ld bc,1000
.loop:
[код цикла]
cpi
jp pe,.loop


На выходе HL=1000.

0xDEAD
15.09.2022, 00:03
Если нет сильной ностальгии разрабатывать именно на реале, лучше перебраться на компиляцию чем-то поновее.

https://hype.retroscene.org/blog/dev/946.html
Я понимаю, конечно, что прошёл чуть ли не год с момента, как это было написано - но зачем?!
Этот мануал давным-давно устарел, и для VSCode появился замечательный плагин DeZog (https://marketplace.visualstudio.com/items?itemName=maziac.dezog), который представляет собой отличный дебаггер, со встроенным эмулятором и возможностью, при желании, подключать внешние. По сути, VSCode+DeZog+Z80 Macro-Assembler (https://marketplace.visualstudio.com/items?itemName=mborik.z80-macroasm) - и для кросс-разработки больше ничего и не нужно...