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