Как подключить lpt порт
За время существования этого сайта мне довольно часто задают один и тот же вопрос, который можно описать примерно следующим образом:
«У меня в компьютере (ноутбуке) нет LPT порта. Купил себе переходник USB-LPT. Установил. Ваши примеры не работают. Почему?»
В итоге решил описать этот вопрос подробнее и написать статью. Да, действительно, сейчас стационарных PC с LPT портом нужно поискать (т.е. далеко не каждая «мать» сейчас идет в комплекте с LPT портом). Про ноутбуки вообще говорить не приходится. Современные модели LPT порт вообще не применяют. Только очень дорогие и специализированные машины, типа DELL, могут «похвастаться» наличием этого порта.
Также, сейчас в продаже можно свободно приобрести вот такие устройства, называемые LPT-USB переходниками.

Инстркуция гласит что этот прибор полностью совместим с различными принтерами, сканерами и т.д. Подключаем переходник к USB порту, устанавливаем драйвера. Смотрим диспетчер устройств. Скорее всего в ветке «Порты LPT/COM» ни чего не появилось (хотя бывают исключения). Скорее всего повится либо новая ветка со странным устройством с именем, например, LPT1USB либо в разделе USB устройств появится странная запись о «USB устройстве поддержки LPT принтеров». Пробуем запустить какой-нибудь пример из статей выше. И ни тут то было — ни чего не работает. Пробуем адрес порта LPT1 — ни чего не работает. Пробуем адресс порта LPT2. К сожалению, такая модернизация адреса в запросах тоже ни к чему не приводит — светодиоды как не загорались так и не загораются.
Чтобы разобраться в чем тут дело давайте вернемся на время к обычному «родному железному» LPT порту — LPT1, который из материнской платы «торчит». Зайдем в диспетчер устройств, заглянем в свойства нашего порта. Там мы увидим вот такую картину. Отлично видно, что система прописала базовый адрес ввода-вывода 0x378 и запрос на прерывание номер 7. Все правильно.
Теперь погрузимся на уровень программирования. В примерах статей выше мы минуя систему защиты ввода-вывода легальными и нелегальными способами напрямую общались с реально существующим регистром ввода-вывода, которому присвоен адрес 0x378. Тут все понятно. Незабудем также о том, что Windows рекомендует работать с LPT портом используя вызовы API функций — OpenFile(), WriteFile(), ReadFile(). Приложения, которые используют LPT порт для обмена информацией по парралельному интерфейсу с внешними устройствами (принтер, например) так и делает. У него нет задачи установить на каком-либо бите регистра Data лигическую еденицу. Ему (приложению) нужно просто отправить пакет данных, а кто там будет какие линиии при этом «дергать» и считывать его не сильно интересует. Эти операции проводит системный драйвер LPT порта. Он подгружается в память при загрузке ОС. Когда мы вызываем функцию OpenFile(«LPT1», . ) мы по сути дела обращаемся к драйверу порта, который имеет символическое имя LPT1. Драйвер делает кучу всякой работы — запрещает доступ к порту другим процессам, настраивает параметры протокола передачи данных, собственно реализует эту передачу, но в конечном итоге все это сводится к прямому управлению отдельными битами LPT порта на уровне ядра ОС.
А теперь попробуем поработать с нашим переходником USB-LPT. Начнем как не покажется странным, с API вызовов. Запускаем OpenFile(«LPT1USB», . ) (смотря как этот переходник диспетчере устройств назовется, если вообще назовется). Что при этом происходит? Дело в том, что теперь мы будем работать не с драйвером LPT порта ОС а с драйвером этого переходника! Вот в чем фокус то! Он принимает пакет данных от нашего пользовательского приложения и в нужном формате через систмный драйвер USB отсылает этот пакет на USB контроллер, «ноги» которого торчат из внешней LPT розетки на проводе (ну это так, «грубое объяснение»). Видете, здесь нет ни какого намека на обращение к регистрам по адресам 0x378(0x278), т.к. их просто нет!
Поэтому, когда Вы патаетесь запускать примеры данного раздела и обращаться напрямую по адресам 0x378 (если этот «псевдо порт» назвался LPT1USB или что-то в этом духе), 0x278 (LPT2_. ) и т.д. ни чего не происходит. Их просто нет! А вот программа котороая работает через API вызовы ни чего не заметит — вся низкоуровневая работа делается драйвером, а каким драйвером и куда пойдут пакеты данных (в реальный порт ввода-вывода или в USB хост-контроллер) — приложению неважно! Попробуйте открыть свойства «псевдопорта» в диспетчере устройств. Нет вкладки с ресурсами? Есть, но там каие-то неадекватные значения или вкладка деактивировнна? В том то и дело.
ИТОГО: С вероятностью 99% купленный Вами переходник USB-LPT не сможет обеспечить Вам работу на низком уровне с пинами этого порта.
Почему 99%? Потомоу что есть самодельниые USB-LPT переходники, которые определяются Windows как полноценный порт LPT1 и ему присваивается вполне обыденный адрес 0x378. Обращения на прямую к пинам порта проходят успешно! Однако это очень нестандартная конструкция (в первую очередь драйвер, который занимается перехватом обращений по базовому адресау порта LPT1). Все это не очень надежно (обновление ОС — и конструкция теряет работоспосбность) и для использования рекомендовано быть может только с натяжкой.
И что тагда делать?
Как ни покажется странным — решение ЕСТЬ. Вы всегда сможете добавить настоящий LPT порт в свой настольный компьютер или ноутбук. Во-первых, забудте сразу о переходниках с интерфейсом USB. Для решения этой задачи необходимо приробрести PCI-LPT переходник для настольного PC (необходимо наличие свободного PCI слота) или PCMCIA-LPT переходник для ноутбука (см. фото ниже).


В случае использования этих устройств ни каких проблем нет. Определяются они как настоящие «родные» LPT порты. Соответствующая запись будет добавлена в диспетчер устройств во вклдаку «LPT/COM порты». Прямое обращение к пинам порта будет работать.
LPT-port: особенности и принципы работы
![]()
Еще на заре появления первых компьютеров перед создателями стояла задача возможности подключения к ним разнообразных устройств. Особенно это стало актуальным тогда, когда компьютеры перестали занимать целые комнаты, а начали помещаться на столе, то есть стали персональными. Ведь компьютер – это не только средство для выполнения вычислений, но и устройство, пользователь которого может выполнять множество различных функций: распечатать текст или фотографии, управлять различными устройствами, воспроизводить фильмы и музыку, связаться с другими пользователями со всех уголков мира с помощью компьютерной сети. Все это становится возможным при подключении к компьютеру внешних устройств, которые называют общим словом периферия, с помощью специальных унифицированных разъемов, называемых портами.

Порты персонального компьютера
Порты персонального компьютера (иначе их еще называют интерфейсы) — это специальные устройства, расположенные на материнской плате компьютера, либо дополнительные платы, подключаемые к ней, которые предназначены для передачи данных между компьютером и внешними устройствами (принтером, мышкой, монитором, веб-камерой и т. п.). Все порты условно можно разделить на 2 большие группы:
- Внутренние – для подключения устройств внутри ПК (жесткие диски, видеокарты, платы расширения).
- Внешние – для подключения внешней периферии (сканера, монитора, клавиатуры, фотоаппарата, флешки).
В данной статье мы рассмотрим один из видов внешнего порта, а именно LPT-port, его принцип работы, подключаемые устройства и современное применение.
Появление LPT-порта
Изначально LPT-port (его еще называют параллельный порт) разрабатывался только для подключения к ПК принтеров, это отражено даже в его названии – Line Printer Terminal, построчный принтерный терминал. Но в дальнейшем этот интерфейс стал применяться и для подключения других устройств: сканеров, дисководов и даже компьютеров между собой.

