PDA

Просмотр полной версии : Учусь программировать (need help)



Dr.Potapov
22.02.2018, 00:05
Решил тут освоить асм для z80. Не пинайте сильно, если вдруг мои вопросы покажутся слишком *****кодием )

Помогите разобраться, есть в буфере buff строка вида "123__456____789" (с произвольным количеством пробелов между любыми символами) заканчивающаяся на #0d,#0a. Строка в buff никогда не начинается с пробела и не заканчивается пробелом (это я осилил). Необходимо привести ее к виду "123_456_789" (с одним пробелом между группами цифр).

Код который я написал


ld hl,buff
ld bc,buff
charloop
ld a,(hl)
ld (bc),a
cp #20
jr z,skipmoveright
inc bc
skipmoveright
inc hl
ld a,(bc)
cp #0a
jr z,clearline
jr charloop
clearline

сжирает и пробелы.

Вижу где, понимаю, что как то надо хоть один пробел оставить, но не пойму куда inc bc:inc hl воткнуть правильно. Помогите, а?

shurik-ua
22.02.2018, 00:24
ld hl,buff
ld bc,buff
ld e,0
charloop
ld a,(hl)
cp #0a
jr z,clearline
inc hl
cp #20
jr z, charloop ; ignoring all spaces in input buf
inc e
ld d,a
ld a,e
cp #03 ; if there 3 digits in a row then add space
jr nz, m1
ld e,0
ld a,d
ld (bc),a
inc bc
ld d,#20
m1
ld a,d
ld (bc),a
inc bc
jr charloop
clearline

Dr.Potapov
22.02.2018, 00:47
Спасибо, но ваш вообще не работает )
64314

Мой код работает так:
64315

Посплю, может приснится )

IgorR76
22.02.2018, 01:08
Дам образный совет.
Попробуйте нарисовать на бумажке алгоритм смещения символов влево. Чтоб наглядно было видно, какой указатель когда увеличивается. Не думайте языком ассемблера, а попробуйте стать процессором и выполнить свой алгоритм сами, проследить по стрелкам, что и куда. И когда оно заработает у Вас, то на ассемблер Вы это легко перепишете.

shurik-ua
22.02.2018, 01:50
но ваш вообще не работает )
я там пару ошибок допустил - щас поправил )

Barmaley_m
22.02.2018, 02:30
Необходимо привести ее к виду "123_456_789" (с одним пробелом между группами цифр).
Вот мой вариант.


ld hl,buff
ld bc,buff
ld e,0 ;0 - после не-пробела, 1 - после пробела
charloop
ld a,(hl)
inc hl
ld (bc),a
cp #0a
jr z,endtext
cp #20
jr z,foundspace
; сюда попадаем при не-пробеле
xor a
ld e,a
jr nonskip
foundspace
ld a,e
or a
jr nz,charloop ;съедаем второй и последующие пробелы
inc e ;при первом пробеле запоминаем, что был пробел, но не съедаем его
nonskip
inc bc
jr charloop
endtext


Вижу где, понимаю, что как то надо хоть один пробел оставить, но не пойму куда inc bc:inc hl воткнуть правильно. Помогите, а?
При решении этой и подобных задач обработки текста нужно думать в терминах конечных автоматов (https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D1%8B%D0%B9_% D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82) и регулярных выражений (https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D 1%8B%D0%B5_%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0 %BD%D0%B8%D1%8F). Это касается как ассемблера, так и других языков программирования. Ход мыслей следующий.

Шаг рассуждения 1. Если в исходной строке встретился пробел, то это может быть первый пробел, а может быть и не первый. Если это первый пробел - то его нужно оставить, а если не первый - съесть.

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

Аналогичным образом можно расширить рассмотрение для отработки пробелов в начале и конце строки (которые надо съесть все).

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

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

shurik-ua
22.02.2018, 04:39
Если это первый пробел - то его нужно оставит

а как же


есть в буфере buff строка вида "123__456____789" (с произвольным количеством пробелов между любыми символами)

