diff --git a/07_dos/README.md b/07_dos/README.md
index 4c99cfa..f7db5d9 100644
--- a/07_dos/README.md
+++ b/07_dos/README.md
@@ -6,13 +6,13 @@
| File | Description |
|---|---|
-| [dos_base.md](dos_base.md) | DosLibrary structure and global state |
-| [file_io.md](file_io.md) | Open, Close, Read, Write, Seek |
-| [locks_examine.md](locks_examine.md) | Lock, UnLock, Examine, ExNext, ExAll |
-| [pattern_matching.md](pattern_matching.md) | ParsePattern, MatchPattern, wildcards |
+| [dos_base.md](dos_base.md) | DosLibrary structure, RootNode, BCPL heritage |
+| [file_io.md](file_io.md) | Open/Close/Read/Write/Seek, buffered I/O (FRead/SetVBuf), FileInfoBlock, practical patterns |
+| [locks_examine.md](locks_examine.md) | Lock semantics (shared/exclusive), handler discovery, Examine/ExNext/ExAll, antipatterns |
+| [pattern_matching.md](pattern_matching.md) | ParsePattern, MatchPattern, AmigaDOS wildcard syntax |
| [process_management.md](process_management.md) | CreateNewProc, SystemTagList, Execute |
-| [packet_system.md](packet_system.md) | DosPacket, ACTION_* codes, handler protocol |
-| [filesystem.md](filesystem.md) | FFS/OFS layout, block structure |
-| [environment.md](environment.md) | GetVar/SetVar, local/global env variables |
-| [error_handling.md](error_handling.md) | IoErr, Fault, PrintFault, error codes |
-| [cli_shell.md](cli_shell.md) | CLI/Shell: pipes, redirection, scripts, ReadArgs |
+| [packet_system.md](packet_system.md) | DosPacket wire format, ACTION_* codes, handler protocol, BSTR encoding |
+| [filesystem.md](filesystem.md) | FFS/OFS block layout, root/file/data blocks, bitmap, hash function, ADF reader |
+| [environment.md](environment.md) | GetVar/SetVar, local/global/persistent env variables |
+| [error_handling.md](error_handling.md) | IoErr, Fault, PrintFault, complete error code table |
+| [cli_shell.md](cli_shell.md) | CLI/Shell: pipes, redirection, scripts, ReadArgs template parsing |
diff --git a/10_devices/README.md b/10_devices/README.md
index f3de935..5752f76 100644
--- a/10_devices/README.md
+++ b/10_devices/README.md
@@ -2,17 +2,19 @@
# Devices — Overview
+Amiga devices are shared libraries with an exec I/O request interface. They provide standardised access to hardware peripherals via `OpenDevice`/`CloseDevice` and `DoIO`/`SendIO` patterns.
+
## Section Index
| File | Description |
|---|---|
-| [trackdisk.md](trackdisk.md) | trackdisk.device — floppy I/O |
+| [trackdisk.md](trackdisk.md) | Floppy disk DMA: MFM encoding, track format, disk geometry, track caching, direct HW access |
| [scsi.md](scsi.md) | scsi.device / 2nd.scsi.device — hard disk I/O |
-| [serial.md](serial.md) | serial.device — RS-232 |
-| [parallel.md](parallel.md) | parallel.device — Centronics |
-| [timer.md](timer.md) | timer.device — timing and delays |
-| [audio.md](audio.md) | audio.device — DMA audio channels |
-| [keyboard.md](keyboard.md) | keyboard.device — keyboard events |
-| [gameport.md](gameport.md) | gameport.device — joystick/mouse |
-| [input.md](input.md) | input.device — event merging |
+| [serial.md](serial.md) | UART/RS-232: CIA registers, baud rate calculation, serial debugging (KPrintF) |
+| [parallel.md](parallel.md) | parallel.device — Centronics parallel port |
+| [timer.md](timer.md) | CIA timers, E-clock, VBlank: delays, ReadEClock, periodic patterns, signal-based waiting |
+| [audio.md](audio.md) | 4-channel DMA audio: hardware registers, priority allocation, double-buffering, modulation |
+| [keyboard.md](keyboard.md) | CIA-A serial handshake, raw key codes, key matrix, reset sequence, FPGA protocol notes |
+| [gameport.md](gameport.md) | gameport.device — joystick/mouse port reading |
+| [input.md](input.md) | Input handler chain: priority dispatch, event classes, key remapping, Commodities Exchange |
| [console.md](console.md) | console.device — text terminal I/O |
diff --git a/10_devices/input.md b/10_devices/input.md
index ee0044b..e28a825 100644
--- a/10_devices/input.md
+++ b/10_devices/input.md
@@ -1,41 +1,216 @@
[← Home](../README.md) · [Devices](README.md)
-# input.device — Event Stream Merging
+# input.device — Input Event Handler Chain
## Overview
-`input.device` merges events from keyboard, mouse, gameport, and timer into a single input stream that feeds Intuition. Input handlers can be installed at various priorities to filter, modify, or consume events.
+`input.device` is the central dispatcher for all input events on AmigaOS. It collects raw events from keyboard, mouse, joystick, and other sources, then distributes them through a **priority-ordered handler chain**. Intuition sits at priority 50 in this chain — custom input handlers can intercept events before or after Intuition.
---
-## Handler Priority Levels
+## Handler Chain Architecture
-| Priority | Consumer |
-|---|---|
-| 100 | System reserved |
-| 51+ | Custom high-priority handlers |
-| 50 | Intuition |
-| 20 | Console.device |
-| 0 | Default |
+```mermaid
+flowchart TD
+ KB["keyboard.device"] --> ID["input.device"]
+ GP["gameport.device"] --> ID
+ OTHER["Other sources"] --> ID
+
+ ID --> H1["Handler Pri 100
Commodities Exchange"]
+ H1 --> H2["Handler Pri 51
Custom 'hot key' handler"]
+ H2 --> H3["Handler Pri 50
Intuition"]
+ H3 --> H4["Handler Pri 0
Console.device"]
+ H4 --> H5["Handler Pri -50
Application handler"]
+
+ style H3 fill:#e8f4fd,stroke:#2196f3,color:#333
+```
+
+Events flow from highest to lowest priority. Each handler can:
+- **Pass** the event through (return the event list unchanged)
+- **Modify** the event (e.g., remap keys)
+- **Consume** the event (remove from chain — lower handlers never see it)
+- **Insert** new synthetic events
---
-## Commands
+## InputEvent Structure
-| Code | Constant | Description |
+```c
+/* devices/inputevent.h — NDK39 */
+struct InputEvent {
+ struct InputEvent *ie_NextEvent; /* linked list */
+ UBYTE ie_Class; /* event type (IECLASS_*) */
+ UBYTE ie_SubClass; /* subclass */
+ UWORD ie_Code; /* key code, button, qualifier */
+ UWORD ie_Qualifier; /* modifier keys state */
+ union {
+ struct {
+ WORD ie_x; /* mouse X delta or absolute */
+ WORD ie_y; /* mouse Y delta or absolute */
+ } ie_xy;
+ APTR ie_addr; /* pointer data */
+ } ie_position;
+ struct timeval ie_TimeStamp; /* when event occurred */
+};
+```
+
+### Event Classes
+
+| Class | Constant | Source |
|---|---|---|
-| 9 | `IND_ADDHANDLER` | Add input handler |
-| 10 | `IND_REMHANDLER` | Remove input handler |
-| 11 | `IND_WRITEEVENT` | Inject an InputEvent into the stream |
-| 12 | `IND_SETTHRESH` | Set double-click threshold |
-| 13 | `IND_SETPERIOD` | Set key repeat period |
-| 14 | `IND_SETMPORT` | Set mouse port type |
-| 15 | `IND_SETMTRIG` | Set mouse trigger |
-| 16 | `IND_SETMTYPE` | Set mouse type |
+| $01 | `IECLASS_RAWKEY` | Raw key press/release |
+| $02 | `IECLASS_RAWMOUSE` | Mouse movement + buttons |
+| $04 | `IECLASS_TIMER` | Timer tick event |
+| $07 | `IECLASS_NEWPOINTERPOS` | Absolute pointer position |
+| $09 | `IECLASS_DISKINSERTED` | Disk inserted |
+| $0A | `IECLASS_DISKREMOVED` | Disk removed |
+| $0D | `IECLASS_NEWPREFS` | Preferences changed |
+
+### Qualifier Bits
+
+| Bit | Constant | Key |
+|---|---|---|
+| 0 | `IEQUALIFIER_LSHIFT` | Left Shift |
+| 1 | `IEQUALIFIER_RSHIFT` | Right Shift |
+| 2 | `IEQUALIFIER_CAPSLOCK` | Caps Lock |
+| 3 | `IEQUALIFIER_CONTROL` | Ctrl |
+| 4 | `IEQUALIFIER_LALT` | Left Alt |
+| 5 | `IEQUALIFIER_RALT` | Right Alt |
+| 6 | `IEQUALIFIER_LCOMMAND` | Left Amiga |
+| 7 | `IEQUALIFIER_RCOMMAND` | Right Amiga |
+| 8 | `IEQUALIFIER_NUMERICPAD` | Key is on numeric pad |
+| 9 | `IEQUALIFIER_REPEAT` | Key repeat (auto-repeat) |
+| 10 | `IEQUALIFIER_INTERRUPT` | Event from interrupt |
+| 11 | `IEQUALIFIER_MULTIBROADCAST` | Broadcast to all handlers |
+| 12 | `IEQUALIFIER_MIDBUTTON` | Middle mouse button |
+| 13 | `IEQUALIFIER_RBUTTON` | Right mouse button |
+| 14 | `IEQUALIFIER_LEFTBUTTON` | Left mouse button |
+| 15 | `IEQUALIFIER_RELATIVEMOUSE` | Mouse values are deltas |
+
+---
+
+## Installing a Custom Input Handler
+
+```c
+#include
+#include
+
+/* Handler function — called from input.device context: */
+struct InputEvent * __saveds __asm MyHandler(
+ register __a0 struct InputEvent *events,
+ register __a1 APTR handlerData)
+{
+ struct InputEvent *ev;
+ for (ev = events; ev; ev = ev->ie_NextEvent)
+ {
+ if (ev->ie_Class == IECLASS_RAWKEY)
+ {
+ UWORD key = ev->ie_Code & 0x7F;
+ BOOL up = ev->ie_Code & 0x80;
+
+ /* Example: remap F10 ($59) to Escape ($45): */
+ if (key == 0x59)
+ ev->ie_Code = (up ? 0x80 : 0x00) | 0x45;
+ }
+ }
+ return events; /* pass all events to next handler */
+}
+
+/* Install the handler: */
+struct Interrupt handlerInt;
+handlerInt.is_Node.ln_Type = NT_INTERRUPT;
+handlerInt.is_Node.ln_Pri = 51; /* just above Intuition (50) */
+handlerInt.is_Node.ln_Name = "MyKeyMapper";
+handlerInt.is_Data = myData;
+handlerInt.is_Code = (APTR)MyHandler;
+
+struct MsgPort *inputPort = CreateMsgPort();
+struct IOStdReq *inputReq = (struct IOStdReq *)
+ CreateIORequest(inputPort, sizeof(struct IOStdReq));
+OpenDevice("input.device", 0, (struct IORequest *)inputReq, 0);
+
+inputReq->io_Command = IND_ADDHANDLER;
+inputReq->io_Data = (APTR)&handlerInt;
+DoIO((struct IORequest *)inputReq);
+
+/* ... handler is now active ... */
+
+/* Remove handler before exit: */
+inputReq->io_Command = IND_REMHANDLER;
+inputReq->io_Data = (APTR)&handlerInt;
+DoIO((struct IORequest *)inputReq);
+```
+
+### Consuming Events (Blocking)
+
+```c
+/* To consume an event (prevent it from reaching Intuition): */
+struct InputEvent * __saveds __asm BlockEscape(
+ register __a0 struct InputEvent *events,
+ register __a1 APTR data)
+{
+ struct InputEvent *ev, *prev = NULL;
+ for (ev = events; ev; )
+ {
+ if (ev->ie_Class == IECLASS_RAWKEY &&
+ (ev->ie_Code & 0x7F) == 0x45) /* Escape */
+ {
+ /* Remove from chain: */
+ if (prev)
+ prev->ie_NextEvent = ev->ie_NextEvent;
+ else
+ events = ev->ie_NextEvent;
+ ev = ev->ie_NextEvent;
+ continue;
+ }
+ prev = ev;
+ ev = ev->ie_NextEvent;
+ }
+ return events;
+}
+```
+
+---
+
+## Injecting Synthetic Events
+
+```c
+/* Send a fake key press: */
+struct InputEvent fake;
+fake.ie_Class = IECLASS_RAWKEY;
+fake.ie_Code = 0x45; /* Escape, key down */
+fake.ie_Qualifier = 0;
+
+inputReq->io_Command = IND_WRITEEVENT;
+inputReq->io_Data = (APTR)&fake;
+inputReq->io_Length = sizeof(struct InputEvent);
+DoIO((struct IORequest *)inputReq);
+```
+
+---
+
+## Commodities Exchange
+
+The Commodities Exchange (`commodities.library`) provides a high-level framework for input handlers:
+
+```c
+/* Define a hot key: */
+CxObj *broker = CxBroker(&newBroker, NULL);
+CxObj *filter = CxFilter("rawkey control esc"); /* Ctrl+Esc */
+CxObj *sender = CxSender(brokerPort, EVT_HOTKEY);
+AttachCxObj(filter, sender);
+AttachCxObj(broker, filter);
+ActivateCxObj(broker, TRUE);
+```
+
+This is the preferred method for applications — avoids writing raw input handlers.
---
## References
-- NDK39: `devices/input.h`
-- [input_events.md](../09_intuition/input_events.md) — handler installation example
+- NDK39: `devices/input.h`, `devices/inputevent.h`
+- ADCD 2.1: input.device autodocs
+- See also: [keyboard.md](keyboard.md) — raw key code map
+- See also: [idcmp.md](../09_intuition/idcmp.md) — Intuition input handling
+- See also: [input_events.md](../09_intuition/input_events.md) — event flow through Intuition
diff --git a/10_devices/serial.md b/10_devices/serial.md
index 00b5430..c81e1db 100644
--- a/10_devices/serial.md
+++ b/10_devices/serial.md
@@ -1,54 +1,130 @@
[← Home](../README.md) · [Devices](README.md)
-# serial.device — RS-232 Communication
+# serial.device — UART and RS-232 Communication
## Overview
-`serial.device` provides buffered RS-232 serial I/O through the Amiga's built-in 8520 CIA UART (active-high active sense).
+`serial.device` provides access to the Amiga's built-in UART (Universal Asynchronous Receiver/Transmitter) for RS-232 serial communication. The hardware UART is implemented using **CIA-B** shift registers and custom chip DMA. Serial I/O is fundamental for debugging (serial console), modem communication, MIDI, and inter-machine networking.
---
-## Opening
+## Hardware
-```c
-struct IOExtSer *ser = (struct IOExtSer *)
- CreateIORequest(port, sizeof(struct IOExtSer));
-OpenDevice("serial.device", 0, (struct IORequest *)ser, 0);
-```
+### Serial Port Pins (DB-25)
----
+| Pin | Signal | Direction | Description |
+|---|---|---|---|
+| 2 | TxD | Output | Transmit data |
+| 3 | RxD | Input | Receive data |
+| 4 | RTS | Output | Request to send (active low) |
+| 5 | CTS | Input | Clear to send (active low) |
+| 6 | DSR | Input | Data set ready |
+| 7 | GND | — | Signal ground |
+| 8 | CD | Input | Carrier detect |
+| 20 | DTR | Output | Data terminal ready |
-## Setting Parameters
+### Custom Chip Registers
-```c
-ser->io_CtlChar = SER_DEFAULT_CTLCHAR;
-ser->io_RBufLen = 4096; /* read buffer size */
-ser->io_Baud = 9600;
-ser->io_ReadLen = 8; /* data bits */
-ser->io_WriteLen = 8;
-ser->io_StopBits = 1;
-ser->io_SerFlags = SERF_XDISABLED; /* no XON/XOFF */
-ser->IOSer.io_Command = SDCMD_SETPARAMS;
-DoIO((struct IORequest *)ser);
-```
-
----
-
-## Commands
-
-| Code | Constant | Description |
+| Register | Address | Description |
|---|---|---|
-| 2 | `CMD_READ` | Read bytes |
-| 3 | `CMD_WRITE` | Write bytes |
-| 5 | `CMD_CLEAR` | Clear buffers |
-| 8 | `CMD_FLUSH` | Abort pending requests |
-| 9 | `SDCMD_QUERY` | Get status (bytes in buffer, errors) |
-| 10 | `SDCMD_BREAK` | Send break signal |
-| 11 | `SDCMD_SETPARAMS` | Set baud/bits/parity/stop |
+| `SERDAT` | `$DFF030` | Serial data write (transmit) — 9 bits: stop bit + 8 data |
+| `SERDATR` | `$DFF018` | Serial data read (receive) — status + received byte |
+| `SERPER` | `$DFF032` | Serial period (baud rate divider) |
+| `ADKCON` | `$DFF09E` | UARTBRK bit for break signal |
+
+### Baud Rate Calculation
+
+```c
+/* SERPER value = (system_clock / baud_rate) - 1 */
+/* PAL system clock for serial = 3,546,895 Hz */
+/* NTSC = 3,579,545 Hz */
+
+#define SERIAL_CLOCK_PAL 3546895
+#define SERIAL_CLOCK_NTSC 3579545
+
+UWORD serper_from_baud(ULONG baud, BOOL isPAL) {
+ return (isPAL ? SERIAL_CLOCK_PAL : SERIAL_CLOCK_NTSC) / baud - 1;
+}
+```
+
+| Baud Rate | SERPER (PAL) | SERPER (NTSC) |
+|---|---|---|
+| 1200 | 2955 | 2982 |
+| 2400 | 1477 | 1491 |
+| 9600 | 368 | 372 |
+| 19200 | 184 | 185 |
+| 31250 (MIDI) | 112 | 113 |
+| 57600 | 60 | 61 |
+| 115200 | 29 | 30 |
+
+---
+
+## Using serial.device
+
+### Open and Configure
+
+```c
+struct MsgPort *serialPort = CreateMsgPort();
+struct IOExtSer *serialReq = (struct IOExtSer *)
+ CreateIORequest(serialPort, sizeof(struct IOExtSer));
+
+/* Open serial.device unit 0: */
+BYTE err = OpenDevice("serial.device", 0,
+ (struct IORequest *)serialReq, 0);
+
+/* Configure: */
+serialReq->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
+serialReq->io_Baud = 9600;
+serialReq->io_ReadLen = 8; /* 8 data bits */
+serialReq->io_WriteLen = 8;
+serialReq->io_StopBits = 1;
+serialReq->io_RBufLen = 4096; /* receive buffer size */
+serialReq->IOSer.io_Command = SDCMD_SETPARAMS;
+DoIO((struct IORequest *)serialReq);
+```
+
+### Write Data
+
+```c
+serialReq->IOSer.io_Command = CMD_WRITE;
+serialReq->IOSer.io_Data = "Hello Serial\r\n";
+serialReq->IOSer.io_Length = 14;
+DoIO((struct IORequest *)serialReq);
+```
+
+### Read Data
+
+```c
+/* Check how many bytes are waiting: */
+serialReq->IOSer.io_Command = SDCMD_QUERY;
+DoIO((struct IORequest *)serialReq);
+ULONG waiting = serialReq->IOSer.io_Actual;
+
+if (waiting > 0) {
+ serialReq->IOSer.io_Command = CMD_READ;
+ serialReq->IOSer.io_Data = buffer;
+ serialReq->IOSer.io_Length = waiting;
+ DoIO((struct IORequest *)serialReq);
+ /* buffer now contains received data */
+}
+```
+
+### Serial Debugging
+
+Many developers use a null-modem cable to a terminal for kernel debugging:
+
+```c
+/* Kprintf — ROM debug output via serial (ROMTags/exec): */
+/* This bypasses serial.device entirely — writes directly to SERDAT */
+void KPrintF(const char *fmt, ...); /* available in debug ROMs */
+
+/* Sushi/Sashimi — capture serial debug output on another Amiga */
+```
---
## References
- NDK39: `devices/serial.h`
+- HRM: *Amiga Hardware Reference Manual* — Serial Port chapter
- ADCD 2.1: serial.device autodocs