LPT-port был разработан компанией Centronics, занимавшейся в 70-х годах прошлого века производством матричных принтеров. Но уже через 10 лет его стала использовать фирма IBM для подключения своих скоростных устройств. Дело дошло до того, что было несколько вариантов данного интерфейса от разных производителей периферии.
В первоначальной версии этот порт был однонаправленным, то есть мог передавать данные только в одном направлении: от компьютера к периферийному устройству. Но это ограничение вскоре перестало устраивать пользователей, так как на рынок массово начали выходить устройства с возможностью передачи данных в обоих направлениях. Для этого различные производители предлагали свои усовершенствования – двунаправленный, ECP, EPP и другие. Пока в 1994 году не был принят международный стандарт IEEE 1284.
Схема LPT-порта
LPT-порт называется параллельным потому, что передача данных с помощью него осуществляется по нескольким проводникам одновременно, то есть параллельно. Этот интерфейс имеет 8-битную шину для передачи данных, 5-битную шину передачи сигналов и 4-битную шину передачи состояния.
Ниже представлена схема контактов LPT-порта.

Принцип работы LPT-порта
В простейшей конфигурации, чтобы реализовать принцип работы параллельного интерфейса, хватило бы только одиннадцати проводов, а именно: 1 провод на корпус (масса), 2 провода подтверждения и 8 проводов передачи данных. Но, по общепринятому стандарту IEEE 1284, каждый из восьми проводов передачи (2-9) данных имеет отдельное заземление.
Во время передачи данных оба устройства должны сообщать друг другу сведения о своем состоянии. Это реализуется с помощью контактов 18 и 35, на которые подается напряжение 0 В либо 5 В.
По проводнику 1 передается особый сигнал STROBE, сообщающий, что компьютер установил байт данных на линии и принтер может начинать печать.
По контакту 11 передается компьютеру сигнал BUSY, сообщающий, что устройство выполняет действие (занято), обрабатывая ту информацию, что находится в буфере.
По контактам 12-14 передаются сигналы, сообщающие контрольные сигналы о состоянии принтера и конфликтах в его оборудовании.
По проводнику 12 на ПК передается информация о том, что в принтере нет бумаги. Компьютер реагирует на это передачей сигналов по линиям SELECT и ERROR и останавливает печать.
По проводнику 13 на компьютер передается информация о состоянии принтера — включен и готов или выключен и не готов.
По контакту 14 принтеру передается сигнал об автоматическом переводе строки.
По контакту 31 (16) передается сигнал о переводе принтера в начальное состояние и очищается буфер данных, т. е. все данные стираются из памяти принтера.
По контакту 32 (15) передаются все сигналы об ошибках во время передачи данных. Сигналы, передаваемые по этой линии, влияют на все остальные контакты и могут остановить печать. Например, часто возникающая ошибка принтера – Time Out, возникающая, когда принтер занят однотипной работой с данными и не может передать на ПК через сигнал BUSY, что он не готов к получению новых данных. Через некоторое время по линии ERROR на компьютер передается ошибка Time Out и новые данные не передаются. Иначе, при отсутствии сигнала ERROR, происходила бы дальнейшая передача данных, что привело бы к зависанию всей системы.
По контакту 36 (17) передается информация о готовности принтера к работе, например после устранения ошибки.
Режимы работы LPT-порта

Существует несколько режимов работы LPT-порта, которые позволяет использовать стандарт IEEE 1284:
- SPP (Standard Parallel Port) — представляет собой однонаправленный порт, который отлично совмещается в работе с интерфейсом Centronics.
- NibbleMode — использование этого порта является возможностью организации двунаправленного обмена данными в режиме SPP, при помощи использования управляющих линий (4 бит) для передачи данных от периферийного устройства к контроллеру.
- Byte Mode— режим для двустороннего обмена данными, который используется довольно редко. Его применяли в некоторых старых контроллерах до принятия стандарта IEEE 1284.
- EPP (Enhanced Parallel Port) — над разработкой этого порта работали сразу несколько известных компаний: Intel, Xircom и Zenith Data Systems. По своей работе это двунаправленный порт, который передает данные со скоростью до 2 Мбайт/сек.
- ЕСР (Extended Capabilities Port) — этот вариант порта появился в результате работы двух компаний: HP и Microsoft. У него появились уже дополнительные возможности, например, возможность аппаратного сжатия данных, присутствие буфера и способность работать в режиме DMA. Также поддерживает работу двунаправленного обмена данными (симметричного), скорость которого может быть до 2,5 Мбайт/с.
Настройка LPT-порта
Настройка LPT-порта происходит в два этапа: предварительная настройка аппаратных средств порта и текущее переключение режимов порта прикладным ПО.

Способ и возможности настройки LPT-порта зависят от его местоположения и вида исполнения. Порты, расположенные на картах расширения, обычно конфигурируются через перемычки на самих платах, а порты, размещенные напрямую на материнской плате компьютера, — через настройки BIOS.
Выборы режимов напрямую или через BIOS сами по себе не приводят к повышению скорости обмена данными между ПК и периферией, а служат для возможности выбора драйвером оптимального режима работы. Но драйверы современных устройств сами автоматически выставляют наиболее эффективные режимы работы параллельного порта, поэтому ручная настройка в большинстве случаев уже не требуется.
Виды реализаций LPT-порта
Раньше большинство производителей материнских плат размещали контроллеры LPT-port на своей продукции либо на задней панели платы. Был еще один вариант расположения. В некоторых случаях было удобно помещать контроллер на самой плате — коннекторе для подключения внешней LPT-port планки. Но с момента появления более скоростных интерфейсов для передачи данных материнских плат с распаянными LPT-портами становилось все меньше и меньше. Сейчас даже не у каждого производителя в ассортименте выпускаемой продукции имеются такие платы. И тогда на помощь приходят карты расширения, подключаемые к более современным интерфейсам:
- PCI – LPT-port. Переходник между LPT-портом и более современным разъемом PCI.
- PCI2 — LPT-port (PCI-Ex. 2.0). Переходник между LPT-портом и разъемом PCI-Ex.2.0
- USB – LPT-port. Переходник между LTP-портом и современной версией широко используемого USB-разъема.

Современное применение LPT-порта
Из-за способности параллельной передачи данных такого порта, в 70-х — 80-х годах он зарекомендовал себя одним из самых быстроработающих интерфейсов компьютера. Поэтому он использовался даже для соединения 2-х компьютеров между собой. Но эта же особенность накладывает и ограничение на максимальную длину кабеля из-за возникающих помех в соседних проводниках. Длина не может превышать 5 м, иначе искажения сигналов превышают допустимые для корректного распознавания данных.
C появлением более скоростных интерфейсов актуальность LPT-порта сошла на нет. Второе дыхание ему придали радиолюбители, которые используют его для управления собранными схемами (освещение в доме, светомузыка и другие устройства).
Несколько способов подключить принтер к компьютеру
Для подключения принтера к компьютеру, ноутбуку или телефону нужно прежде всего разобраться с портами, которые есть на принтере для подключения.
- Подключение принтера через USB порт (стандартный алгоритм)
- Подключение принтера через шнур LPT
- Как подключить принтер к локальной сети
- Как подключиться к принтеру по Wi-Fi
- Как подключить принтер с LPT портом к USB
- Принт сервер из роутера
USB порт для подключения принтера