Bedazzle
22.02.2018, 07:56
а как же

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

Vladimir_S
22.02.2018, 09:01
Dr.Potapov, Если перевести на человеческий. Некий флаг сброшен в ноль. Встретился зрак пробела который переносится в буфер, а флаг устанавливается в 1. Адрес строки и адрес буфера увеличивается. Если опять читается код пробела, но флаг в 1, то увеличивается только адрес строки. Встретился код не пробела - флаг в 0 и снова здорово. Пока не будет прочитан указатель конца строки.

Dr.Potapov
22.02.2018, 09:10
я там пару ошибок допустил - щас поправил )
Не во всех вариантах работает.
64317

Вообще задачка интересная оказалась ) Спасибо большое сообществу, что с воодушевлением откликнулись )

shurik-ua
22.02.2018, 09:12
Не во всех вариантах работает
вроде во всех )

Dr.Potapov
22.02.2018, 09:18
Преимущества подхода конечных автоматов - это то, что любая обработка текста, которая может быть описана регулярными выражениями, может производиться за один проход вперед по строке без необходимости заглядываний вперед или откатов назад. Единственное, что требуется - это хранить переменную состояния (как правило, одну). Таким образом, можно очень большой класс задач обработки текста решать единообразно и эффективно.
Все, врубился ) Состояние поиска в Е мне мозги вправило )) Я то это состояние вчера в полуночной горячке пытался где то в параллельном мире сохранить ))) Спасибо большое!
64318

Shiny
22.02.2018, 09:24
Набросок на скорую руку. не факт, что сделано верно. Из двух пробелов оставляет только один.

Dr.Potapov
22.02.2018, 09:25
вроде во всех )
не, черная строка это то что я ввожу руками, а синяя строка это результат работы функции )
т.е. при вводе "a_s_d_f_g" выдает результат "asd_fg", а должна оставить строку как есть "a_s_d_f_g"
при вводе "a___s___d___f___g" результат "asd_fg" а должен быть "a_s_d_f_g"
Все равно спасибо, Шурик, что с Бармалеем совместно меня носом в регистр Е ткнули с умной мыслью использовать его ))

shurik-ua
22.02.2018, 09:53
это я неправильно понял ) - думал что надо разбивать строку по 3 символа и разделять пробелами )

drbars
22.02.2018, 23:44
Dr.Potapov,

Как вариант:



LD HL,ARRAY
LD BC,END-ARRAY

LL0 LD A,#20
LL1 CPIR
RET PO ; Выход
JR NZ,LL1
INC BC
LD E,L
LD D,H
CP (HL)
LL3 CPI
JR Z,LL3
PUSH DE
JR C,LL4
DEC HL
LD A,C
LDIR
LD C,A
LL4 POP HL
JR LL0


ARRAY
DB "12 3 456 789",#0D,#0A
END:

drbars
24.02.2018, 07:04
Если длина массива > 256 то лучше заменить код на:



LD A,C ; --> PUSH BC
LDIR
LD C,A ; --> POP BC
LL4 POP HL
JR LL0 ; --> JR LL1


Принцип работы процедуры нахождении более 1 пробела между словами, и подтягиванием хвоста массива к началу :D
Однопроходный алогорим Бармалея правда оптимальнее, пример больше полезен для понимания работы с командами CPI(R). ;)

Dr.Potapov
24.02.2018, 17:06
Если длина массива > 256
Спасибо )
Буфер под строку 128 байт.
Я бармалеевский метод применил ) точнее Бармалей подсказал в каком месте я затупил.

drbars
24.02.2018, 20:40
Спасибо )
Буфер под строку 128 байт.
Я бармалеевский метод применил ) точнее Бармалей подсказал в каком месте я затупил.
На самом деле, тут можно посчитать такты... предполагаю что мой пример, возможно, на коротких массивах будет работать чуть быстрее. Но опять же надо пробовать :)

