Рассылка закрыта
При закрытии подписчики были переданы в рассылку "О карьере и профессиональном развитии IT-специалистов" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
← Сентябрь 2003 → | ||||||
1
|
2
|
3
|
4
|
6
|
7
|
|
---|---|---|---|---|---|---|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
17
|
18
|
19
|
20
|
21
|
22
|
24
|
25
|
26
|
27
|
28
|
|
29
|
30
|
Статистика
+11 за неделю
Низкоуровневое программирование для дZeнствующих #0041
Информационный Канал Subscribe.Ru |
# Пролог
|
отвђт | Сообщенiе |
animator
Воин дзена |
Дата: Авг 29, 2003 13:22:39 Скажите пажалуйста как програмно выдернуть сетевой шнур из сетевой платы под Win2000Prof? |
pas
Воин дзена |
Дата: Авг 29, 2003 14:29:45 Скажите пажалуйста как програмно выдернуть сетевой шнур из сетевой платы под Win2000Prof? ИМХО програмно под любой ОС ни как, только подойти в шнуру и ручками его выдернуть. |
Sk. Inc.
Воин дзена |
Дата: Авг 29, 2003 19:23:12 Очень просто и даже можно двумя вариантами: 1) Запрограммировать сетевой шнур на выскакивание из сетевой платы 2) Запрограммировать сетевую плату на выплёвывание шнура Какой способ проще - решать тебе ;-) |
DaemoniacaL
Воин дзена |
Дата: Авг 30,
2003 01:29:11 А можно запрограммировать собачку AIBO пусть подбегает и выдергивает когда надо :) |
KiNDeR
Воин дзена |
Дата:
Авг 30, 2003 01:40:59 Во, Идея. Берем ленточный принтер, веревку. Один конец веревки привязываем к барабану принтера, другой к кабелю. Пишем прогу которая посылает в принтер сигнал. Барабан вращается, веревка натягивается, кабель выдергивается... Все довольны, все медитируют... |
Quantum
Воин дзена |
Дата: Авг 30,
2003 01:57:15 KiNDeR Ещё проще: Привязываем верёвочку к CD; Программно открываем CD :) ЗЫ: А как воткнуть этот шнур обратно? |
KiNDeR
Воин дзена |
Дата:
Авг 30, 2003 01:58:24 Quantum Воткнуть. Вместо веревки используем проволку...или стальной прут. |
DaemoniacaL
Воин дзена |
Дата: Авг 30,
2003 02:12:11 Сделать переходник RJ-45<>RJ-45 внутрь встаить 8-контактное реле, управлять с какого-нить порта. |
volodya
[ HI-TECH ] |
Дата:
Авг 30, 2003 05:01:37 Наверное, как-то можно это сделать. Написать драйвер, который бы долбал бы драйвер платы (я так понимаю, в терминологии Windows это будет мини-драйвер), и усе :))) Работы на пару месяцев... :) |
Edmond / HI-TECH
/\ & ()
(алгоритмы и оптимизация)
Извечное преобразование слов машины в слова людей
Сегодня я решил покончить с клюбом читателей WASM.RU, и плавно перенести его в клуб творителей. Это значит, что у Вас есть теперь повод поучаствовать в чём-то достаточно серьёзном. С другой стороны, последнее время я заметил насколько поднимает жизненный тонус оптимизация кода, и решил, что если совместить очень приятное с очень полезным – получится рулез во всех смыслах этого слова.
Перед вами код четырёх функций преобразования. Это не просто функции, а так называемые Элементарные контекстные функции.
Многие из нас привыкли, что преобразование числа в строку осуществляется
отдельной функцией. Да, с точки зрения достижение максимума скорости
такой подход был
бы верен, однако с точки зрения совершенства архитектуры – нет. Перед вами
не сами
функции перевода строки в число или наоборот. Сами по себе переводить они
не могут, ибо не учитывают ошибок, которые могут быть допущены при
конвертировании
(а если учитывают, то список этих ошибок мал).
На основе этих элементарных контекстных функций могут быть основаны сложнейшие
процедуры, начиная от банального перевода строк в числа и наоборот, заканчивая
конвейерной обработкой данных в компиляторах ets. Интерфейсы этих функций
построены таким образом, чтобы их было легко использовать как при преобразовании
UNICODE,
так и ANSI строк.
Вам предлагается оптимизировать их по скорости либо по размеру, соблюдая
жёсткие рамки интерфейса на входе и выходе функций. Однако вы можете:
- Менять алгоритм
- По-другому переплетать код
Ваши предложения и примеры мы ждём на форуме WASM.RU. Либо, если по каким-то причинам вам не доступен форум, я буду рад вашим замечаниям по email Edmond@wasm.ru
Лучшие варианты по размеру и скорости будут опубликованы в качестве первых релизов проекта ULIB/TplOut в феврале 2004 года.
;; Модуль x_to_x ;; Базовая версия ;; (c) Edmond/HI-TECH ;; ####################### DATA ##################################### .data CONSTANT_1999999Ah dd 1999999Ah CONSTANT_1000000000 dd 1000000000 CONSTANT_10 dd 10 CONSTANT_5 dd 5 .code ;; ######################## CODE #################################### ;; FUN::ВСDInt32_to_Str ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; int32 = ecx type:dword ; - целое число, которое должно быть преобразовано в строку ; buffer = edi type:pointer ; - указатель на буфер, в который должна быть помещена строка. ; RET:: ; Функция возвращает строку в буфер, и edi указывает на последнюю ; секцию символов, помещённые в буфер. ; Регистр ebx содержит последнюю секцию, помещённую в буфер. ; ; DPN:: ; Элементарная функция, которая преобразовывает число ; в BCD строку ; в обратном виде (то есть от младшего разряда к старшему) ;------------------------------ ; ALG: Comment # Функция делит число на 10, а остатоки от деления состовляют строку Здесь функция имеет два цикла: 1. Большой цикл 2. Малый цикл В малом цикле результат выполнения функции накапливается в регистре ebx И в конце цикла помещается в строку. # ;; ================================================================== Int_to_Str@@Div10 MACRO ;; %%%%%%%%%%%%%%%%%%%%%%%%%% Comment # Макро, который скрывает код для деления числа на 10 А так же код, который участвует в малом цикле # ; USE: ; eax - текущее число для mul ; ebx - аккумулятор для собрания 4 символов строки ; ecx - число x ; edx - число x/10 ;; %%%%%%%%%%%%%%%%%%%%%%%%%% mul CONSTANT_1999999Ah ;; Сохранение результата для последующих операций mov eax,edx ;; Умножение eax на 10 add eax,eax lea eax,[eax+eax*4] ;; Получаем остаток в ecx sub ecx,eax ;; Помещаем в eax следующее значение x/10, ;; которое хранилось в edx mov eax,edx ;; результат в ebx shrd ebx,ecx,8 ;; Помещаем в ecx текущее число mov ecx,eax ENDM ;; =========================================== BCDInt32_to_Str proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # ::Алгоритм. Функция выполняет последовательное деление исходного числа на число 10. Остатки при деление и есть десятичными разрядами числа. ::Особенности алгоритма Деление выполняется при помощи умножения с переполнением. # ; USE: ; eax - текущее число для mul ; ebx - аккумулятор для собрания 4 символов строки ; ecx - число x ; edx - число x/10 ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;; +++++++ Большой цикл +++++++++++++++++++++++++ ALIGN 16 @@: mov eax,ecx xor ebx,ebx ;;++++++++++++++++++++++++++++++++++++++++++++++++ comment /------------- Точки входа BCDInt32_to_Str_xx существуют для возможности использовать данный код более гибче в других функциях ---------------------/ BCDInt32_to_Str_1:: Int_to_Str@@Div10 BCDInt32_to_Str_2:: Int_to_Str@@Div10 BCDInt32_to_Str_3:: Int_to_Str@@Div10 BCDInt32_to_Str_4:: Int_to_Str@@Div10 ;;++++++++++++++++++++++++++++++++++++++++++++++++ mov eax,ebx ;; (Если вы добавите эту строчку вы получите ASCII строку) ;; -- add eax,30303030h -- test ecx,ecx stosd jnz short @B ;; +++++++ Большой цикл +++++++++++++++++++++++++ ret BCDInt32_to_Str endp ;; ############################################################# ;; ############################################################# ;; FUN::BCDInt64_to_Str ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; int64 = edx:eax type:qword ; - целое число, которое должно быть преобразовано в строку ; buffer = edi type:pointer ; - указатель на буфер, в который должна быть помещена строка. ; RET:: ; Функция возвращает число в буфер, регистр edi указывает ; на последнию секцию ; символов, помещённые в буфер, а в регистре ebx -- их содержит. ; DPN:: ; Элементарная функция, которая преобразовывает число int6 ; 4 в BCD строку, ; в обратном виде (то есть от младшего разряда к старшему). ;------------------------------ ; ALG: Comment # Функция разбивает 64-битное число на два, и использует участки кода функции BCDInt32_to_Str для того, чтобы преобразовать части числа. Особенностью работы функции является запись 32 битного результата в память. Поскольку функция BCDInt32_to_Str возвращает результат выполнения в регистре ebx и результат может не полностью помещатся в регистр ebx, то функция следит за тем,чтобы следующий результат BCDInt32_to_Str продолжил заполнения этого регистра. # ;; ================================================================== BCDInt64_to_Str proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; all ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;; 1. При помощи команды div CONSTANT_1000000000 ;; нельзя делить числа ;; большие FFFFFFFFFFFFF, поэтому это следует учесть. test edx,0f0000000h jnz @F ;; 2. В том случае, если условие не выполнилось, ;; число можно разделить при помощи ;; Одной команды DIV start: div CONSTANT_1000000000 push eax mov ecx,edx call BCDInt32_to_Str ;; 3. !!! ;; После вызова этой функции, регистр ebx хранит последние ;; помещённые в память ;; четыре байта. При этом доказано, что какое бы на входе ;; ни было число, ;; ebx заполнен только младшим разрядом, то есть, ;; ebx = 000000xxh ;; Таким образом, чтобы последующие числа не были разорваны нулями ;; мы корректируем ebx, так, как будто уже выполнился ;; первая часть кода в BCDInt32_to_Str ;; И передаём управление не на начало процедуры BCDInt32_to_Str ;; а на BCDInt32_to_Str_2 ;; Эти операции выполняет код ;; Помещаем старшую числа для преобразования pop ecx sub edi,4 ;; Уменьшаем edi, ;; чтобы число записалось вместо старого mov eax,ecx ror ebx,8 ;; Я делаю jmp вместо call, и управление вернётся к функции, ;; которая вызвала данную jmp BCDInt32_to_Str_2 ;; 4. В том случае, если число больше значения FFFFFFFFFFFFF ;; Алгоритм усложняется ;; 4.1 Сперва мы покомпонентно делим число на 1000000000 @@: mov ecx,eax mov eax,edx xor edx,edx div CONSTANT_1000000000 ;; После этой команды в eax - остаётся самый старший разряд числа ;; Мы сохраняем его в стеке, так как он понадобится позднее. push eax xchg ecx,eax div CONSTANT_1000000000 ;; 4.2 После этой команды, в регистре edx окажется младшая ;; часть числа, ;; которую можно перевести в строку функцией BCDInt32_to_Str ;; Сохраняем старшую часть push eax ;; В ecx - параметр функции mov ecx,edx call BCDInt32_to_Str ;; 4.3 Восстанавливаем из стека сохранённые части 64-bits числа ;; в edx:eax pop eax pop edx div CONSTANT_1000000000 ;; 4.4 Аналогично, теперь мы имеем разбитое на две части число. ;; Старшуй часть, которая в eax, сохраняем на потом, ;; а младшую (edx), переводим ;; в строку. push eax Comment /------------------ Пара команд sub edi,4 ror ebx,8 Находятся здесь потому как они корректируют контекст после выполнения функции BCDInt32_to_Str. При этом доподлинно известно, что после выполнения этой функции ebx заполняется только младшим разрядом. ebx = 000000xxh Чтобы строка BCD не разделялась нулями, которых нет, мы корректируем значения ebx и edi, и вызываем выполнение с точки входа BCDInt32_to_Str_2 -------------------/ sub edi,4 mov ecx,edx mov eax,edx ror ebx,8 call BCDInt32_to_Str_2 Comment /------------------ Пара команд sub edi,4 shl ebx,16 Находятся здесь потому, так как они корректируют контекст после выполнения функции BCDInt32_to_Str_2. При этом доподлинно известно, что после выполнения этой функции ebx заполняется только двумя младшими байтами: ebx = 0000xxxxh. Чтобы строка BCD не разделялась нулями, которых нет, мы корректируем значения ebx и edi, и вызываем выполнение с точки входа BCDInt32_to_Str_3 -------------------/ ;; Помещаем старшую числа для преобразования pop ecx ;; Уменьшаем edi sub edi,4 ;; Контекст для точки вызова mov eax,ecx shl ebx,16 ;; после этого ebx = xxxx0000h ;; Я делаю jmp вместо call, и управление вернётся к функции, которая ;; вызвала данную jmp BCDInt32_to_Str_3 BCDInt64_to_Str endp ;; ################################################################## ;; Варианты функций для непроверенной не BCD строки IF not CM$__string_check ;; ################################################################## ;; FUN::Str_to_Int32 ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; pbuffer = esi type:offset buffser ; - буффер строки ASCII заканчивающийся нулём ; RET:: ; int32 = edx ; - число ; errcode = eax ; - код ошибки. Если ошибок не было errcode = 0 ; pbuffer = esi ; - указывает на последний символ, ; который был прочитан функцией. ; DPN:: ; Функция конвертирует строку ASCII в число int32. В случае ; ошибки, функция возвращает ненулевое значение в eax, которое ; равно символу, не соответствующиму ASCII цифрам. ; Таким образом, не обязательно, чтобы строка завершалась нулём. ; Но вызвавшая функция сама должна оценить ситуацию. ; ;------------------------------ ; ALG: Comment # Алгоритм Функция считывает каждый символ из строки. Преобразовывает ASCII символ в BCD (ASCII-30) и запоминает его (edx). При каждом следующем считывании она увеличивает запомненное число на 10, и прибавляет к нему новое, взятое из строки. # ;; ================================================================== Str_to_Int32 proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; eax, edx ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% xor eax,eax xor edx,edx ALIGN 16 @@: ; ------- Цикл ------------ lodsb sub al,30h ;; Если меньше -- конец jb short @F cmp al,9 ja short @F lea edx,[edx+edx*4] lea edx,[eax+edx*2] jmp short @B @@: add al,30h ret Str_to_Int32 endp ;; ################################################################## ;; ################################################################## ;; FUN::Str_to_Int64 ;;------------------------------ ; CONV::ASMCALL ; FORMALS:: ; buffer = esi type:pointer ; - указатель на строку, заканчивающююся нулём. ; RET:: ; int64 = edx:eax ; - число ; ecx - Последний символ, обработанный функцией ; esi - Указатель на последний символ, ; обработанный функцией ; DPN:: ; Функция преобразовывает строку в INT64 число. ;;------------------------------ ; ALG: Comment # Алгоритм Перевод осуществляется в два этапа. Сперва переводится в число старшая часть числа После младшая И в конце результат корректируется. Основная идея разделения перевода строки в 64 число, состоит в следующем: 1. Мы переводим строку в число, пока число не превысит крайнего значения 32-bits 2. Результат запоминается 3. Переводится оставшая (младшая) часть числа, и при этом подсчитывается количество разрядов, вошедшее в это число. 4. Основываясь на колличестве разрядов, старшая половина корректируется и суммируется с младшей частью. # ;; ================================================================== Str_to_Int64 proc ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comment # # ; USE: ; eax,edx,ecx ;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% xor eax,eax xor edx,edx mov ecx,eax ;; ECX будет содержать число 10*n, ;; где n - число разрядров прошедших к анализу. inc ecx ALIGN 16 @@: ; ------- Цикл ------------ lodsb sub al,30h ;; Если меньше -- конец jb short endproc cmp al,9 ja short endproc lea edx,[edx+edx*4] lea edx,[eax+edx*2] ;; Если сумматор превысит это значение, ;; значит преобразование старшей части завершено Str_to_Int64_next:: ;; ffffffffh/ah = 19999999h cmp edx,19999999h jna @B @@: ;; Когда закончилось преобразование ;; старшей части запоминаем её в стеке push edx xor edx,edx @@: ; ------- Цикл ------------ lodsb sub al,30h ;; Если меньше -- конец jb short @F cmp al,9 ja short @F shl ecx,1 ;; edx = edx*10+eax lea edx,[edx+edx*4] lea edx,[eax+edx*2] ;; ecx = ecx*10 (shl ecx, 1 уже была выше) lea ecx,[ecx+ecx*4] ;; Следует следить за тем, чтобы сумматор множетеля разрядности ;; не переполнился cmp ecx,1000000000 jae short above_19999999h jmp @B @@: ;; Преобразование младшей части закончено. ;; В стеке была сохранена старшая часть числа ;; Помещаем её в eax. pop eax ;; Процедура корректирования результата преобразований ;; младшей и старшей части. Str_to_Int64_correct:: correct: ;; Контекст ;; eax - старшая часть числа ;; edx - младшая часть числа ;; ecx - множитель ;; 1. Сохраняем в стеке младшую часть. push edx mul ecx ;; После этой команды в edx:eax хранится 64-bits число ;; Теперь к нему нужно прибавить оставшуюся часть результата, ;; которая находится в стеке pop ecx ;; edx:eax = edx:eax+ecx add eax,ecx adc edx,0 ;; Последний символ возвращается в cl xor ecx,ecx mov cl, byte ptr [esi-1] ret above_19999999h: ;; Управление приходит сюда, только в том случае, ;; если число слишком ;; большое для команд lea и поэтому нужно воспользоваться ;; другим алгоритмом. ;; Контекст: ;; edx - содержит текущее число, которое больше, чем 19999999h ;; ecx - содержит число переведённых разрядов ;; в стеке лежит старшая часть числа. ;; Поскольку теперь уже всё равно придётся иметь дело ;; с умножением 64 битного ;; числа, мы делаем приведение pop eax ;; eax = старшая часть числа call correct ;; Теперь edx:eax - содержат 64-bits текущее число. mov ecx,eax ;; Прибавляем последний разряд xor eax,eax lodsb sub al,30h ;; Если меньше -- конец jb short @F cmp al,9 ja short @F ;; старшая часть числа * 10 ;; младшая * 10 push eax mov eax,ecx mov ecx,edx shl ecx,1 mul CONSTANT_10 lea ecx,[ecx+ecx*4] add edx,ecx pop ecx ;; edx:eax = edx:eax + ecx (ecx = последний символ - 30h) add eax,ecx adc edx,0 ;; Преобразование законченно xor ecx,ecx mov cl,[esi] inc esi ret ;; Управление приходит на эту метку, ;; если мы вернулись при преобразовании последнего символа @@: add al,30h xchg ecx,eax ret ;; Управление приходит сюда, если мы вернулись из первого цикла. endproc: xor eax,eax mov al,byte ptr [esi-1] mov ecx,eax mov eax,edx xor edx,edx ret Str_to_Int64 endp ;; ##################################################################
# Эпилог
…. И каждое начало Сентября вы будете чувствовать необычайный подъём сил. Кто знает, возможно, точно так же как чувствовал это Александр Сергеевич в момент наступления осени. Хотя он был поэт, а не программист. Однако сколько же программистов по имени Александ :)))) |
###########################################################################
Оригинальное всТупление
|
Edmond / HI-TECH
|
|
------------- |
###########################################################################
Рассылка составлена HI-TECH GROUP 02 сентября 2003 года. |
(c) HI-TECH 2000-2003
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||