- Внешний вид usb порта на принтере
- Внешний вид порта usb на компьютере или ноутбуке
- кабель usb для подключения принтера
- схема подключения принтера к компьютеру
Параллельный порт (LPT) порт принтера
Только очень старые принтеры не имеют на борту USB разъем, а подключаются через LPT порт

кабель для подключения принтера через LPT порт
Возможно в Вашем принтере присутствуют два порта для подключения, USB и LPT. В таком случае, предпочтение лучше отдать LPT по ряду причин, рассказ об этом долог и скучен, поверьте на слово.
Сетевой порт для подключения к локальной сети

Подключение принтера через USB порт (стандартный алгоритм)
- Соедините компьютер или ноутбук с принтером при помощи кабеля
- Включите принтер в розетку
- Если все сделали правильно, на экране появится уведомление о подключении нового устройства (принтера или МФУ).
- Запустите установочный диск и установка драйверов произойдет автоматически (для Windows 10 неактуально, мастер установки оборудования сам предложит установить драйверы для принтера)
- Проверьте состояние. Зайдите в панель управления, откройте папку «Устройства и принтеры», если установка прошла успешно, то в данном разделе появится название вашего принтера.
Как подключить устройство без диска?
Достаточно неприятная ситуация, когда установочный диск устройства несовместим с вашим ПК или же вообще вы его не нашли в комплекте. Мы расскажем вам, как подключить принтер к компьютеру без диска. Вам потребуется выполнить следующие шаги:
- Зайдите на сайт производителя.
- Выберите модель своего принтера.
- Скачайте и установите программный элемент.
После этого вы можете подключать свой принтер и пользоваться, но широкоформатная печать — только после проверки подключения и тестовой страницы печати.
Подключение принтера через шнур LPT
Некоторые принтеры подключаются к компьютеру через LPT кабель, алгоритм действий тот же что и с USB подключением.
Для начала включите принтер в розетку и вставьте шнур в разъем на компьютере. Загрузите диск с драйверами и установите их. На экране выплывет уведомление о подключении нового устройства, кликните по нему. Найдите название вашего принтера и активируйте. Моментально начнется распознавание устройства, а когда завершится, то вы сможете использовать свой принтер для печати.
Как подключить принтер к локальной сети
- Установите связь между ПК. Для этого нужен либо кабель, либо объединить домены в группу и настраивать подключение через беспроводные сети. Второй вариант на много удобнее.
- Подключите принтер через WiFi на одном компьютере.
- На оставшихся компьютерах зайдите в папку «Устройства и принтеры», что находится в панели управления. Кликнете «установка принтера».
- Открываете «Добавить сетевой, беспроводной или Bluetooth-принтер».
- Выберите название нужного принтера и кликните. В течение двух минут завершится установка.
Как подключиться к принтеру по Wi-Fi
- Включите функцию WPS на роутере. Существуют модели с отдельной кнопкой для этого. Если вы у себя не нашли такую, то активируйте ее вручную через компьютер. Как это сделать вы можете узнать благодаря инструкции вашего устройства.
- Запустите WPS у себя на принтере с помощью кнопки или же на компьютере через путь Пуск – Панель управления – Сеть – Беспроводная сеть – WiFi Protected Setup. Подключение произойдет автоматически в течение двух минут.
- После того, как произошло подключение, всплывет окно с запросом логина и пароля для принтера. Эти данные вы можете найти в инструкции.
Как подключить принтер с LPT портом к USB
Принт сервер из роутера
Обычный принтер с (usb портом) сделать сетевым можно с помощью роутера имеющего usb порт, смотрите видео:
5 способов подключения к принтеру
Для подключения принтера к компьютеру, ноутбуку или телефону нужно прежде всего разобраться с портами, которые есть на принтере для подключения.
- Подключение принтера через USB порт (стандартный алгоритм)
- Подключение принтера через шнур LPT
- Как подключить принтер к локальной сети
- Как подключиться к принтеру по Wi-Fi
- Как подключить принтер с LPT портом к USB
- Принт сервер из роутера
USB порт для подключения принтера

- Внешний вид usb порта на принтере
- Внешний вид порта usb на компьютере или ноутбуке
- кабель usb для подключения принтера
- схема подключения принтера к компьютеру
Параллельный порт (LPT) порт принтера
Только очень старые принтеры не имеют на борту USB разъем, а подключаются через LPT порт

кабель для подключения принтера через LPT порт
Возможно в Вашем принтере присутствуют два порта для подключения, USB и LPT. В таком случае, предпочтение лучше отдать LPT по ряду причин, рассказ об этом долог и скучен, поверьте на слово.
Сетевой порт для подключения к локальной сети