Dr.Potapov
25.02.2018, 00:49
На самом деле, тут можно посчитать такты... предполагаю что мой пример, возможно, на коротких массивах будет работать чуть быстрее. Но опять же надо пробовать :)
Мне не сложно в код скопировать ваш вариант, но без подсчета тактов я даже не знаю как замерить скорость да и нужно ли )
Эта функция вызывается по нажатию ENTER, буфер на 128 байт с писаниной пользователя приводится в порядок по пробелам (убираются вначале, в конце и затем лишние в том что осталось). ENTER нажимается не столь часто, поэтому думаю что компактность тут более предпочтительна. Да и задачи по оптимизации пока нет (я еще очень плохо въезжаю в чужой и серьезно оптимизированный код), главное что бы работало и я мог понять что там написано )

char
25.02.2018, 08:23
навскидку, как-то так бы накарябал :)



ld hl,buff
call space1
;...

;
space1:
ld a,h ;a>#20 anyway, not space )))
ld d,h
ld e,l

lp_spc1:
ld c,a ;prev char
ld a,(hl)
ld (de),a
inc hl
cp #0a
ret z
; jr z,clearline

cp #20
jr nz,go_spc1 ;current char not space

cp c
jr z,lp_spc1 ;prev and current char = space, skip one

go_spc1:
inc de ;moved ok
jr lp_spc1


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

вместо ld a,h, наверное, лучше просто xor a, да и все )))

char
25.02.2018, 10:29
вот так, кстати, можно 1 переход (и байт) вырезать ;)


space1:
ld b,h ;b>0 anyway )))
ld d,h
ld e,l

lp_spc1:
ld c,b ;prev char -#20
ld a,(hl)
ld (de),a
inc hl
cp #0a
ret z
; jr z,clearline

sub #20
ld b,a
or c
jr z,lp_spc1 ;prev and current char = space, skip one

inc de ;moved ok
jr lp_spc1

char
26.02.2018, 08:00
и да, для второй адресной пары, обычно лучше брать de, а не bc.

( b используется в djnz, hl -> de используется в ldi/ldir и т.д. )

Dr.Potapov
26.02.2018, 10:48
Главное теперь не "подсесть" на помощь зала )) Очень приятно удивлен количеством откликнувшихся и качеством помощи. Спасибо други! )

drbars
02.03.2018, 06:35
Главное теперь не "подсесть" на помощь зала )) Очень приятно удивлен количеством откликнувшихся и качеством помощи. Спасибо други! )
Это отличный способ размять мозг :)

Подсчитал такты, сделал 3 замера. В среднем моя процедура на 300 тактов быстрее работает на коротких массивах. Особенно, если в массиве несколько слов разделены двойными пробелами. Выигрыш видимо получается за счёт LDIR'а.

Dr.Potapov
02.03.2018, 09:54
Подсчитал такты, сделал 3 замера. В среднем моя процедура на 300 тактов быстрее работает на коротких массивах. Особенно, если в массиве несколько слов разделены двойными пробелами. Выигрыш видимо получается за счёт LDIR'а.

LDIR штука замечательная ) но у меня нет указателя на конец массива (точнее он уже неверный, после "стрижки" пробелов в массиве "до" и "после" значащей строки), т.е. вот тут "LD BC,END-ARRAY" мне просто в BC впихнуть нечего, если только я не пробегусь отдельным циклом по массиву со счетчиком и не поймаю #0D в конце или в процессе отсечения пробелов "до" и "после" буду модифицировать указатель на длину массива, что сами понимаете лишняя совершенно морока.

