Главная страница Случайная страница Разделы сайта АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
💸 Как сделать бизнес проще, а карман толще?
Тот, кто работает в сфере услуг, знает — без ведения записи клиентов никуда. Мало того, что нужно видеть свое раписание, но и напоминать клиентам о визитах тоже.
Проблема в том, что средняя цена по рынку за такой сервис — 800 руб/мес или почти 15 000 руб за год. И это минимальный функционал.
Нашли самый бюджетный и оптимальный вариант: сервис VisitTime.⚡️ Для новых пользователей первый месяц бесплатно. А далее 290 руб/мес, это в 3 раза дешевле аналогов. За эту цену доступен весь функционал: напоминание о визитах, чаевые, предоплаты, общение с клиентами, переносы записей и так далее. ✅ Уйма гибких настроек, которые помогут вам зарабатывать больше и забыть про чувство «что-то мне нужно было сделать». Сомневаетесь? нажмите на текст, запустите чат-бота и убедитесь во всем сами! Newline. mov bx,0; индекс массива
mov cx, N; счётчик цикла mov bx, 0; индекс массива L2: inint ax mov Y[bx], ax; ввод очередного элемента es: Y[bx] add bx, 2; увеличение индекса loop L2 mov bx, offset X; указатель на X[1] mov si, offset Y+2*N-2; указатель на Y[N] L3: mov ax, [bx]; первый сомножитель mul word ptr es: [si]; умножение на Y[N-i+1] jc Err; большое произведение add S, ax jc Err; большая сумма add bx, type X; это bx: =bx+2 sub si, 2; это i: =i-1 loop L3; цикл суммирования mov dx, offset T2 Outstr outword S Newline Finish Err: mov dx, T3 Outstr Finish code ends end begin_of_program
Подробно прокомментируем эту программа. Количество элементов массивов мы задали, используя директиву эквивалентности N equ 20000, это есть указание программе Ассемблера о том, что всюду в программе, где встретится имя N, надо подставить вместо него операнд этой директивы – число 20000. Таким образом, это почти полный аналог описания константы в языке Паскаль.[20] Под каждый из массивов директива dw зарезервирует 2*N байт памяти. Заметим теперь, что оба массива не поместятся в один сегмент данных (в сегменте не более примерно 32000 слов, а у нас в сумме 40000 слов), поэтому массив X мы размещаем в сегменте data1, а массив Y – в сегменте data2. Директива assume говорит, что на начала этих сегментов будут соответственно указывать регистры ds и es, что мы и обеспечили в самом начале программы. При вводе массивов мы использовали индексный регистр bx, в котором находится смещение текущего элемента массива от начала этого массива. При вводе массива Y мы для учебных целей вместо предложения L2: inint Y[bx]; ввод очередного элемента записали два предложения L2: inint ax mov Y[bx], ax; ввод очередного элемента Это мы сделали, чтобы подчеркнуть: при доступе к элементам массива Y Ассемблер учитывает то, что имя Y описано в сегменте data2 и автоматически (используя информацию из директивы assume) поставит перед командой mov Y[bx], ax специальную однобайтную команду es:. Эту команду называют префиксом программного сегмента, так что на языке машины у нас будут две последовательные, тесно связанные команды: es: mov Y[bx], ax В цикле суммирования произведений для доступа к элементам массивов мы использовали другой приём, чем при вводе – регистры-указатели bx и si, в этих регистрах находятся адреса очередных элементов массивов. Напомним, что адрес – это смещение элемента относительно начала сегмента (в отличие от индекса элемента – это смещение от начала массива). При записи команды умножение mul word ptr es: [si]; умножение на Y[N-i+1] мы вынуждены явно задать размер второго сомножителя и записать префикс программного сегмента es:, так как по виду операнда [si] Ассемблер не может сам " догадаться", что это элемент массива Y размером в слово и из сегмента data2. В команде add bx, type X; это bx: =bx+2 для задания размера элемента массива мы использовали оператор type. Параметром этого оператора является имя из нашей программы, значением оператора type < имя> является целое число – тип данного имени. Для имён областей памяти это длина этой области в байтах (для массива это почти всегда длина одного элемента), для меток команд это отрицательное число –1, если метка расположена в том же сегменте, что и оператор type, или отрицательное число –2 для меток из других сегментов. Все остальные имена имеют тип ноль. Вы, наверное, уже почувствовали, что программирование на Ассемблере сильно отличается от программирования на языке высокого уровня (например, на Паскале). Чтобы подчеркнуть это различие, рассмотрим пример задачи, связанной с обработкой матрицы, и решим её на Паскале и на Ассемблере. Пусть дана прямоугольная матрица целых чисел и надо найти сумму элементов, которые расположены в строках, начинающихся с отрицательного значения. Для решения этой задачи на Паскале можно предложить следующий фрагмент программы Const N=20; M=30; Var X: array [1..N, 1..M] of integer; Sum, i, j: integer; ... { Ввод матрицы X } Sum: =0; for i: =1 to N do if X[i, 1]< 0 then for j: =1 to M do Sum: =Sum+X[i, j]; Сначала обратим внимание на то, что переменные i и j несут в программе на Паскале двойную нагрузку: это одновременно и счётчики циклов, и индексы элементов массива. Такое совмещение функций упрощает понимание программы и делает её очень компактной по внешнему виду, но не проходит даром: чтобы по индексам элемента массива вычислить его адрес в сегменте, приходится выполнить достаточно сложные действия. Например, адрес элемента X[i, j] приходится вычислять так: Адрес(X[i, j])= Адрес(X[1, 1])+2*M*(i-1)+2*(j-1) Эту формулу легко понять, учитывая, что матрица хранится в памяти по строкам (сначала первая строка, затем вторая и т.д.), и каждая строка имеет длину 2*M байт. Буквальное вычисление адресом элементов по приведённой выше формуле (а именно так чаще всего и делает Паскаль-машина) приводит к весьма неэффективной программе. При программировании на Ассемблере лучше всего разделить функции счётчика цикла и индекса элементов. В качестве счётчика лучше всего использовать регистр cx (он и специализирован для этой цели), а адреса лучше хранить в индексных регистрах (bx, si и di). Исходя из этих соображений, можно так переписать программу на Паскале, предвидя её будущий перенос на Ассемблер. Const N=20; M=30; Var X: array [1..N, 1..M] of integer; Sum, cx, oldcx: integer; bx: ↑ integer; ... { Ввод матрицы X } Sum: =0; bx: =↑ X[1, 1]; {Так в Паскале нельзя} for cx: =N downto 1 do if bx↑ < 0 then begin oldcx: =cx; for cx: =M downto 1 do begin Sum: =Sum+bx↑; bx: =bx+2 {Так в Паскале нельзя} end; cx: =oldcx End else bx: =bx+2*M {Так в Паскале нельзя} Теперь осталось переписать этот фрагмент программы на Ассемблере:
N equ 20 M equ 30 oldcx equ di Data segment X dw N*M dup (?) Sum dw? ... Data ends ... ; Ввод матрицы X mov Sum, 0 mov bx, offset X; Адрес X[1, 1] mov cx, N L1: cmp word ptr [bx], 0 jge L3 mov oldcx, cx mov cx, M L2: mov ax, [bx] add Sum, ax add bx, 2 loop L2 mov cx, oldcx jmp L4 L3: add bx, 2*M L4: loop L1
Приведённый пример очень хорошо иллюстрирует стиль мышления программиста на Ассемблере. Для доступа к элементам обрабатываемых данных применяются указатели (ссылочные переменные, адреса), и используются операции над этими адресами (адресная арифметика). Получающиеся программы могут максимально эффективно использовать все особенности архитектуры используемого компьютера. Применение адресом и адресной арифметики свойственно и некоторым языкам высокого уровня (например, языку С), который ориентирован на использование особенности машинной архитектуры для написания более эффективных программ. Вернёмся к описанию команд цикла. В языке Ассемблера есть и другие команды цикла, которые могут производить досрочный (до исчерпания счётчика цикла) выход из цикла. Как и для команд условного перехода, для мнемонических имен некоторых из них существуют синонимы, которые мы будем разделять в описании этих команд символом /. Команда loopz/loope L выполняется по схеме Dec(CX); if (CX< > 0) and (ZF=1) then goto L; А команда loopnz/loopne L выполняется по схеме Dec(CX); if (CX< > 0) and (ZF=0) then goto L; В этих командах необходимо учитывать, что операция Dec(CX) является частью команды цикла и не меняет флага ZF. Как видим, досрочный выход из таких циклов может произойти при соответствующих значениях флага нуля ZF. Такие команды используются в основном при работе с массивами, для усвоения этого материала Вам необходимо изучить соответствующий раздел учебника по Ассемблеру.
|