Так я подключаю обычно принтеры для этикеток, см. тут: A-TRADE.KZ
Подключение принтера через USB порт (стандартный алгоритм)
- Соедините компьютер или ноутбук с принтером при помощи кабеля
- Включите принтер в розетку
- Если все сделали правильно, на экране появится уведомление о подключении нового устройства (принтера или МФУ).
- Запустите установочный диск и установка драйверов произойдет автоматически (для Windows 10 неактуально, мастер установки оборудования сам предложит установить драйверы для принтера)
- Проверьте состояние. Зайдите в панель управления, откройте папку «Устройства и принтеры», если установка прошла успешно, то в данном разделе появится название вашего принтера.
Как подключить устройство без диска?
Достаточно неприятная ситуация, когда установочный диск устройства несовместим с вашим ПК или же вообще вы его не нашли в комплекте. Мы расскажем вам, как подключить принтер к компьютеру без диска. Вам потребуется выполнить следующие шаги:
- Зайдите на сайт производителя.
- Выберите модель своего принтера.
- Скачайте и установите программный элемент.
После этого вы можете подключать свой принтер и пользоваться.
Подключение принтера через шнур LPT
Некоторые принтеры подключаются к компьютеру через LPT кабель, алгоритм действий тот же что и с USB подключением.
Для начала включите принтер в розетку и вставьте шнур в разъем на компьютере. Загрузите диск с драйверами и установите их. На экране выплывет уведомление о подключении нового устройства, кликните по нему. Найдите название вашего принтера и активируйте. Моментально начнется распознавание устройства, а когда завершится, то вы сможете использовать свой принтер для печати.
Как подключить принтер к локальной сети
- Установите связь между ПК. Для этого нужен либо кабель, либо объединить домены в группу и настраивать подключение через беспроводные сети. Второй вариант на много удобнее.
- Подключите принтер через WiFi на одном компьютере.
- На оставшихся компьютерах зайдите в папку «Устройства и принтеры», что находится в панели управления. Кликнете «установка принтера».
- Открываете «Добавить сетевой, беспроводной или Bluetooth-принтер».
- Выберите название нужного принтера и кликните. В течение двух минут завершится установка.
Как подключиться к принтеру по Wi-Fi
- Включите функцию WPS на роутере. Существуют модели с отдельной кнопкой для этого. Если вы у себя не нашли такую, то активируйте ее вручную через компьютер. Как это сделать вы можете узнать благодаря инструкции вашего устройства.
- Запустите WPS у себя на принтере с помощью кнопки или же на компьютере через путь Пуск – Панель управления – Сеть – Беспроводная сеть – WiFi Protected Setup. Подключение произойдет автоматически в течение двух минут.
- После того, как произошло подключение, всплывет окно с запросом логина и пароля для принтера. Эти данные вы можете найти в инструкции.
Как подключить принтер с LPT портом к USB
Принт сервер из роутера
Обычный принтер с (usb портом) сделать сетевым можно с помощью роутера имеющего usb порт, смотрите видео:
Старый софт, LPT и современное железо
В своей прошлой статье я не был полностью честен. Перед тем, как получить рабочее устройство, я много раз проверял как мой код работает, перезаписывая его на многоразовую флеш AT28С64. И с самого начала знал что отлаживаться придется на железе, а потому встал вопрос программатора параллельных EEPROM.
Некогда крайне востребованные, а ныне необходимые только для редких специфических задач, эти программаторы стоят неприлично дорого (на этот раз серьезно). Есть бюджетные варианты, например собрать такой программатор на основе ардуины (но не весело) или быстро изобрести решение самому (но лень писать софт).
Однако, у отца оказался программатор Omega. На самом деле это не совсем программатор, это базовый блок на основе которого, теоретически, можно собрать множество разных устройств используя разные адаптеры, но один из адаптеров (имеющихся в наличии) — это универсальный программатор Orange. Но есть одна небольшая загвоздка: у меня современные компьютеры с Windows 10 и Windows 11, а этот программатор использует LPT. И нужно было как-то из этой ситуации выходить.
Эта статья о том, как можно заставить работать на новом компьтере старый софт и старое железо, рассчитанные на связь через LPT, при этом не прибегая к изменению ни оригинальных исполняемых файлов, ни схемотехники устройства. В статье речь будет идти о программаторе Omega-Orange и поставляемого к нему софту, но все описанное актуально и для других программ с другими устройствами.

Обзор возможных решений
Некогда популярный, а ныне забытый LPT — очень удобный для программиста параллельный порт. Тем не менее, все еще можно встретить его на материнских платах в том или ином виде. Сразу рассмотрим возможные варианты подключения устройства с LPT к современному компьютеру.
Реальный LPT порт
Несмотря на довольно долгую поддержку на аппаратном уровне, последнее время LPT на материнских платах или не распаивали вовсе, или распаивали только разьем для подключения, вместо распайки полноценного DB25. Сейчас же его поддержку вырезали на аппаратном уровне, но все еще существуют актуальные материнские платы где он присутствует.
Реальный LPT, адаптация софта не требуется.
Далеко не у всех есть, а дальше будет и того меньше.
Большая часть программ предполагает программирование этого порта через прямое обращение к портам ввода вывода (инструкции IN/OUT), чего винды просто так сделать не дадут. А ставить сторонний драйвер (не подписанный или подписанный слитым сертификатом) не все могут, а многие справедливо откажутся.
Шнурок USB-LPT

На рынке есть много производителей, предлагающих такое решение. Однако я не смог найти нормальную документацию на используемые чипы.
USB есть у всех.
Это не настоящий LPT-порт, а некая его абстракция, с которой можно взаимодействовать только через WINAPI, и то не совсем понятна функциональность. Похоже, существует исключительно для поддержки древних принтеров.
Даже если в прошлом пункте я ошибся, и через WINAPI все же можно гибко шевелить таким виртуальным LPT — все еще необходима адаптация софта, потому что в пространстве IO он никак не будет отображен.
PCI-LPT адаптер
Активно существует и производится, как минимум китайской компанией WCH.
Вполне себе реальный LPT-порт.
В ноутбук, увы, PCI не воткнешь.
Адреса ввода/вывода у такого порта будут сильно отличаться от стандартных (а в софте они, как правило, указаны жестко).
Так же актуален вопрос с драйверами, которые откроют доступ к IO.
В итоге решение, позволяющее подключить старое устройство с портом LPT к новому железу попросту отсутствует. У некоторых производителей есть проприетарные решения, предполагающие обновленный софт, но это не портируемые решения существующие только в области промышленного оборудования (а они ОЧЕНЬ не любят обновлять железо, некоторые до сих пор используют компьютеры PDP!). Или ищи старый компьютер и ставь его рядом с новым, или изобретай свое. Конечно же, я решил изобрести свое.
Особый подход
Итак, взявшись решить проблему самостоятельно, попутно придется изобрести пару велосипедов. Для начала, надо определиться с требованиями к решению. Я составил следующие:
Отсутствие потребности в особых драйверах.
Отсутствие необходимости изменять оригинальную программу.
Может показаться что это слишком амбициозно для проблемы, которую еще никто почему-то не решил (или я не умею гуглить). Однако, план у меня есть.
Для начала надо разобраться с тем, что же такое LPT. Я начал свою практику когда LPT уже считался критически устаревшим, и тыкал его всего пару раз интереса ради, ограничиваясь записью в регистр 888. Но тут пришлось влезть в это дело глубже.
Что такое LPT
Это параллельный порт, претерпевший в ходе своей жизни несколько переработок. Оригинальный порт имел восемь линий данных (только вывод), пять линий статуса (только ввод) и четыре линии управления (только вывод). Еще у него было аж пять линий земли, но это не так важно.
Изначально предполагалось (обычно называется legacy или ISA), что это будет специальный порт для принтера. Собственно LPT — это Line Printer Terminal. Так как считывать с принтера нечего, то данные работали только на выход, линии статуса использовались для синхронизации и определения ошибок. Тем не менее, порт был настолько прост в программировании и удобен по своей структуре, что пользователи быстро начали создавать для него свои устройства, совершенно не похожие на принтеры. Но разработчики быстро столкнулись с нехваткой линий ввода, что делало считывание с устройств крайне неудобным.
Следующая версия (обычно называется BiDir или PS/2) была практически копией своего предшественника, но имела важное отличие: направление линий данных стало переключаемым, что позволило организовать очень удобную полудуплексную передачу данных. Однако, одна из проблем продолжающих существовать с прошлой версии: порт предполагал синхронизацию ввода/вывода, но не реализовывал ее аппаратно. А потому многие программисты игнорировали ее, полагаясь что скорость их кода сама собой будет синхронизацией, и в последствии, когда компьютеры стали быстрее, пользователи заимели много головной боли, пытаясь заставить работать свои устройства, которые теперь уже не успевали за компьютером. Нормальных решений проблеме отсутствия синхронизации не существовало, так что решали проблему чем могли, например использовали специальные программы замедляющие процессор, чтобы замедлить скорость работы IO.
Необходимость вручную считывать регистр статуса, проверять состояние отдельных бит, и в зависимости от них ждать дальше или править регистр управления была ключевой для грамотной работы LPT‑порта. Производитель решил избавить программистов от этого, и так появилась реализация LPT под названием EPP (Extended Parallel Port). Сохраняя полную совместимость с предыдущими версиями, он реализовал дополнительные регистры (адреса и данных), при записи и чтения из которых линии данных, статуса и управления переходили под контроль аппаратного обеспечения, автоматически выставляя нужную комбинацию для считывания и записи, и сами ожидали подтверждения готовности от ведомого. Это значительно упрощает работу, однако детальнее мы это рассмотрим позже.
В последствии была разработана еще одна версия LPT — ECP. Повысили скорость, добавили буферизацию, и вероятно что‑то еще. Однако, он меня совершенно не интересует на данный момент, потому что в документации к моему программатору сказано что он работает исключительно в режиме EPP.
И что с этим делать
Задачу можно разделить на два этапа:
Заставить программу поверить, что у меня существует реальный LPT-порт, и она может с ним работать. Требуется программное решение.
Заставить устройство поверить что программа взаимодействует с ним через LPT-порт. Требуется аппаратное решение.
Если кому‑то кажется странным, что я собираюсь заставить работать устаревшую программу — напомню, что у винды все очень хорошо с обратной совместимостью, а софт зачастую разрабатывался для Win9x/WinXP, и единственное что не дает ему нормально работать — это необходимость иметь доступ к пространству IO, где оно ожидает LPT‑порт.
Я принципиально не хочу патчить исходную программу, потому что крайне не люблю оставлять свои следы, которые могут в последствии самым неожиданным образом сказаться на работоспособности программы (я встречал программу, которая рассчитывала адрес функций, используя хеш‑сумму своего исполняемого файла). К тому же, пачинг сделает мое решение совершенно непереносимым. А значит, надо найти способ перехватывать обращения к IO не изменяя программу.
И несмотря на то, что программу изменять я не буду, никогда не лишним будет узнать что у нее внутри. По какой‑то причине разработчик выложил на сайте программу в зашифрованном виде, и ключ к архиву выдает исключительно по запросу. Мне не очень понятен этот ход, но раз уж он так решил — не буду выкладывать внутренности программы, ограничусь скриншотами и описаниями отдельных частей.

