Document TSR communication driver
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 4 Aug 2025 20:13:22 +0000 (23:13 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 4 Aug 2025 20:13:22 +0000 (23:13 +0300)
Networking/LPT communication driver/diagram.png [new file with mode: 0644]
Networking/LPT communication driver/index.html [new file with mode: 0644]
Networking/LPT communication driver/index.org
Tools/Update web site
index.org

diff --git a/Networking/LPT communication driver/diagram.png b/Networking/LPT communication driver/diagram.png
new file mode 100644 (file)
index 0000000..baf5bee
Binary files /dev/null and b/Networking/LPT communication driver/diagram.png differ
diff --git a/Networking/LPT communication driver/index.html b/Networking/LPT communication driver/index.html
new file mode 100644 (file)
index 0000000..cf57632
--- /dev/null
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<!-- 2025-08-04 ma 02:12 -->
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>LPT Communication Driver</title>
+<meta name="generator" content="Org Mode" />
+<style>
+  #content { max-width: 60em; margin: auto; }
+  .title  { text-align: center;
+             margin-bottom: .2em; }
+  .subtitle { text-align: center;
+              font-size: medium;
+              font-weight: bold;
+              margin-top:0; }
+  .todo   { font-family: monospace; color: red; }
+  .done   { font-family: monospace; color: green; }
+  .priority { font-family: monospace; color: orange; }
+  .tag    { background-color: #eee; font-family: monospace;
+            padding: 2px; font-size: 80%; font-weight: normal; }
+  .timestamp { color: #bebebe; }
+  .timestamp-kwd { color: #5f9ea0; }
+  .org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
+  .org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
+  .org-center { margin-left: auto; margin-right: auto; text-align: center; }
+  .underline { text-decoration: underline; }
+  #postamble p, #preamble p { font-size: 90%; margin: .2em; }
+  p.verse { margin-left: 3%; }
+  pre {
+    border: 1px solid #e6e6e6;
+    border-radius: 3px;
+    background-color: #f2f2f2;
+    padding: 8pt;
+    font-family: monospace;
+    overflow: auto;
+    margin: 1.2em;
+  }
+  pre.src {
+    position: relative;
+    overflow: auto;
+  }
+  pre.src:before {
+    display: none;
+    position: absolute;
+    top: -8px;
+    right: 12px;
+    padding: 3px;
+    color: #555;
+    background-color: #f2f2f299;
+  }
+  pre.src:hover:before { display: inline; margin-top: 14px;}
+  /* Languages per Org manual */
+  pre.src-asymptote:before { content: 'Asymptote'; }
+  pre.src-awk:before { content: 'Awk'; }
+  pre.src-authinfo::before { content: 'Authinfo'; }
+  pre.src-C:before { content: 'C'; }
+  /* pre.src-C++ doesn't work in CSS */
+  pre.src-clojure:before { content: 'Clojure'; }
+  pre.src-css:before { content: 'CSS'; }
+  pre.src-D:before { content: 'D'; }
+  pre.src-ditaa:before { content: 'ditaa'; }
+  pre.src-dot:before { content: 'Graphviz'; }
+  pre.src-calc:before { content: 'Emacs Calc'; }
+  pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
+  pre.src-fortran:before { content: 'Fortran'; }
+  pre.src-gnuplot:before { content: 'gnuplot'; }
+  pre.src-haskell:before { content: 'Haskell'; }
+  pre.src-hledger:before { content: 'hledger'; }
+  pre.src-java:before { content: 'Java'; }
+  pre.src-js:before { content: 'Javascript'; }
+  pre.src-latex:before { content: 'LaTeX'; }
+  pre.src-ledger:before { content: 'Ledger'; }
+  pre.src-lisp:before { content: 'Lisp'; }
+  pre.src-lilypond:before { content: 'Lilypond'; }
+  pre.src-lua:before { content: 'Lua'; }
+  pre.src-matlab:before { content: 'MATLAB'; }
+  pre.src-mscgen:before { content: 'Mscgen'; }
+  pre.src-ocaml:before { content: 'Objective Caml'; }
+  pre.src-octave:before { content: 'Octave'; }
+  pre.src-org:before { content: 'Org mode'; }
+  pre.src-oz:before { content: 'OZ'; }
+  pre.src-plantuml:before { content: 'Plantuml'; }
+  pre.src-processing:before { content: 'Processing.js'; }
+  pre.src-python:before { content: 'Python'; }
+  pre.src-R:before { content: 'R'; }
+  pre.src-ruby:before { content: 'Ruby'; }
+  pre.src-sass:before { content: 'Sass'; }
+  pre.src-scheme:before { content: 'Scheme'; }
+  pre.src-screen:before { content: 'Gnu Screen'; }
+  pre.src-sed:before { content: 'Sed'; }
+  pre.src-sh:before { content: 'shell'; }
+  pre.src-sql:before { content: 'SQL'; }
+  pre.src-sqlite:before { content: 'SQLite'; }
+  /* additional languages in org.el's org-babel-load-languages alist */
+  pre.src-forth:before { content: 'Forth'; }
+  pre.src-io:before { content: 'IO'; }
+  pre.src-J:before { content: 'J'; }
+  pre.src-makefile:before { content: 'Makefile'; }
+  pre.src-maxima:before { content: 'Maxima'; }
+  pre.src-perl:before { content: 'Perl'; }
+  pre.src-picolisp:before { content: 'Pico Lisp'; }
+  pre.src-scala:before { content: 'Scala'; }
+  pre.src-shell:before { content: 'Shell Script'; }
+  pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
+  /* additional language identifiers per "defun org-babel-execute"
+       in ob-*.el */
+  pre.src-cpp:before  { content: 'C++'; }
+  pre.src-abc:before  { content: 'ABC'; }
+  pre.src-coq:before  { content: 'Coq'; }
+  pre.src-groovy:before  { content: 'Groovy'; }
+  /* additional language identifiers from org-babel-shell-names in
+     ob-shell.el: ob-shell is the only babel language using a lambda to put
+     the execution function name together. */
+  pre.src-bash:before  { content: 'bash'; }
+  pre.src-csh:before  { content: 'csh'; }
+  pre.src-ash:before  { content: 'ash'; }
+  pre.src-dash:before  { content: 'dash'; }
+  pre.src-ksh:before  { content: 'ksh'; }
+  pre.src-mksh:before  { content: 'mksh'; }
+  pre.src-posh:before  { content: 'posh'; }
+  /* Additional Emacs modes also supported by the LaTeX listings package */
+  pre.src-ada:before { content: 'Ada'; }
+  pre.src-asm:before { content: 'Assembler'; }
+  pre.src-caml:before { content: 'Caml'; }
+  pre.src-delphi:before { content: 'Delphi'; }
+  pre.src-html:before { content: 'HTML'; }
+  pre.src-idl:before { content: 'IDL'; }
+  pre.src-mercury:before { content: 'Mercury'; }
+  pre.src-metapost:before { content: 'MetaPost'; }
+  pre.src-modula-2:before { content: 'Modula-2'; }
+  pre.src-pascal:before { content: 'Pascal'; }
+  pre.src-ps:before { content: 'PostScript'; }
+  pre.src-prolog:before { content: 'Prolog'; }
+  pre.src-simula:before { content: 'Simula'; }
+  pre.src-tcl:before { content: 'tcl'; }
+  pre.src-tex:before { content: 'TeX'; }
+  pre.src-plain-tex:before { content: 'Plain TeX'; }
+  pre.src-verilog:before { content: 'Verilog'; }
+  pre.src-vhdl:before { content: 'VHDL'; }
+  pre.src-xml:before { content: 'XML'; }
+  pre.src-nxml:before { content: 'XML'; }
+  /* add a generic configuration mode; LaTeX export needs an additional
+     (add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
+  pre.src-conf:before { content: 'Configuration File'; }
+
+  table { border-collapse:collapse; }
+  caption.t-above { caption-side: top; }
+  caption.t-bottom { caption-side: bottom; }
+  td, th { vertical-align:top;  }
+  th.org-right  { text-align: center;  }
+  th.org-left   { text-align: center;   }
+  th.org-center { text-align: center; }
+  td.org-right  { text-align: right;  }
+  td.org-left   { text-align: left;   }
+  td.org-center { text-align: center; }
+  dt { font-weight: bold; }
+  .footpara { display: inline; }
+  .footdef  { margin-bottom: 1em; }
+  .figure { padding: 1em; }
+  .figure p { text-align: center; }
+  .equation-container {
+    display: table;
+    text-align: center;
+    width: 100%;
+  }
+  .equation {
+    vertical-align: middle;
+  }
+  .equation-label {
+    display: table-cell;
+    text-align: right;
+    vertical-align: middle;
+  }
+  .inlinetask {
+    padding: 10px;
+    border: 2px solid gray;
+    margin: 10px;
+    background: #ffffcc;
+  }
+  #org-div-home-and-up
+   { text-align: right; font-size: 70%; white-space: nowrap; }
+  textarea { overflow-x: auto; }
+  .linenr { font-size: smaller }
+  .code-highlighted { background-color: #ffff00; }
+  .org-info-js_info-navigation { border-style: none; }
+  #org-info-js_console-label
+    { font-size: 10px; font-weight: bold; white-space: nowrap; }
+  .org-info-js_search-highlight
+    { background-color: #ffff00; color: #000000; font-weight: bold; }
+  .org-svg { }
+</style>
+<style type="text/css">
+               body {
+                 max-width: 35cm !important;
+               }
+               #content {
+                 max-width: 80em !important;
+                 width: 90%;
+               }
+               </style>
+<link rel="stylesheet" type="text/css" href="https://thomasf.github.io/solarized-css/solarized-dark.min.css" />
+</head>
+<body>
+<div id="content" class="content">
+<h1 class="title">LPT Communication Driver</h1>
+<div id="table-of-contents" role="doc-toc">
+<h2>Table of Contents</h2>
+<div id="text-table-of-contents" role="doc-toc">
+<ul>
+<li><a href="#orga5cdfb9">1. Overview</a></li>
+<li><a href="#org933ae73">2. LPT Communication Driver</a>
+<ul>
+<li><a href="#org193f48a">2.1. Overview</a></li>
+<li><a href="#orge83c8c0">2.2. Data transmission implementation details</a></li>
+<li><a href="#org096cdc9">2.3. Driver API</a>
+<ul>
+<li><a href="#orgcf7457f">2.3.1. Deactivate the driver</a></li>
+<li><a href="#orgb25a6e0">2.3.2. Activate the driver</a></li>
+<li><a href="#org50591bb">2.3.3. Retrieve downloaded data from the driver's input buffer</a></li>
+<li><a href="#org0867f81">2.3.4. Upload data to the driver's output buffer for transmission</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-orga5cdfb9" class="outline-2">
+<h2 id="orga5cdfb9"><span class="section-number-2">1.</span> Overview</h2>
+<div class="outline-text-2" id="text-1">
+<p>
+This is weird networking solution. It allows to send data using
+parallel LPT port serially(!) by bit-banging between two connected
+computers :)
+</p>
+
+<p>
+Out of <a href="https://en.wikipedia.org/wiki/Parallel_port">25 physical wires in LPT port</a>, only 3 are used:
+</p>
+
+<dl class="org-dl">
+<dt>Pin 14</dt><dd>Carries a synchronization signal which uses a periodic
+pattern (e.g., 010101&#x2026;) to maintain timing alignment between the
+communicating computers.</dd>
+
+<dt>Pin 17</dt><dd>Functions as the bidirectional data line, responsible for
+transmitting and receiving data between the connected computers.</dd>
+
+<dt>Pin 18</dt><dd>Acts as the ground connection, providing a common
+reference for electrical signals to ensure consistency in
+communication.</dd>
+</dl>
+
+
+<div id="org3425788" class="figure">
+<p><img src="diagram.png" alt="diagram.png" />
+</p>
+</div>
+
+<p>
+By utilizing only three wires and software controlled bit-banging
+algorithm, custom, comparatively simple, cheap and long cable can be
+built to connect 2 computers in a DIY network setup.
+</p>
+</div>
+</div>
+
+<div id="outline-container-org933ae73" class="outline-2">
+<h2 id="org933ae73"><span class="section-number-2">2.</span> LPT Communication Driver</h2>
+<div class="outline-text-2" id="text-2">
+</div>
+<div id="outline-container-org193f48a" class="outline-3">
+<h3 id="org193f48a"><span class="section-number-3">2.1.</span> Overview</h3>
+<div class="outline-text-3" id="text-2-1">
+<p>
+The LPT Communication Driver is a Terminate and Stay Resident (TSR)
+driver designed to facilitate communication between computers using
+parallel printer ports (LPT). This driver uses bit-banging to send and
+receive data serially over the LPT port, utilizing only three wires
+for communication.
+</p>
+
+<p>
+Driver hooks into the system's IRQ 0 to ensure that system timer
+always keeps executing it in the background. While operating as a
+background process, it periodically monitors LPT port to detect
+incoming transmission. When transmission is detected, driver receives,
+decodes and stores it into preallocated 5000 byte receive buffer.
+</p>
+
+<p>
+Applications can then communicate with the driver on their own
+schedule using INT 63h to poll for and retrieve received messages, if
+any. Applications can also send outgoing messages into 5000 byte
+driver outbox memory buffer. Thereafter driver will transmit those
+messages in background mode over the wire.
+</p>
+
+<p>
+The driver is half-duplex: it prioritizes receiving over sending and
+does not transmit while receiving.
+</p>
+
+<p>
+During active transmission/reception, the driver can consume 100% CPU
+due to busy-wait loops in the IRQ handler, potentially causing random
+temporary hiccups for application running in the
+foreground. Unsuitable for real-time systems.
+</p>
+
+<p>
+Download:
+</p>
+<ul class="org-ul">
+<li>Source code: <a href="lptdrv.asm">lptdrv.asm</a></li>
+<li>Compiled binary: <a href="lptdrv.com">lptdrv.com</a></li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-orge83c8c0" class="outline-3">
+<h3 id="orge83c8c0"><span class="section-number-3">2.2.</span> Data transmission implementation details</h3>
+<div class="outline-text-3" id="text-2-2">
+<p>
+When there is incoming data transmission, the TSR driver detects it
+during its periodic execution in the IRQ 0 (timer) handler, which runs
+approximately every 55ms.
+</p>
+
+<p>
+Driver checks for possible transmission comparatively rarely (every 55
+ms) and for this reason it is important to have quite long
+transmission start indicator/header before actual data is sent. This
+allows long enough time for recipient computer communication driver to
+detect that transmission line is active and start listening to it now
+in exclusive busy-wait loop. So, the TSR driver steals 100% of the CPU
+for the duration of transmission.
+</p>
+
+<p>
+The start of transmission is detected by reading the port (37Ah)
+value, and checking if bit 3 of the raw input is high. It then enters
+a "skip header" loop. Once bit 1 goes low, bit reception begins. The
+end is detected via a timeout: during bit reception, a counter
+increments on each poll. If there is no change in the port value for
+30 consecutive polls (indicating no new bit transition), it assumes
+the transmission is complete, appends a 2-byte length to the receive
+buffer, and exits the routine.
+</p>
+
+<p>
+So, both receive and send routines execute within the IRQ 0 handler
+using busy-wait polling loops (for receive) or timed output loops (for
+send). These can hold the CPU for the full duration of a transmission,
+as the handler does not yield until complete.
+</p>
+
+<p>
+It bit-bangs data by treating the LPT control port (37Ah) as both
+output and input, using bit 3 (pin 17, data line) for the serial data
+bit and bit 1 (pin 14, sync line) for a clock that alternates (low on
+even bit indices, high on odd) to signal transitions.
+</p>
+
+<p>
+For sending: the port is first set to 0xFF (all bits high) as a
+header, held for ~110ms (2 timer ticks).
+</p>
+
+<p>
+For receiving: after start detection, it polls the port for value
+changes (transitions). On each change, it reads the port again (to
+ensure that synchronization bit did not arrive ahead data bit over the
+separate physical wire), extracts bit 3 as the data bit, shifts it
+into a byte accumulator, and resets the timeout counter. Once a full
+byte is accumulated, it stores it in the receive buffer. No ACK or
+error checking.
+</p>
+
+<p>
+Driver can receive multiple transmissions into its 5000-byte receive
+buffer before the client program reads it out. Each incoming
+transmission appends its data bytes followed by a 2-byte length word
+directly to the end of the buffer (updating dbufsiz to the new total
+used). As long as the cumulative size doesn't exceed 5000 bytes,
+multiple can queue up. The buffer acts as a FIFO for concatenated
+packets; overflows are not handled (it would corrupt without
+checks). The client retrieves the entire buffer contents at once when
+polling.
+</p>
+
+<p>
+When the client program reads received data from the TSR (via INT 63h
+AH=2), the driver copies the full buffer contents (up to dbufsiz
+bytes) to the client's specified ES:DI pointer, returns the byte count
+in AX, and immediately resets dbufsiz to 0, clearing the buffer. This
+ensures the data is not served again on subsequent reads, as the
+buffer is emptied after each retrieval. If no data is available, AX=0
+is returned.
+</p>
+</div>
+</div>
+
+<div id="outline-container-org096cdc9" class="outline-3">
+<h3 id="org096cdc9"><span class="section-number-3">2.3.</span> Driver API</h3>
+<div class="outline-text-3" id="text-2-3">
+<p>
+The driver uses INT 63h for its API, with functions selected via the
+AH register. It maintains two internal buffers:
+</p>
+
+<ul class="org-ul">
+<li>Download Buffer: 5000 bytes for incoming (received) data. Multiple
+transmissions can be queued here, each appended with a 2-byte length
+footer.</li>
+<li>Upload Buffer: 5000 bytes for outgoing (to-be-sent) data. Data is
+copied here and transmitted when the line is free.</li>
+</ul>
+
+
+<p>
+Communication is polling-based for applications; the driver handles
+transmission/reception in the background.
+</p>
+
+<p>
+No error checking, acknowledgments, or flow control; it's a simple,
+unidirectional-per-turn protocol.
+</p>
+
+<p>
+To use the driver:
+</p>
+<ul class="org-ul">
+<li>Load the TSR (e.g., run lptdrv.com).</li>
+<li>Activate it via the API.</li>
+<li>Poll for received data or queue sends as needed.</li>
+<li>Deactivate when done.</li>
+</ul>
+
+<p>
+API overview:
+</p>
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col  class="org-right" />
+
+<col  class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-right">AH register</th>
+<th scope="col" class="org-left">API function</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="org-right">0</td>
+<td class="org-left"><a href="#orgcf7457f">Deactivate the driver</a></td>
+</tr>
+
+<tr>
+<td class="org-right">1</td>
+<td class="org-left"><a href="#orgb25a6e0">Activate the driver</a></td>
+</tr>
+
+<tr>
+<td class="org-right">2</td>
+<td class="org-left"><a href="#org50591bb">Retrieve downloaded data from the driver's input buffer</a></td>
+</tr>
+
+<tr>
+<td class="org-right">3</td>
+<td class="org-left"><a href="#org0867f81">Upload data to the driver's output buffer for transmission</a></td>
+</tr>
+</tbody>
+</table>
+</div>
+
+<div id="outline-container-orgcf7457f" class="outline-4">
+<h4 id="orgcf7457f"><span class="section-number-4">2.3.1.</span> Deactivate the driver</h4>
+<div class="outline-text-4" id="text-2-3-1">
+<p>
+Disables the driver, stopping background monitoring and
+transmission. The LPT port is reset (set to 0).
+</p>
+
+<ul class="org-ul">
+<li>AH: 0</li>
+<li>Parameters: None</li>
+<li>Returns: None</li>
+<li>Side Effects: Clears the enabled flag.</li>
+<li>Usage Notes: Call this before unloading the TSR or when
+communication is no longer needed to free system resources.</li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-orgb25a6e0" class="outline-4">
+<h4 id="orgb25a6e0"><span class="section-number-4">2.3.2.</span> Activate the driver</h4>
+<div class="outline-text-4" id="text-2-3-2">
+<p>
+Enables the driver, starting background LPT port monitoring for
+incoming data. The LPT port is reset (set to 0) upon activation.
+</p>
+
+<ul class="org-ul">
+<li>AH: 1</li>
+<li>Parameters: None</li>
+<li>Returns: None</li>
+<li>Side Effects: Sets the enabled flag. Existing buffer contents are
+preserved.</li>
+<li>Usage Notes: Must be called after loading the TSR and before any
+send/receive operations. Can be called multiple times; redundant
+activations are harmless.</li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-org50591bb" class="outline-4">
+<h4 id="org50591bb"><span class="section-number-4">2.3.3.</span> Retrieve downloaded data from the driver's input buffer</h4>
+<div class="outline-text-4" id="text-2-3-3">
+<p>
+Copies all accumulated received data from the driver's download buffer
+to the caller's memory location and clears the buffer.
+</p>
+
+<ul class="org-ul">
+<li><b>AH</b> : 2</li>
+<li><b>Parameters</b> :
+<ul class="org-ul">
+<li><b>ES:DI</b> : Pointer to the buffer where received data should be
+copied (must be large enough to hold up to 5000 bytes).</li>
+</ul></li>
+<li><b>Returns</b> :
+<ul class="org-ul">
+<li><b>AX</b> : Number of bytes copied (0 if no data available).</li>
+</ul></li>
+<li><b>Side Effects</b> : Resets the download buffer size to 0, preventing
+re-retrieval of the same data.</li>
+<li><b>Usage Notes</b> :
+<ul class="org-ul">
+<li>Data is retrieved as a concatenated stream of all queued
+transmissions.</li>
+<li>Each transmission in the buffer ends with a 2-byte length word
+(little-endian) indicating its payload size (excluding the length
+itself).</li>
+<li>Poll this function periodically in a loop to check for new data.</li>
+<li>If AX=0, no copy occurs.</li>
+<li>Example: In assembly, set ES:DI to your receive buffer and call
+INT 63h; then process AX bytes if &gt;0.</li>
+</ul></li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-org0867f81" class="outline-4">
+<h4 id="org0867f81"><span class="section-number-4">2.3.4.</span> Upload data to the driver's output buffer for transmission</h4>
+<div class="outline-text-4" id="text-2-3-4">
+<p>
+Copies the specified data to the driver's upload buffer for background
+transmission. Transmission occurs when the line is free (no incoming
+data).
+</p>
+
+<ul class="org-ul">
+<li><b>AH</b> : 3</li>
+<li><b>Parameters</b> :
+<ul class="org-ul">
+<li><b>DS:SI</b> : Pointer to the data to upload.</li>
+<li><b>CX</b> : Number of bytes to upload (must not exceed remaining upload
+buffer space; no checks performed).</li>
+</ul></li>
+<li><b>Returns</b> : None</li>
+<li><b>Side Effects</b> : Appends data to the upload buffer and updates its
+size. Transmission is asynchronous.</li>
+<li><b>Usage Notes</b> :
+<ul class="org-ul">
+<li>Data is sent as a single transmission (no automatic framing;
+caller can add headers if needed).</li>
+<li>If the buffer is full (total &gt;5000 bytes), behavior is undefined
+(overflow).</li>
+<li>Multiple calls can queue data sequentially in the buffer.</li>
+<li>Transmission starts in the next IRQ 0 tick if the line is idle.</li>
+<li>The driver adds no footer; the receiver sees exactly the sent bytes.</li>
+<li>Example: Load DS:SI with your message, CX with length, call INT
+63h; the driver handles sending.</li>
+</ul></li>
+</ul>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div id="postamble" class="status">
+<p class="date">Created: 2025-08-04 ma 02:12</p>
+<p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
+</div>
+</body>
+</html>
index 5ba91f0..cb82b14 100755 (executable)
@@ -26,6 +26,7 @@ Out of [[https://en.wikipedia.org/wiki/Parallel_port][25 physical wires in LPT p
   reference for electrical signals to ensure consistency in\r
   communication.\r
 \r
+[[file:diagram.png]]\r
 \r
 By utilizing only three wires and software controlled bit-banging\r
 algorithm, custom, comparatively simple, cheap and long cable can be\r
@@ -33,6 +34,7 @@ built to connect 2 computers in a DIY network setup.
 \r
 * LPT Communication Driver\r
 \r
+** Overview\r
 \r
 The LPT Communication Driver is a Terminate and Stay Resident (TSR)\r
 driver designed to facilitate communication between computers using\r
@@ -52,40 +54,195 @@ any. Applications can also send outgoing messages into 5000 byte
 driver outbox memory buffer. Thereafter driver will transmit those\r
 messages in background mode over the wire.\r
 \r
+The driver is half-duplex: it prioritizes receiving over sending and\r
+does not transmit while receiving.\r
+\r
+During active transmission/reception, the driver can consume 100% CPU\r
+due to busy-wait loops in the IRQ handler, potentially causing random\r
+temporary hiccups for application running in the\r
+foreground. Unsuitable for real-time systems.\r
+\r
 Download:\r
 - Source code: [[file:lptdrv.asm][lptdrv.asm]]\r
 - Compiled binary: [[file:lptdrv.com][lptdrv.com]]\r
 \r
+** Data transmission implementation details\r
+\r
+When there is incoming data transmission, the TSR driver detects it\r
+during its periodic execution in the IRQ 0 (timer) handler, which runs\r
+approximately every 55ms.\r
+\r
+Driver checks for possible transmission comparatively rarely (every 55\r
+ms) and for this reason it is important to have quite long\r
+transmission start indicator/header before actual data is sent. This\r
+allows long enough time for recipient computer communication driver to\r
+detect that transmission line is active and start listening to it now\r
+in exclusive busy-wait loop. So, the TSR driver steals 100% of the CPU\r
+for the duration of transmission.\r
+\r
+The start of transmission is detected by reading the port (37Ah)\r
+value, and checking if bit 3 of the raw input is high. It then enters\r
+a "skip header" loop. Once bit 1 goes low, bit reception begins. The\r
+end is detected via a timeout: during bit reception, a counter\r
+increments on each poll. If there is no change in the port value for\r
+30 consecutive polls (indicating no new bit transition), it assumes\r
+the transmission is complete, appends a 2-byte length to the receive\r
+buffer, and exits the routine.\r
+\r
+So, both receive and send routines execute within the IRQ 0 handler\r
+using busy-wait polling loops (for receive) or timed output loops (for\r
+send). These can hold the CPU for the full duration of a transmission,\r
+as the handler does not yield until complete.\r
+\r
+It bit-bangs data by treating the LPT control port (37Ah) as both\r
+output and input, using bit 3 (pin 17, data line) for the serial data\r
+bit and bit 1 (pin 14, sync line) for a clock that alternates (low on\r
+even bit indices, high on odd) to signal transitions.\r
+\r
+For sending: the port is first set to 0xFF (all bits high) as a\r
+header, held for ~110ms (2 timer ticks).\r
+\r
+For receiving: after start detection, it polls the port for value\r
+changes (transitions). On each change, it reads the port again (to\r
+ensure that synchronization bit did not arrive ahead data bit over the\r
+separate physical wire), extracts bit 3 as the data bit, shifts it\r
+into a byte accumulator, and resets the timeout counter. Once a full\r
+byte is accumulated, it stores it in the receive buffer. No ACK or\r
+error checking.\r
+\r
+Driver can receive multiple transmissions into its 5000-byte receive\r
+buffer before the client program reads it out. Each incoming\r
+transmission appends its data bytes followed by a 2-byte length word\r
+directly to the end of the buffer (updating dbufsiz to the new total\r
+used). As long as the cumulative size doesn't exceed 5000 bytes,\r
+multiple can queue up. The buffer acts as a FIFO for concatenated\r
+packets; overflows are not handled (it would corrupt without\r
+checks). The client retrieves the entire buffer contents at once when\r
+polling.\r
+\r
+When the client program reads received data from the TSR (via INT 63h\r
+AH=2), the driver copies the full buffer contents (up to dbufsiz\r
+bytes) to the client's specified ES:DI pointer, returns the byte count\r
+in AX, and immediately resets dbufsiz to 0, clearing the buffer. This\r
+ensures the data is not served again on subsequent reads, as the\r
+buffer is emptied after each retrieval. If no data is available, AX=0\r
+is returned.\r
 \r
 ** Driver API\r
-*** Deactivate the driver\r
 \r
-*Parameters*:\r
-: AH = 0\r
+The driver uses INT 63h for its API, with functions selected via the\r
+AH register. It maintains two internal buffers:\r
 \r
-*Returns*: None\r
+- Download Buffer: 5000 bytes for incoming (received) data. Multiple\r
+  transmissions can be queued here, each appended with a 2-byte length\r
+  footer.\r
+- Upload Buffer: 5000 bytes for outgoing (to-be-sent) data. Data is\r
+  copied here and transmitted when the line is free.\r
 \r
-*** Activates the driver\r
 \r
-*Parameters*:\r
-: AH = 1\r
+Communication is polling-based for applications; the driver handles\r
+transmission/reception in the background.\r
 \r
-*Returns*: None\r
-\r
-*** Retrieve downloaded data from the driver's input buffer\r
+No error checking, acknowledgments, or flow control; it's a simple,\r
+unidirectional-per-turn protocol.\r
 \r
-*Parameters*:\r
-: AH = 2\r
-: ES:DI = Pointer to the location where downloaded data should be placed\r
+To use the driver:\r
+- Load the TSR (e.g., run lptdrv.com).\r
+- Activate it via the API.\r
+- Poll for received data or queue sends as needed.\r
+- Deactivate when done.\r
 \r
-*Returns*:\r
-: AX : Number of bytes downloaded\r
+API overview:\r
+| AH register | API function                                            |\r
+|-------------+---------------------------------------------------------|\r
+|           0 | [[id:a7aaa0e6-92de-467c-bcd4-b3d3216b15d4][Deactivate the driver]]                                   |\r
+|           1 | [[id:944be7b6-d3ba-486a-98bd-1a66cfffe6e5][Activate the driver]]                                     |\r
+|           2 | [[id:49350196-55b9-4d50-b672-b3c6d6d55e53][Retrieve downloaded data from the driver's input buffer]] |\r
+|           3 | [[id:53fd0c68-4057-4e9e-b908-87fab6eab5c8][Upload data to the driver's output buffer for transmission]]                                                        |\r
 \r
-*** Upload data to the driver's output buffer for transmission\r
+*** Deactivate the driver\r
+:PROPERTIES:\r
+:ID:       a7aaa0e6-92de-467c-bcd4-b3d3216b15d4\r
+:END:\r
+\r
+Disables the driver, stopping background monitoring and\r
+transmission. The LPT port is reset (set to 0).\r
+\r
+- AH: 0\r
+- Parameters: None\r
+- Returns: None\r
+- Side Effects: Clears the enabled flag.\r
+- Usage Notes: Call this before unloading the TSR or when\r
+  communication is no longer needed to free system resources.\r
+\r
+*** Activate the driver\r
+:PROPERTIES:\r
+:ID:       944be7b6-d3ba-486a-98bd-1a66cfffe6e5\r
+:END:\r
+\r
+Enables the driver, starting background LPT port monitoring for\r
+incoming data. The LPT port is reset (set to 0) upon activation.\r
+\r
+- AH: 1\r
+- Parameters: None\r
+- Returns: None\r
+- Side Effects: Sets the enabled flag. Existing buffer contents are\r
+  preserved.\r
+- Usage Notes: Must be called after loading the TSR and before any\r
+  send/receive operations. Can be called multiple times; redundant\r
+  activations are harmless.\r
 \r
-*Parameters*:\r
-: AH = 3\r
-: DS:SI: Pointer to the data to be uploaded\r
-: CX: Number of bytes to upload\r
+*** Retrieve downloaded data from the driver's input buffer\r
+:PROPERTIES:\r
+:ID:       49350196-55b9-4d50-b672-b3c6d6d55e53\r
+:END:\r
+\r
+Copies all accumulated received data from the driver's download buffer\r
+to the caller's memory location and clears the buffer.\r
+\r
+- *AH* : 2\r
+- *Parameters* :\r
+  - *ES:DI* : Pointer to the buffer where received data should be\r
+    copied (must be large enough to hold up to 5000 bytes).\r
+- *Returns* :\r
+  - *AX* : Number of bytes copied (0 if no data available).\r
+- *Side Effects* : Resets the download buffer size to 0, preventing\r
+  re-retrieval of the same data.\r
+- *Usage Notes* :\r
+  - Data is retrieved as a concatenated stream of all queued\r
+    transmissions.\r
+  - Each transmission in the buffer ends with a 2-byte length word\r
+    (little-endian) indicating its payload size (excluding the length\r
+    itself).\r
+  - Poll this function periodically in a loop to check for new data.\r
+  - If AX=0, no copy occurs.\r
+  - Example: In assembly, set ES:DI to your receive buffer and call\r
+    INT 63h; then process AX bytes if >0.\r
 \r
-*Returns*: None\r
+*** Upload data to the driver's output buffer for transmission\r
+:PROPERTIES:\r
+:ID:       53fd0c68-4057-4e9e-b908-87fab6eab5c8\r
+:END:\r
+\r
+Copies the specified data to the driver's upload buffer for background\r
+transmission. Transmission occurs when the line is free (no incoming\r
+data).\r
+\r
+- *AH* : 3\r
+- *Parameters* :\r
+  - *DS:SI* : Pointer to the data to upload.\r
+  - *CX* : Number of bytes to upload (must not exceed remaining upload\r
+    buffer space; no checks performed).\r
+- *Returns* : None\r
+- *Side Effects* : Appends data to the upload buffer and updates its\r
+  size. Transmission is asynchronous.\r
+- *Usage Notes* :\r
+  - Data is sent as a single transmission (no automatic framing;\r
+    caller can add headers if needed).\r
+  - If the buffer is full (total >5000 bytes), behavior is undefined\r
+    (overflow).\r
+  - Multiple calls can queue data sequentially in the buffer.\r
+  - Transmission starts in the next IRQ 0 tick if the line is idle.\r
+  - The driver adds no footer; the receiver sees exactly the sent bytes.\r
+  - Example: Load DS:SI with your message, CX with length, call INT\r
+    63h; the driver handles sending.\r
index acd87ee..f162ed4 100755 (executable)
@@ -43,6 +43,7 @@ export_org_to_html "Math/Truth table"
 export_org_to_html "Miscellaneous/Mouse driver"
 
 export_org_to_html "Networking/Digital data over analog audio"
+export_org_to_html "Networking/LPT communication driver"
 
 # Upload project homepage to the server.
 rsync -avz --delete  -e 'ssh -p 10006' ./ \
index 59e8acf..ae99e95 100644 (file)
--- a/index.org
+++ b/index.org
@@ -888,6 +888,12 @@ How It Works:
 
 Download source code: [[file:Networking/LPT%20to%20COM%20port%20data%20transfer.bas][LPT to COM port data transfer.bas]]
 
+** LPT communication driver
+
+TSR driver that allows 2 computers to communicate over parallel port serially.
+
+[[file:Networking/LPT%20communication%20driver/index.html][Read more]]
+
 ** Data over analog audio CODEC
 
 Utilities to encode digital data to sound file and back.