Фак программисту. Или как понять свой старый код
Не так давно я вернулся к разработке zifi. Этот код был позаброшен больше 9 месяцев назад, и тогда я… устал продолжать :)
Но — не забываю, есть старые проблемы, есть вновь найденные неудобства. Надо фиксить, надо развивать.
Оболочка полезная вышла.
Но, ччёрт, столько времени прошло…
Как понять свой старый код через год? И как писать так, что-бы его можно было пОнять позже?
Я выложу свои идеи по этому поводу, но хотелось бы услышать Ваши стандарты разработки.
Итак, мои личные рекомендации:
1. Не пишите код в линию.
ld a,h, b,l,d,i: or a: jr z,milf — это замечательно, и настолько лаконично… но двояко, для меня лично.
Это крайне слабо читаемо. Да, я понимаю — «сгущение» (не подберу другого слова) смысла в строках.
Но читаемость кода, на мой взгляд — падает основательно. Из-за приёмов, подобных «ld a,h, b,l,d,i».
Возможно, этот метод только лично мне не привычен, ок.
Но код в строчку — читать не могу.
2. Разделяйте процедуры переносами строк.
когда вы видите сплошную ленту дизасемблера — это одно. тут уж увы, не поформатишь, пожинай логику байтами.
но в СВОЁМ коде — отделяйте блоки и процедуры друг от друга.
визуальное восприятие блоками — резко повышает читаемость и восприятие логики каждой части программы.
мало пары переносов — да пожалуйста, используй свои разделители в комментариях.
важнее всего здесь — визуальное разделение блоков кода.
Тебе его потом отлаживать.
3. Применяйте понятные метки.
Время 8-ми байтовых меток давно ушло. Экономить не обязательно, не ZEUS.
Для важных процедур стоит использовать метки, максимально описывающие — что делает процедура.
Важно выделять важные места в программе, стараясь максимально понятно их называя:
Но! для незначащих переходов — стоит использовать короткие метки перехода sjasm — 1b, 1f:
3.1. equ для меток
Об этом нельзя сказать ничего плохого, но есть метод проще. Вместо чего-то типа:
заюзайте вариант попроще:
3.2. Ох, я намешал этих 2b / 1f, теряюсь!
Сделай себе понятнее. Определи понятные метки вместо временных для тех, которые ВАЖНЫ.
ибо временные метки важными не являются по определению. и хер ты на нужную сможешь сослаться.
4. Протестил — оформь процедурой.
Всё обычно начинается с наброска, с теста — заработает ли, выйдет ли что-нибудь из этой затеи?
но если уж всё получается — выдели этот блок, с умным и понятным именем в отдельную процедуру.
что это даст?
упрощение отладки:
по сути — три щелчка по f8, и ты из прошёл.
и если есть проблема — тут же ясно в какой именно процедуре зараза :)
и да, давай процедуру с процедурами, окей?
5. Да всё понятно это, банальные вещи тулишь. Что делать с упрощением доступа к данным из асма, типа где IX+56 и т.д.?
Замечательная проблема, приводящая к постоянной автозамене.
Господа, юзайте структуры.
вкратце:
ld а,(ix+sprite.size_x) — здесь мы получаем доступ к необходимой ячейке по её имени с приставкой, к чему именно она принадлежит.
описание её выглядит вот так (справедливо для sjasmplus):
для себя отмечаем, что эта структура — 6 байт в длину :)
это даёт понятный код, и огромную простоту при переделке.
6. Хочу много одинаковых повторяющихся кодов и данных вперемешку, потому что у меня тьюринг! МАШЫНА!
отлично!
есть хорошее средство обьединить код и данные в группу — макросы.
вместо определения макроса в код будет подставлено его содержимое.
при этом — для макроса можно задавать входные параметры, что очень удобно.
параметры и содержимое макроса будет лежать в памяти точно так-же, как и привычный код.
Представь себе — что это подпрограмма, в которой можно заменить параметры описанные в заголовке.
приведу макрос, юзаемый мною в демах. он — без кода, и предназначен для указания, какая именно процедура будет вызываться в основном цикле. значимо здесь — номер команды (2), и адрес передаваемой процедуры:
Но — не забываю, есть старые проблемы, есть вновь найденные неудобства. Надо фиксить, надо развивать.
Оболочка полезная вышла.
Но, ччёрт, столько времени прошло…
Как понять свой старый код через год? И как писать так, что-бы его можно было пОнять позже?
Я выложу свои идеи по этому поводу, но хотелось бы услышать Ваши стандарты разработки.
Итак, мои личные рекомендации:
1. Не пишите код в линию.
ld a,h, b,l,d,i: or a: jr z,milf — это замечательно, и настолько лаконично… но двояко, для меня лично.
Это крайне слабо читаемо. Да, я понимаю — «сгущение» (не подберу другого слова) смысла в строках.
Но читаемость кода, на мой взгляд — падает основательно. Из-за приёмов, подобных «ld a,h, b,l,d,i».
Возможно, этот метод только лично мне не привычен, ок.
Но код в строчку — читать не могу.
2. Разделяйте процедуры переносами строк.
когда вы видите сплошную ленту дизасемблера — это одно. тут уж увы, не поформатишь, пожинай логику байтами.
но в СВОЁМ коде — отделяйте блоки и процедуры друг от друга.
визуальное восприятие блоками — резко повышает читаемость и восприятие логики каждой части программы.
мало пары переносов — да пожалуйста, используй свои разделители в комментариях.
важнее всего здесь — визуальное разделение блоков кода.
Тебе его потом отлаживать.
3. Применяйте понятные метки.
Время 8-ми байтовых меток давно ушло. Экономить не обязательно, не ZEUS.
Для важных процедур стоит использовать метки, максимально описывающие — что делает процедура.
Важно выделять важные места в программе, стараясь максимально понятно их называя:
change_page ld bc,#7ffd
...
Но! для незначащих переходов — стоит использовать короткие метки перехода sjasm — 1b, 1f:
2 ld de,0
jr 1f
jr 2b
...
1 ld hl,0
на всякий: 1 — это номер метки, b / f — это сторона перехода — back или forward: назад к метке 1, или вперёд по коду к ней.Естественно, злоупотреблять ними не стоит; но я нахожу им отличное применение — вместо ненужных «названиефункции1» или «названиефункции3».
3.1. equ для меток
Об этом нельзя сказать ничего плохого, но есть метод проще. Вместо чего-то типа:
ololo
equ $+1
ld hl,0
заюзайте вариант попроще:
ld (ololo+1),hl
3.2. Ох, я намешал этих 2b / 1f, теряюсь!
Сделай себе понятнее. Определи понятные метки вместо временных для тех, которые ВАЖНЫ.
ибо временные метки важными не являются по определению. и хер ты на нужную сможешь сослаться.
4. Протестил — оформь процедурой.
Всё обычно начинается с наброска, с теста — заработает ли, выйдет ли что-нибудь из этой затеи?
но если уж всё получается — выдели этот блок, с умным и понятным именем в отдельную процедуру.
что это даст?
упрощение отладки:
start
ld sp,#bfff
call all_init
call set_256c_mode
call sd_init
по сути — три щелчка по f8, и ты из прошёл.
и если есть проблема — тут же ясно в какой именно процедуре зараза :)
и да, давай процедуру с процедурами, окей?
5. Да всё понятно это, банальные вещи тулишь. Что делать с упрощением доступа к данным из асма, типа где IX+56 и т.д.?
Замечательная проблема, приводящая к постоянной автозамене.
Господа, юзайте структуры.
вкратце:
ld а,(ix+sprite.size_x) — здесь мы получаем доступ к необходимой ячейке по её имени с приставкой, к чему именно она принадлежит.
описание её выглядит вот так (справедливо для sjasmplus):
struct sprite
y byte
size_y byte
x byte
size_x byte
tnum byte
spal byte
ends
для себя отмечаем, что эта структура — 6 байт в длину :)
это даёт понятный код, и огромную простоту при переделке.
6. Хочу много одинаковых повторяющихся кодов и данных вперемешку, потому что у меня тьюринг! МАШЫНА!
отлично!
есть хорошее средство обьединить код и данные в группу — макросы.
вместо определения макроса в код будет подставлено его содержимое.
при этом — для макроса можно задавать входные параметры, что очень удобно.
параметры и содержимое макроса будет лежать в памяти точно так-же, как и привычный код.
Представь себе — что это подпрограмма, в которой можно заменить параметры описанные в заголовке.
приведу макрос, юзаемый мною в демах. он — без кода, и предназначен для указания, какая именно процедура будет вызываться в основном цикле. значимо здесь — номер команды (2), и адрес передаваемой процедуры:
MACRO _play addr
db 2
dw addr
ENDM
102 комментария
Так вот. Проблема не коде в строчку. Проблема в неудобных мнемониках и каше из разных частей кода в строке.
Т.е., твои примеры «неправильные». К коду в строку отношения не имеющие.
Задумайся сам, зачем тратить на это 7 строк:
Вопрос — как писать, сверху-вниз или слева-направо напрямую относится к психологии восприятия человеком, к работе ума. Тут не все так просто, не только вопрос удобства.
Логическая аргументация такова:
Ход вычислений — сверху вниз. И когда мы располагаем их слева-направо, это осложняет нам восприятие хода вычислений, так как приходится постоянно «переключаться» от сверху-вниз к слева-направо. Это может быть аргументированно там где требуется и общепринято, например при вычислении выражений, но если особой ДОСТАТОЧНОЙ на то необходимости не имеется, лучше этого избегать.
Это «удобно» до той поры, когда ты «в теме», пока тебе заранее известно что происходит в данном фрагменте текста программы. Но как только ты утеряешь это представление, и тебе надлежит РАЗБИРАТЬСЯ (вот в чем разница), подобная запись немного, но осложняет восприятие. Является маленьким препятствием, местом преткновения.
Вплоть до того что я иногда на автомате enter'ом возвращаю сложные строки обратно к представлению в столбец.
Ну и последняя аргументация. Если бежать и создавать к каждой строке комментарии отдельным столбцом, то подобные строки слишком широки и не дают нормально написать комментарии. К тому же, если будет столбец сплошных строк-комментариев, он «маскирует» такую длинную строку.
Так что… любой «выверт» имеет свою цену… :)
Я думаю, что большинство таких рассуждений — чисто размышлизмы. Но на практике, запись в столбик придумывали на экранах 30-40 символов в ширину. Не знаю насчёт тебя, а я сижу сейчас напротив экрана, который даёт мне в терминале 210 символов в ширину. Ты всерьёз думаешь, что сможешь убедить меня потратить 3/4 этого пространства на пустое место?
Сейчас конкретно я сижу перед браузером :) А вообще, бывает, работаю с кодом открывая три и даже четыре окна параллельно рядом друг с другом. Почему окна открываются слева-направо, четырьмя столбцами окон? Почему там код в столбец? Почему они не открываются друг-под-другом? (ну ок, два окна еще можно). И почему я матюгаюсь, скроллируя окошко влево-вправо, когда какой-то деятель нафигачил длинных строк? :)
Всерьез убеждаю тебя освоить редактирование с параллельным открытием нескольких окошек рядом… Дико удобно, да и если кто-нибудь видит, немедленно оценит твою крутизну :))))
А когда кто-то напишет нормальный IDE — как ставить брейкопинты и степать прям по коду?
Напомню, в C разрешено больше одной команды в строке. И мы все до сих пор живы.
Типа:
Дебаггером в отладке не простепаешь по statements в строке.
Дебаг-инфа сохраняется в виде адрес->строчка исходного кода.
Номер 3.1 — вкусовщина. Раньше я всегда писал (ololo+1), сейчас мне кажется, что это путает. Чаще ввожу специально явную ссылку. Но вообще, обычно у меня в одном исходнике обычно можно найти и так и эдак. Когда-нибудь преодолею…
Номер 4 — вкусовщина.
Номер 6 требует в качестве подпункта ссылку на макро-библиотеку Flying, которая не содержит ни одного компилируемого байта:
zxpress.ru/article.php?id=3614
Реальный дзен и, если чуть серьёзнее, там и правда есть несколько хороших идей.
Хотя у меня всё не так! :)
приемлемо только по началу — одноразовое исполнение, но что делать потом?
с кучей ссылок на ссылки и данными оттуда, которые ссылаются на данные.
либо же — быстрый но рабочий набросок за один вечер, который абсолютно был не описан но сегодня для тебя имеет значение как особенная реализация стандартной процедуры
с кучей ссылок на ссылки и данными оттуда, которые ссылаются на данные»? Ну да, ссылок больше. Но есть одно достоинство — ты не путаешь метки с данными. Пример из моего кода:
Видишь? Мне не нравится писать ld (trb_play+1),hl, потому что я начал рефактор, добавил команду и получил чудесные глюки.
Да, это больше меток и труда. Но это реально лучше код.
я кажется делал так: вводил две метки, одну на строке с командой, чтобы нельзя было ничего вписать между меткой, а вторую ставил вне строки. Т.е.
Соответственно, первая метка это имя функции для call/jump, вторая чисто для данных. Да, ее нельзя использовать «где-то там» без контроля, и это плохо, error-prone.
Плохо уже помню, но кажется конструкций вида label equ $+1 в некоторый момент просто не было… Сейчас лучше использовать label equ $+1, еще и потому на других машинах встретится именно это.
потом уже понял что это ну совсем не дело :)
Только отладчик у меня был в десятичной системе и команды я помнил в десятичной системе…
Нет, ну в самом деле, конструкция
вызывает кратковременное преткновение ума, необходимо пусть хоть и кратковременно, но все же понять что означает эта метка, вычислить ее значение в уме. Вместо equ $+1 для других команд там должно быть equ $+2 и даже equ $+3, а это уже error-prone.
Это основное.
Такжке для логической аргументации можно упомянуть нарушение принципа verbosity в командах которые используют эту метку.
ld (label+1),reg мне явно показывает что это само-модифицирующийся код, тогда как ld (label),reg такого не показывает, скрывает это от меня.
Это не всегда означает просто, увы.
(Эйнштейн) «Все должно быть изложено так просто, как только возможно, но не проще.»
С этим тесно связан закон достаточного основания. Любая сложность должна быть весомо аргументирована. Но это уже из области построения систем, как таковых.
Так пишут только мудаки!
ага и zeus отменили, да? Помести см себя на место другого, который будет использовать твой код на другом ассемблере.
а теперь сравни время набора такого кода и форматированного сырка. Учись читать, короче.
фак сам себе написал короче.
но всё таки — это всё связано удобством для себя, в прошлом и будующем, а так-же для остальных.
с кратким кодом не согласен, но я знаю — это вкусовщина.
Про метки — я бы ещё локальные, с точкой которые, упомянул бы. Нравятся они мне, не засоряют неймспейс )
Мне кажется, дешевле всё же набрать по-человечески, чем потом сушить мозги.
Хотя я тоже люблю пхать в строку короткие смысловые блоки типа or a: jr z, milf.
расскажи плиз, о .1
Код, само собой, от балды и смысла не несёт, оптимизировать не надо )
Штука в том, что метки с точкой «видны» только внутри блока, до следующей «обыкновенной» метки. Внутри второй процедуры я спокойно могу использовать имя .loop повторно, и ничего мне за это не будет )
Однако к такой метке можно при желании доступиться и извне, указав полный путь типа jp do_some_stuff.leave.
Т. е. такая себе более удобная / приятная для понимания альтернатива вот этим вот всем 1b / 2f.
Но у меня как-то мышление неправильно под них выстраивается, и я начинаю психовать, когда пытаюсь писать с ними систематически. Особенно, видимо, мешает как раз их локальность, т.к. номерки скачут через другие метки, а локальные — не могут и меня это как-то очень деморализует.
— MGIMO fininshed?
= ask!
это нормально? Это я так понимаю всё ради компилятора не способного дать возможность построить таблицу чисел не подряд байтик за байтиком, а +256. И самое главное проводятся тесты, да ещё и плакать начинаем, что лишние 512 байт памяти в дырки ноликов превратились. А тестировать надо только оптимизированный код, вот такой:
Вот это читаемо LD H,HIGH(MULTAB), а так тестируют только мудаки LD H,MULTAB/512, тратящие место и главное такты на ограниченность компилятора. Слово мудаки я использовал только по отношению к тому кто его написал.
Но меня заинтересовало другое. Умножение. У тебя что, тоже есть такая дрянь раскрянченая? Или ты вообще?
И, в любом случае, было бы здорово, если бы ты поделился, даже не самими процедурами своими, но примерно порядком — сколько, как ты считаешь, должно занимать знаковое умножение 8*8->8 и 8*8->16?
и считаю что оба этих метода — удобны. и знаю последователей — вас, psndcj.
но мне просто удобнее мой, привычый :)
Люди. Складывайте библиотеки в модули. Это реально упрощает жизнь.
Глядишь, поборол бы наконец лень и написал себе синхрилку папки с PC, а то слот SD скоро в ноль сотрётся ;)
В следующем выпуске!
Только у нас!
Всего неделя!
По одной в руки!
«Let's Start Publishing Usable Assembly Language Code»
Кстати, еще одно правило: не пишите бгомерзкий код на Alasm.
:)))
(шутка, shinilb0g!)
Если это прочитать быстро то можно вызвать демона :) Чем продолжительней строка тем круче демон.
вполне логическая связка, годная для быстроты ввода.
Потому что мы все имеем опыта программирования на более высоком уровне, выделение строки для каждой команды слишком «разбавляет» код. Да, я тоже терпеть не могу вереницы присваиваний
Но пора перестать мешать в кучу код в строку (нормальная идея) от синтаксиса шторма — это не одно и то же.
Голова не обязательно должна кружиться от кода в строчку:
Разумеется, любую, даже самую хорошую идею можно довести до абсурда. Но что это доказывает? Правильно, ничего.
именно вариант с: нравится.
Код в строку не читаем. Да ляпать по клавиатуре меньше, да макросы как не использовали, так и не используем, но это не важно, главное это «фича» — в строку писать. У вас такой команды LD HL,A нет, сделайте — будет, фича ведь, меньше клавиш нажимать, и модно как. А ($+3) генерируем по-рандому. Но ещё лучше записываем вот так … Смотрите, какая компактная запись, а как читается? Ну просто прекрасно. От того что в СИ можно писать в строчку не значит что код будет читаем, это не значит, что так надо писать. От тог что в русском языке есть набор унижающих других слов, не значит что их надо применять, иначе давайте возвращайте забанненых тролей назад, они просто писали в строчку слова которые вы не можете правильно прочитать, а некоторые не могут прочитать потому что «мудаки». Про «мудака» — то про меня, конечно же. Вот по этой причине я и не выкладываю свои стати, я ведь «мудак» пишу на своём асаме, да в масоне.
Т.е. проблема опять в том, что ты утрируешь немного. И хочешь писать по-своему. Но пойми, Вова напал на нас первый и мы (многокомандвстрочники) просто защищались. Я же не говорю всем как писать, я говорю что имею право писать и мне так удобно. И есть свои достоинства, далеко всё не чёрно-белое. А разные люди приходят и говорят, что ld b,c,d,e — плохая команда, и что $ в строке непонятно к чему привязать. А то, что у меня в коде нет ни первого и не второго видимо спросить забыли.
Я верю, что каждый может в строчку написать сто-тыщ операций, но это не читаемо. Читаемы слова, объясняющие что ты там на прерываниях делаешь. И потом когда будет происходить «дебаг», править надо будет только макрос, в котором через двоеточие, по спиральке, змейкой и крестиком пиши как угодно. Опять-таки я не говорю про простой код, пусть он будет сложный, пусть будет ветвистый и выжатый до последнего такта, но максимально читаемый. А каждый пример который был тут приведён не несёт оптимизации, и не несёт информативность, он несёт только фичу.
И я ещё раз говорю, слово «мудак» я применяю только к тому, кто его применил ко мне. И да, я очень сильно разозлился, кода прочитал что я «мудак» только лишь потому, что кто-то вышивает крестиком, да ещё постыдно это называет читаемый код.
А про код… ну я на самом деле сейчас в некотором недоумении, потому что тут просто удивительно сколько людей поругались сейчас из-за удивительно ничтожного повода. Ну вот я внимательно прочёл. Вертикальные блоки. Макросы. Ну если очень коротко, у нас разное понимание что такое читаемость. У Raidera одна, у тебя — другая, у меня как видишь третья оказалась. Не знаю что делать по этому поводу. Для начала, наверное, нужно перестать по этому поводу ссориться.
Но боюсь на него CALL бывает очень редко.
haters gonna hate
мы то не в курсе с кем ты говоришь. а читаем именно мы а не он :)
но!
LD A,32,(DE),A:INC E:DJNZ $-2 === IzcOrD788LMOaLcSDNUvWuYBk0otWm!ya3
а это — кусок кода из wild player, подпрограмма PRLIN
т.е. нужно дополнительное и немалое усилие что-бы прочитать что здесь, особенно если весь стиль — такой.
другими словами — такой код нужно сразу приводить к стандартным мненоникам, а потом только разбираться и тестить и ИСКАТЬ ГДЕ НЕ ВЕРНО СКОНВЕРТИЛ К СТАНДАРТНЫМ МНЕМОНИКАМ z80!
нахер эти головоломки дополнительные?
с другой стороны —
ld hl,#4000: ld de,#4001: ld bc,6911: ld (hl),l: ldir гораздо легче воспринимается.
благодаря тому что ЕСТЬ КОМАНДЫ, А НЕ какая-то МЕТАКОМАНДА LD, и они отделены пробелами и двоеточием.
если учесть что для некоторых асмов нужно писать XOR A,A а для других эта же запись на выходе выглядит как XOR A: XOR A, то дополнительно себе устраивать дебаг ещё и по этому поводу я вообще не пойму.
В общем — любой дебаггер выводит по команде в строку, не смешивая. это показывает как код выполняется процессором.
и поэтому, это — нормальная запись.
И невзирая на всё это, у меня выработались другие предпочтения.
Твоё знание не абсолютно. Так же как и моё разумеется. У нас разный дзен, Лёша. Не о чем спорить.
А теперь пошли спать!
Деня, твой звин сильно расхолаживает.
Есть большой смысл ставить метки в начале строки — так ты визуально отделяешь начало подпрограммы от кода:
«notGameOver», деня! а не " notGameOver"
почему? потому что другие асмы воспринимают эту метку иначе.
отсюда же:
минимум — пробел в начале строки перед кодом, и никаких пробелов для меток, переменных.
В общем, все две твои игры которые я портировал, начинались с того что я приводил текст в нормальный вид, я, блин, ФОРМАТИРОВАЛ КОД с начала.
зачем? да он не компилируется!
и да, я пользуюсь двумя-тремя табами перед каждой строкой кода :)
С похожим я тоже столкнулся, когда выложил сырки под Enterprise128 — пользователь написал, что непонятно и неясно, каким ассемблером пользоваться. тем же исходником я тоже столкнулся и долго вспоминал, что было написано год назад.
я отлично запомнил метку для подпрограммы у Коши:
GROB
что делает эта процедура? ;)
Надо было написать:
G
R
O
B
чтобы тебе понятнее было!
особенно — в интрах, когда начинается оптимизация — там такая жесть по перетасовке всего и вся начинается…