При запуске программа выдает ошибку загрузки драйвера. Что это за драйвер — можно догадаться по лежащему в папке с программой WinIo.sys. Это один множества драйверов, которые активно использовались для доступа к пространству IO в эпоху, когда подпись у драйвера была опциональной фичей. Работали они все одинаково: программа их загружала, потом отправляла запрос на доступ к портам, а драйвер ей этот доступ выдавал. В связи с особенностью устройства линейки Windows NT, права доступа к пространству IO одни на все запущенные программы, что не очень‑то и безопасно (как и загрузка стороннего драйвера). В Linux это реализовано проще и удобнее, но это другая история.
WinIo вместе с исходниками был доступен с сайта http://www.internals.com/ (а сейчас доступен через вебархив), и для программы представлял собой библиотеку с десятью функциями:
Нас интересует InitializeWinIo , которая проверяет что драйвер запущен, и запускает его если он не запущен, и функции GetPortVal / SetPortVal , через который осуществляется доступ к портам.
Когда я увидел что WinIO предполагается в виде сторонней библиотеки — хотел порадоваться что все дело обойдется подменой dll. Однако, в данном случае используется статическая линковка.
Перехватить межмодульные вызовы несложно, и второй моей идеей было перехватывать обращения к драйверу через перехват вызова DeviceIoControl. Идея многообещающая, посмотрим что говорит документация к WinIO:
Place winio.dll, winio.vxd and winio.sys in the directory where your application’s executable file resides.
Add winio.lib to your project file by right clicking on the project name in the Visual C++ workview pane and selecting «Add Files to Project. «.
Add the #include «winio.h» statement to your source file.
Call InitializeWinIo.
Call the library’s functions to access I/O ports and physical memory.
Call ShutdownWinIo.
Тут все логично. Кидаем два файла драйвера (для NT и для Win9x) и библиотеку, инициализируем, используем функции для доступа к портам и памяти. Не знаю зачем честному человеку могло потребоваться использовать MapPhysToLin / UnmapPhysicalMemory / GetPhysLong / SetPhysLong , но прямой доступ к физической памяти затея в целом нездоровая и небезопасная (хотя и крайне веселая). Возможно, для любителей что-то рисовать на экране минуя графический драйвера винды.
Так же есть заметка относительно InitializeWinIo :
Under Windows NT/2000/XP, calling InitializeWinIo grants the application full access to the I/O address space. Following a call to this function, an application is free to use the _inp/_outp functions provided by the C run-time library to access I/O ports on the system.
И это уже куда менее веселая новость. Перехватывать исполнение инструкций не так просто, как перехватывать межмодульные вызовы. Нужно убедиться что разработчик использует вызовы к WinIO, вместо простого вызова _inp/_outp .
Впрочем, разочарование наступило когда присмотрелся к самим функциями GetPortVal / SetPortVal . Они проверяли версию системы, и если система была NT — то тоже использовали прямой вызов _inp/_outp .
Конечно, можно было бы перехватить и GetVersionEx и подменить значение, но это уже совершенно неспортивно. К тому же, программа вызывает GetVersionEx много раз, и подмена всех значений могла привести к неопределенным последствиям. Альтернативно — можно закладываться на адрес возврата, и относительно него определять какое значение необходимо вернуть. Но мне такая идея совершенно не понравилась.
Перехват вызовов от программы
Итак, нам нужно перехватить межмодульные вызовы чтобы программа запустилась, а затем перехватить выполнение инструкций IN/OUT чтобы эмулировать LPT.
Первая часть работы тривиальна: достаточно использовать один из множества способов перехвата межмодульных вызовов. Например, использовать библиотеку detours от самих майкрософт. Что приятно, с момента моего последнего использования этой библиотеки прошло много времени, и она успела стать опенсорсной https://github.com/microsoft/Detours.
Итак, что мы делаем:
Создаем Dll, которая будет выполнять перехват
Создаем лаунчер, который запустит процесс и встроит в него мою DLL
Внутри Dll перехватываются вызовы CreateFile, подменяя хендл создаваемый для \\.\WINIO
Так же перехватываем вызов DeviceIoControl с обращением к хендлу созданному в прошлом шаге, имитируя наличие драйвера
В целом, все просто. Не вижу смысла углубляться в детали, их можно посмотреть в исходниках. После того как я имитировал положительный ответ от драйвера, и приложение запустилось — оказалось что приложение не может обнаружить LPT (в основном потому что его и правда нет). Как оно это делает? Чтобы это понять я воспользовался API Monitor от rohitab. Изначально ожидалось что программа обращается к SetupAPI, но оказалось что оно проверяет реестр в разделе HKLM\HARDWARE\DEVICEMAP\PARALLEL PORTS, пытаясь вычитать оттуда список LPT-портов. Причем программе не так важно что именно она там найдет. Она честно пытается распарсить найденное значение, но на практике оказалось что в паре ключ-значение shit=pants успешно обнаруживается наличие LPT0. Конечно, можно было бы создать какое-то такое значение на работающей системе, но некрасиво будет оставлять такие артефакты, к тому же я уже вошел во вкус при перехвате вызовов, так что и проблему решим перехватом.
RegOpenKeyExA ( HKEY_LOCAL_MACHINE, «HARDWARE\DEVICEMAP\PARALLEL PORTS», . )
RegEnumValueA от предыдущего хендла
RegQueryValueExA с запросом на возвращенное во втором шаге значение
Теперь программа запускается. На этом заканчивается специфическая для моей программы часть, все описанное далее применимо к любой программе работающей с LPT.
Перехват инструкций IN/OUT через AddVectoredExceptionHandler
Можно отлавливать системные исключения при выполнении инструкций чтения портов и обрабатывать их, это элегантно и делается довольно просто, через регистрацию своего обработчика с помощью AddVectoredExceptionHandler и написания в нем простенького декодера инструкций. Конечно же, через удаленный поток или в той же встроенной Dll. Это не так сложно, тем более что инструкций всего 12 (чтение и запись, по три (8, 16, 32) на прямое указание порта и на указание порта через DX). В теории, тут всегда должна быть запись/чтение по 8, но мало ли. К тому же, эти инструкции не обновляют флагов. Однако, есть и минусы:
The handler should not call functions that acquire synchronization objects or allocate memory, because this can cause problems. Typically, the handler will simply access the exception record and return.
Технически, это предупреждение может ничего и не значить, но на практике именно из за игнорирования таких предупреждений и возникают проблемы с переносимостью.
Перехват инструкций IN/OUT отладчиком
Альтернативный вариант — запустить процесс в режиме отладки, а затем отлавливать исключения EXCEPTION_PRIV_INSTRUCTION. Когда такое исключение получено — определить какая именно инструкция ее вызвала, и имитировать ее исполнение, изменив по необходимости значения регистров данных и исправив EIP. Чтобы запустить процесс в режиме отладки достаточно создать его с флагом DEBUG_PROCESS, а затем ловить уведомления от него через WaitForDebugEvent, обрабатывать их, и возвращать управление через ContinueDebugEvent.
Тут вызывается функция process_io_exception, которая определяет что именно за инструкция вызвала исключение, и если это наша ожидаемая IN/OUT — обрабатывает ее. Если что‑то неожиданное — не обрабатывает.
Так как перехватываемых инструкций не так много — я написал небольшой дизассемблер. Звучит громко, хотя было реализовано лишь это:
Тут можно задать вопрос: а как же INS/OUTS? И уж тем более REP INS/OUTS? А никак. Добавить их поддержку можно, но не очень-то и нужно, так что пока обойдемся без них. Следующий вопрос — зачем я отлавливаю передачи размером в 16 и 32 байта, если реально обрабатываю только восьмибитные? Для полноты картины и для упрощения расширения функционала в дальнейшем.
Тем не менее, инструкции перехватываются, декомпилируются, программа считает что они работают, а это уже победа. Для начала просто игнорировались инструкции записи, а при чтении всегда возвращался ноль. Программа запустилась! «Находит» в реестре запись о порте LPT, а затем исправно начинает мучать порты IO (которые на этом этапе у меня просто логгировались в файл) и не получая от устройства ответа выдает ошибку. Заглянув в полученный файл с логом, я увидел кучу ожидаемых обращений к портам 0x37A/0x378/0x379, но помимо них так же заметил запись в порт 0x77A. О таком я услышал впервые, и погуглив, а нагуглить сейчас информацию о настолько устаревших технологиях непросто, обнаружил что этот регистр никак не упоминается в большинстве списков. Например, в списке портов реализованных в BOCHS. И это только добавило вопросов. Благо вспомнил о существовании Ralf brown interrupt list, который в своем оригинальном виде включает так же и список портов. И он говорит следующее: PORT 0778-077A — Intel 82091AA — ECP-mode PARALLEL PORT . Я плохо представляю себе тонкости работы с железом тех лет, и не уверен совместимы ли реализации ECP разных производителей, но все дальшенаписанное будет основано на документации к 82091AA. Порт 0x077A — конфигурационный порт LPT-ECP, и туда пишется значение 0x20. Значение 0x20 настраивает DATA на вход. Почему это делается именно там — не знаю, но могу предположить что это фикс для специфичной ошибки какого-то чипсета. А значит — можно просто игнорировать обращения к этому порту.
Осталось не так много — реализовать аппаратную часть.
Аппаратная часть
Что такое LPT-порт и какой он бывает уже определились. И как уже упомянул — готовые решения в виде USB-LPT шнурков не предоставляют достаточно документации, чтобы использовать их для эмуляции LPT-порта. Так что реализую свою версию.
Для реализации я выбрал микроконтроллер ATMEGA8. Почему ее? Она пятивольтовая (а LPT использует напряжения TTL, примерно 3-5 вольт), есть удобная программная реализация USB, выпускается в DIP (удобно для пайки прототипов), доступна, я умею ею пользоваться и главное — она у меня есть. Для прототипа самое то, а если по какой-то причине понадобиться еще — можно будет оперативно все это портировать на нормальный контроллер с аппаратным усб и ценой в два десятка центов, либо переразвести с ней же, но в более компактном и дешевом корпусе.
Схема получилась такой:

Тут самый минимум всего того, что может быть нужно. Я даже не стал добавлять защитных резисторов на выход LPT. Оригинальные LPT их зачастую не имели, и с удовольствием дохли от любого слегка завышенного тока, так что будем считать это дополнительным уровнем совместимости. Все необходимые для программирования пины, за исключением RESET, я использовал на разъеме, а значит для перепрошивки микроконтроллера достаточно вытащить наружу RESET, а все остальное можно взять с разъема.

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

Как известно, залог хорошего продукта — удобные средства разработки. Так что для перепрошивки контроллера я собрал вот такой адаптер под стандартный разьем AVR-ISP.


Осталось малое — написать прошивку, и для этого нужно:
Поднять USB — делается очень просто с помощью V-USB
Реализовать интерфейс LPT
Заставить работать без драйверов
Первый пункт очень прост — достаточно скопировать исходники в свой проект и USB поднимется.
Для реализации LPT надо разобраться как программировали оригинальный LPT.
Версии ISA и PS/2 имели всего три регистра:
base+0: PDATA — регистр порта данных, полудуплексная шина. У PS/2 направление определяется битом в PCON.5.
base+1: PSTAT — регистр статуса, содержит статус LPT порта и его линий. Можно вычитать: состояние линий BUSY, ACK#, PERROR, SELECT и FAULT#.
base+2: PCON — регистр настройки порта и управляющими линиями. У PS/2 настраивает направление PDATA, прерывания от LPT порта, линии SELECTIN#, INIT#, AUTOFD# и STROBE#.
У EPP есть еще два регистра:
base+3: ADDSTR — регистр, при записи/чтения данных в который генерируется последовательность передачи адреса.
base+4: DATASTR — четыре регистра с общим названием при записи/чтения в которые генерируется последовательность передачи данных.
Теоретически, EPP имеет сразу четыре регистра DATASTR, но практически определен только первый из них, а функциональность оставшихся трех зависит от реализации. Будем считать что в моей реализации их нет. А если регистры ADDSTR/DATASTR не использовать — то работать EPP будет точно как версия PS/2.
Сразу нужно заметить что некоторые линии инвертированы, а именно:
PDATA — при чтении и записи использует прямую трансляцию, то есть 1 в регистре соответствует 1 на лини.
PSTAT — ACK#, PERROR, SELECT, FAULT# — используют прямую трансляцию, а BUSY — обратную
PCON — STROBE#, AUTOFD#, SELECTIN# — используют обратную трансляцию, а INIT# — прямую.
Этого достаточно чтобы реализовать LPT. Я начал с того, что реализовал только режимы Legacy и PS/2. Не смотря на документацию к программатору он может работать не только с EPP, но так же и в режиме PS/2, так что для проверки работоспособности идеи этого будет достаточно.
Для усб-устройства (которое я гордо назвал AVRUSBLPT, или кратко AVRLPT) достаточно следующих команд:
AvrLpt_SetMode — выбор режима совместимости.
AvrLpt_SetReg — запись в указанный регистр.
AvrLpt_GetReg — чтение из указанного регистра. Все остальные команды, если они и будут — служебные (вроде AvrLpt_GetVersion)
Для обмена данными с AVRLPT я выбрал USB vendor control transfer. Углубляться не буду, скажу только что это наиболее простой способ реализации передачи сообщения по USB.
Следующая задача — отсутствие необходимости в драйвере. Решается элементарно, достаточно использовать драйвер WinUSB, который винда подгрузит автоматически если обнаружит на устройстве особый дескриптор под названием WCID. Это нестандартное расширение интерфейса USB от майкрософт, позволяющее винде использовать универсальный драйвер для устройства без необходимости согласовывать это с пользователем. Очень хорошо и подробно WCID описан тут: https://github.com/pbatard/libwdi/wiki/WCID‑Devices