Меня больше беспокоит объем кода, так как я не программист и по большей части времени пишу "трактором", особенно когда Мысль ))) При этом задача заключается в том, что бы уместить ядро программы в 8 килобайт (#6000-#7FFF). В ядре сейчас содержится функция возврата указателя на произвольный драйвер (вызов драйвера идет по имени типа "fd","scr32","kbdn","trd" и прочие), + сами драйверы (какие необходимо в текущей сборке ядра). Функцию возврата указателя дергают системные функции (fs, kbd, sc, syscall которые вкомпилены в ядро всегда) которые в свою очередь вызываются уже userapp (которое вообще не знает ни о каких hardware drivers или системных функциях и получает указатель через ту же функцию возврата указателей по имени). Если что, скажу сразу - возврат указателя происходит один раз и запоминается самой userapp (патчатся нули в "LABEL DB #C3,#00,#00") и в дальнейшем вызов происходит обычным "CALL LABEL". И вся эта канитель сейчас уже занимает 1526 байт (3574 с системным шрифтом) из доступных 8192 байт. Т.е. под дальнейшее творчество (переписать драйвер клавиатуры, дописать системные fs и mem, а так же драйвер trd) осталось 4618 байт.

И я полностью согласен с вами ))

Это отличный способ размять мозг :)

makbar
02.03.2018, 13:06
регулярных выражений. Это касается как ассемблера
как интересно, никогда не видел для ассемблера такой библиотеки.

Barmaley_m
03.03.2018, 19:14
как интересно, никогда не видел для ассемблера такой библиотеки.
Я тоже не видел. Хотя я этим не интересовался; может быть, такая библиотека и есть.

Но я вел речь не о библиотеке, а об общем методе обработки текста с помощью конечных автоматов. Каждому конечному автомату соответствует некоторый регулярный язык (https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D 1%8B%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA). И наоборот, каждому регулярному языку соответствует некоторый конечный автомат. Таким образом, если задачу обработки текста можно свести к распознаванию регулярного языка - то необходимо сначала формально записать этот язык, а потом найти соответствующий конечный автомат. Реализовав который на каком-нибудь языке программирования (в том числе ассемблере), получим требуемую программу.

Существует множество компиляторов регулярных выражений (т.е. программ, которые находят по заданному регулярному выражению соответствующий конечный автомат). Как правило, они выдают на выходе код на языке Си. Этот код можно скомпилировать в ассемблер Z80 автоматически (компилятором) или вручную. В обоих случаях получится заведомо работающая программа, учитывающая всевозможные "краевые случаи", что экономит программисту время на разработку и отладку.

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

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

goodboy
04.03.2018, 00:54
иногда важнее область применения.
возможно готовый ответ (алгоритм) применяется например в адвентюрах
(особенно в тех где распознаются фразы из трёх слов)

Sergey
06.03.2018, 08:44
Если нужны процедуры обработки текстовых строк, их есть у меня! :)
Пример работы на скриншоте: первая строчка - это синтаксис функции, следующая - результат ее выполнения.
Если интересно, постараюсь исходники восстановить.


как интересно, никогда не видел для ассемблера такой библиотеки.
кое-что:
http://zx-pk.ru/threads/21695-sdcc-biblioteki-dlya-zx.html?highlight=%d0%b1%d0%b8%d0%b1%d0%bb%d0%b8%d 0%be%d1%82%d0%b5%d0%ba%d0%b8+sdcc

Dr.Potapov
06.03.2018, 09:45
Если нужны процедуры обработки текстовых строк, их есть у меня! :)
Пример работы на скриншоте: первая строчка - это синтаксис функции, следующая - результат ее выполнения.
Если интересно, постараюсь исходники восстановить.
Конечно интересно! Буду благодарен и если получится восстановить (не срочно, я пока другие задачи решаю в проекте) буду рад использовать при возможности.

Sergey
06.03.2018, 20:30
Конечно интересно! Буду благодарен и если получится восстановить (не срочно, я пока другие задачи решаю в проекте) буду рад использовать при возможности.

14 числа доберусь до исходников, - выложу.

Sergey
13.03.2018, 23:15
Собственно, обещанное.

Процедуры оформлены для SDCC на ассемблере sdasz80.
Надеюсь, это не сильно затруднит изучение. Если что, спрашивайте - ответим.

Dr.Potapov
14.03.2018, 10:51
Собственно, обещанное.
Спасибо ) буду разбираться.