Замечу следующее: WinUSB условно поддерживается начиная с Windows XP SP2 (хотя если у вас Windows XP — то и LPT наверняка есть), нормально поддерживается начиная с windows 8. За основу для своего устройства я использовал код отсюда: https://github.com/mariusgreuel/USBasp/
В итоге получилось устройство которое могло вести себя как LPT, подключается по USB, не требует драйвера и может работать в режимах Legacy и PS/2. Как ни странно, все заработало и даже фирменный софт смог увидеть программатор! Прогрмамматор периодически чудил, а когда не чудил то просто крайне медленно работал. Я не знаю что в этом обвинить как не проблемы с синхронизацией, вызванные использованием нестабильных задержек. Нужно было реализовать EPP.
Итак, EPP. Все довольно просто. При записи в регистр ADDSTR/DATASTR происходит следующее:
Хост выставляет Write# в 0
Хост выставляет данные на шину DATA
Хост выставляет data strobe# опускается в 0 (если это DATASTR) или addr strobe# в 0 (если это ADDSTR)
Устройство выставляет Wait# в 1
Хост выставляет data strobe# в 1 (если это DATASTR) или addr strobe# в 1 (если это ADDSTR)
Хост выставляет Write# в 1
Устройство выставляет Wait# в 0
Увы, разные производители документируют эту последовательность по разному. Ниже две последовательности: из документации Intel и из документации National Semiconductor. Разница ощутимая, но я предпочел второй алгоритм.


Все бы хорошо и понятно, но остаются вопросы о таймингах и таймаутах. Совершенно не ясно какие задержки должны быть реализованы, через какое время считать что данные не переданы, как сигнализировать об ошибке и как обрабатывать ситуацию если в начале передачи Wait# изначально высокий. В интернете есть упоминания таймаутов в 5, 10 или 15 мкс, возьму за основу 10. Так же вызывает вопрос предварительная инициализация линий порта. Судя по всему, это зависит от реализации. Так же кое‑где упоминается что младший бит PSTAT может быть флагом таймаута, но в документации на 82 091 этого нет. Пришлось откопать документацию на PC87 338 (другая реализация SuperIO) и посмотреть там. Там он описан так: действует только при EPP, в нормальном режиме 0, если произошел таймаут — устанавливается в 1, и сбрасывается при чтении. Так и реализую.
После реализации EPP глюки пропали, а скорость работы значительно выросла, хотя и осталось далекой от ожидаемой.
Первая мысль — буферизировать операции вывода, но на практике большая часть обращений к портам это поллинг регистра PSTAT, а операции записи идущие друг за другом последовательно — явление крайне редкое.
Ускоряем работу перехвата IN/OUT
Тест показал что обработка одного исключения, не считая обращения к AVRLPT занимает примерно 101 мкс, а вместе с обращением — уже около 381 мкс. То есть если исключить из этого время обработки прерываний — то каждое обращение к порту будет занимать около 280 мкс. Все еще много, но уже лучше. А при условии что такие обращения происходят тысячами — выигрыш во времени (на треть быстрее!) уже заметный.
Как я говорил, патчить исполняемый файл очень сильно не хочется — это не только лишает решение портируемости (на потенциальные новые версии этой же программы) и универсальности (на другие программы), но и чрезвычайно скучно.
Другое дело — патчить сразу в памяти. Отлавливаем обращение к IO, проверяем размер, создаем процессу‑жертве новый клок памяти, куда копируем нужный код и по адресу где произошло исключение вставляем заплатку, вызывающую мой патч. Так как вызов по 32-х битному адресу занимает 5 байт, а инструкция чтения/записи не более 3 — придется еще и часть инструкций переносить.
Финальный штрих, патчинг программы прямо в памяти. План дествий такой:
В Dll, которая подгружается в процесс, добавляем функции работы с AVRLPT
После подгрузки этой Dll получаем адреса искомых функций
После запуска процесса ждем исключений UNPRIVILEDGED INSTRUCTION
По адресу исключения определяем точный тип инструкции IN/OUT, вырезаем ее
Дизассемблером длин определяем длину следующей инструкции, повторяем пока освободившегося места не хватит для LONG JMP
Все вырезанные инструкции копируем в буфер
Добавляем в этот же буфер код обработки IO
Добавляем в этот же буфер адреса функций работы с AVRLPT
Добавляем в этот же буфер LONG JMP обратно на место исключения + длина LONG JMP
Буфер внедряем в адресное пространство обрабатываемого процесса
На месте с вырезанными инструкциями добавляем LONG JMP на ранее подготовленный буфер, при необходимости дополняем инструкциями NOP
Дизассемблер длин был использован этот: https://github.com/greenbender/lend, но немного доработан для предотвращения выхода за пределы буфера.
В конце концов нашелся плюс от того что EIP нельзя использовать как адресный регистр. Однако, если вызов был в конце одной функции, сразу за которой начинается другая, то такой патч испортит вызов следующей функции. Будем надеяться что такое происходит не слишком часто, потому что однозначного способа предотвратить это я не вижу. Аналогичная проблема возникнет если выше будет осуществляться условный переход на адреса следующие прямо за вызовом IN/OUT. Теоретически, можно реализовать алгоритм поиска свободного места и адаптивным патчингом, но это огромный пласт работы который я делать не хочу. Остается лишь надеяться что применимо к программе программатора этот метод будет работать не создавая ошибок, но всегда можно откатиться на версию осуществляющую перехват без патчинга.
Может показаться что логичнее было бы не использовать два LONG JMP и уникальный кусок кода для каждого вызова, а ограничится CALL и универсальными функциями для ввода и для вывода. Однако, мы копируем себе следующую за IN/OUT инструкцию, и это вполне может оказаться инструкция работы со стеком, так что стоит оставить стек в том виде, в котором он ожидается.
Это решение замедлит обработку первого исключения по каждому из адресов, но в последствии IO будет работать быстрее. Можно, конечно, представить себе синтетическую ситуацию, где обрабатываемый процесс постоянно создает новые адресные пространства с вызовами IO, и тогда получится что мы генерируем код для генерированного кода, что приведет к неконтролируемому нарастанию потребления памяти в целевом процессе.
Инструкцию LONG JMP можно осуществить только со сменой страницы, и мне не хочется вникать в вопрос всегда ли винда использует для кода одну и ту же страницу, так что эту инструкцию отбрасываем. Зато в те же шесть байт помещается PUSH DWORD+RET, что по сути тот же LONG JMP, но без смены страницы. Его и использовал.
После реализации метода с патчингом в памяти скорость обмена данными увеличилась и программу теперь использовать довольно комфортно. Однако скорость выросла не так сильно как хотелось бы, и зависания интерфейса остались, так как разработчик использует один поток на все операции. В моем случае скорость программы ограничивается искусственными (и сильно завышенными) задержками, которые используются при обращении к микросхеме памяти.
Патчинг в памяти, как бы он ни был хорош в моем случае, может сломать программу в прочих случаях. А потому я на всякий случай оставил возможность собрать инжектор в режиме обработки при перехвате — это определяется флагом препроцессора MEMORY_PATCH_MODE .
Вывод: адаптировать таким образом старые приложения под новые реалии можно, но скорость обмена данными страдает. И это может быть фатально в тех случаях, когда разработчик не реализовал синхронизацию (а это, увы, случается, хотя и не мой случай). Приборы, рассчитывающие на скорость обмена данных, вроде логических анализаторов втыкаемых в LPT работать не будут.
Вывод
Используя описанный метод можно подключить устройство рассчитанное на работу с LPT-портом используя LPT-USB адаптер, а мой софт сможет перехватить «сырые» обращения к LPT и перенаправить их через адаптер. Скорость работы немного пострадает, но это не должно быть критично в большинстве случаев. Да, большинству устаревших устройств можно дать вторую жизнь. А если очень хочется — то можно даже снова что-то разработать под LPT.
Послесловие
VID/PID у моего USBLPT используются нелегально, запрос на их выделение отправлю в ближайшем будущем.
Мое решение может потребовать незначительной доработки (ECP, INS/OUTS, REP INS/OUTS) для использования с другими программами, использующими LPT. Я мог бы заняться и этим, но для решения моей задачи это излишне.
В статье, на схеме и в прошивке может наблюдаться некоторая неразбериха в названиях выводов LPT‑порта, связанная с тем, что разные версии имеют разные названия, и некоторые названия хоть и не официальные — но прижившиеся. В разных случаях я использую разные названия, но на схеме устройства есть табличка проясняющая этот момент.
Если обнаружится что какой‑то из многочисленных USB‑LPT адаптеров позволяет реализовать все то, что я реализовал через AVRLPT — один из велосипедов можно будет исключить, оставив лишь самое важное — пехеват и перенаправление.
Как подключить старый принтер LPT к USB
Проблема не нова, но постоянно появляются пользователи, с ней не справляющиеся. При обновлении парка ПК дома или на фирме или при переходе на ноутбук у пользователя возникает проблема: как же подключить старый принтер? На новой технике LPT портов нет, одни USB. Почитали мы форумы на эту тему и от души повеселились. Каких только вариантов не предлагают, а оно у них не работает. На самом деле всё просто донельзя. Давайте рассмотрим варианты, которые предлагались, — и в конце воспользуемся нашим.
А началось всё с того, что буквально вчера достался мне бесплатно старый добрый лазерный HP 6L . И причём абсолютно случайно. Иду я домой, а он бедный стоит одиноко возле жбана и дожидается, когда его мусорник вывезет. И причём в идеальном состоянии дожидается, как оказалось, даже картридж полный. Жалко мне стало беднягу. Машинка-то хорошая, хоть и медленная. Дай думаю своему малому для школы подцеплю, для рефератов подойдёт. А ведь сколько таких аппаратов, которые по выносливости способны пережить ещё не одно поколение современных принтеров, без дела пылится и выкидывается пользователями и фирмами с тупыми админами только потому, что они считают невозможным их подключение к современному ПК или ноутбуку и сами принтеры медленными? И зря. На старых принтерах картриджи не чипованные, и их можно перезаправлять и восстанавливать сколько хочешь — в отличии от современных. Да и тонера влазит больше (хватает на дольше).
Но это только начало истории. Малому своему обновил аппарат недавно и специально под современные игры, так что как Вы понимаете оборудование — самое современное и операционка Windows 7 на борту. Конечно же, никаких портов LPT и в помине нет. Хотя знаем как цеплять, решили почитать, что народ на форумах делать пробует и какими методами пользуется. И вот тут, полазив по нескольким десятков форумов, долго катались со смеху. Ни на одном самое простое и дешёвое подключение не озвучено. Чего только народ не предлагает бедным мученикам со старой техникой. И поставить дома старый дешёвый компьютер с LPT портом в качестве сервера (в русских квартирах как раз много места и подходящий дизайн для старого ненужного ПК) . И, купить принтсервер коробку (1200 руб самая дешёвая и не факт, что состыкуется дешёвая). И всунуть контроллер LPT в ПК (сам контроллер 350 руб + кабель). Контроллеры такого типа, мягко говоря, не всегда стыкуются даже не с самым современным ПК, к тому же в ноут его не вставишь. И разыскивают спец драйвера для переходников под Windows Vista и 7 (которых не существует даже на официальных сайтах производителя). И списываются со спецами из HP, которые разводят руками и не знают как состыковать по кабелю технику своего же производства.
На самом деле старые модели известных брэндов прекрасно стыкуются с современными ПК на Vista и 7 по самому обычному кабелю-переходнику LPT-USB (300 руб примерно). Драйвера старых известных брэндов уже встроены в современные операционные системы и они опознаются и устанавливаются автоматически даже на 64 битные версии, как только Вы подключите оборудование. И конечно оно у Вас печатать не будет, если Вы полезете на форумы и поначитаетесь всякой мути. Нужно произвести всего лишь одну маленькую настройку в свойствах принтера (подходит для большинства старых брэндов). Если ВЫ подцепили кабель и оборудование определилось, то всё у Вас получится. Единственное, если Вы взяли левый кабель, то и определяться будет нечему. Несмотря на высокую цену картриджей для старых моделей принтеров, они многократно окупят себя за счёт перезаправки по сравнению с современными и, скорее всего, переживут новый купленный принтер. А теперь о деле.
Заходим «Пуск», пункт «Настройка», пункт «Принтеры». В появившемся окне находим значок Вашего установившегося принтера. Становимся на него курсором мыши и жмём правую клавишу мыши. В появившемся меню щёлкаем по пункту «Свойства». В появившемся окне «Свойства принтера» переходим на закладку «Дополнительно». Находим кнопку «Обработчик печати» и жмём. В появившемся окне «Обработчик заданий печати» в поле «Тип данных по умолчанию» выбираем вариант «TEXT”. Жмём везде «ОК» и «Применить» и наслаждаемся печатью.
Имейте ввиду, что Вам может быть отказано в изменении параметров, если Вы насовали заданий в принтер. Сначала нужно очистить очередь печати. Для этого щёлкаем перед началом операций по значку принтера и в появившемся окне щелкаем меню «Принтер», пункт «Очистить очередь печати» и ждём, пока все задания удалятся. Для ускорения очистки памяти можно выключить питание принтера и перезагрузить комп перед продолжением настройки.
