From ec0ec8f0745540b5736b56a853ffe831dbc4e80d Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 1 Mar 2026 19:01:40 -0600 Subject: [PATCH] Replace event-driven WM_COMMNOTIFY architecture with polling main loop The ISR still fills the ring buffer (mandatory for 115200 baud), but the app now polls ReadComm directly via a PeekMessage loop instead of waiting for WM_COMMNOTIFY. Blink uses GetTickCount instead of WM_TIMER. This eliminates all Windows message overhead from the data path while keeping the message loop alive for keyboard, paint, and scrollbar. Removed from KPCOMM.PAS: NotifyWndProc, hidden notification window, RegisterClass/CreateWindow, EnableCommNotification, SetCommEventMask, DoCommEvent, Process*Notify methods, OnComm/CommEvent/RThreshold/ SThreshold properties, modem shadow state (CTS/DSR/CD). Removed from KPANSI.PAS: WM_TIMER handler, SetTimer/KillTimer, replaced with public TickBlink method using GetTickCount at 500ms intervals. Removed from drv/isr.c: checkNotify function and its call from isrDispatch. Removed from drv/commdrv.c: pfnPostMessage, all rxNotifySent/txNotifySent edge-trigger bookkeeping, gutted enableNotification to a no-op API-compat stub. Removed from drv/commdrv.h: rxNotifySent/txNotifySent fields (shifts struct layout), PostMessageProcT typedef, pfnPostMessage extern. Co-Authored-By: Claude Opus 4.6 --- delphi/KPANSI.PAS | 49 +++----- delphi/KPCOMM.PAS | 267 +------------------------------------------- delphi/KPTEST.DPR | 2 +- delphi/TESTMAIN.PAS | 85 ++++++++------ drv/commdrv.c | 66 ++--------- drv/commdrv.h | 20 +--- drv/isr.c | 59 ---------- 7 files changed, 86 insertions(+), 462 deletions(-) diff --git a/delphi/KPANSI.PAS b/delphi/KPANSI.PAS index 9400611..c3b4a02 100644 --- a/delphi/KPANSI.PAS +++ b/delphi/KPANSI.PAS @@ -71,9 +71,9 @@ type FCellWidth: Integer; { Character cell width in pixels (typically 8) } FCellHeight: Integer; { Character cell height in pixels (typ 12-16) } - { Blink/timer state } + { Blink state } FBlinkOn: Boolean; { Cursor blink phase: True=visible } - FTimerActive: Boolean; { True if WM_TIMER is running (SetTimer) } + FLastBlinkTick: Longint; { GetTickCount value at last blink toggle } { Scrollback view } FScrollPos: Integer; { Lines scrolled back (0=live, >0=viewing history) } @@ -97,9 +97,6 @@ type FPaintFont: HFont; { GDI font handle for OEM_CHARSET rendering } FStockFont: Boolean; { True if FPaintFont is a stock object (no delete) } - { Rendering control } - FBlinkCount: Integer; { Timer ticks since last blink toggle } - { Dirty tracking: per-row flags for incremental rendering } FDirtyRow: array[0..255] of Boolean; { True = row needs re-render } FAllDirty: Boolean; { True = all rows need re-render } @@ -158,7 +155,6 @@ type procedure UpdateScrollbar; procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message wm_EraseBkgnd; procedure WMGetDlgCode(var Msg: TMessage); message wm_GetDlgCode; - procedure WMTimer(var Msg: TWMTimer); message wm_Timer; procedure WMVScroll(var Msg: TWMScroll); message wm_VScroll; protected procedure CreateParams(var Params: TCreateParams); override; @@ -170,6 +166,7 @@ type destructor Destroy; override; procedure Clear; procedure Reset; + procedure TickBlink; procedure Write(const S: string); property CursorCol: Integer read GetCursorCol; property CursorRow: Integer read GetCursorRow; @@ -210,10 +207,8 @@ const $00FFFFFF { 15 White (bright) } ); - { Blink timer interval. Win16 minimum is ~55 ms (18.2 Hz tick). } - BlinkTimerMs = 55; - { Cursor and text blink toggle every 9 timer ticks (~500 ms). } - BlinkInterval = 9; + { Blink toggle interval in milliseconds (cursor + text blink). } + BlinkMs = 500; { OUT_RASTER_PRECIS may not be defined in Delphi 1.0 WinTypes } OutRasterPrecis = 6; @@ -484,12 +479,11 @@ begin FCellWidth := 8; FCellHeight := 16; FBlinkOn := True; - FTimerActive := False; + FLastBlinkTick := GetTickCount; FScrollPos := 0; FWrapMode := True; FPaintFont := 0; FStockFont := False; - FBlinkCount := 0; FAllDirty := True; FScrollbarDirty := False; FTextBlinkOn := True; @@ -648,11 +642,6 @@ end; destructor TKPAnsi.Destroy; begin - if FTimerActive and HandleAllocated then - begin - KillTimer(Handle, 1); - FTimerActive := False; - end; DestroyRowBuffers; if (FPaintFont <> 0) and not FStockFont then begin @@ -1870,13 +1859,6 @@ begin CreateRowBuffers; FAllDirty := True; - { Start render/blink timer } - if not FTimerActive then - begin - SetTimer(Handle, 1, BlinkTimerMs, nil); - FTimerActive := True; - end; - Invalidate; end; @@ -2322,20 +2304,19 @@ begin end; -procedure TKPAnsi.WMTimer(var Msg: TWMTimer); +procedure TKPAnsi.TickBlink; +var + Now: Longint; begin - { Blink counter: toggle cursor and text blink every BlinkInterval ticks } - Inc(FBlinkCount); - if FBlinkCount >= BlinkInterval then + Now := GetTickCount; + if Now - FLastBlinkTick >= BlinkMs then begin - FBlinkCount := 0; - FBlinkOn := not FBlinkOn; - FTextBlinkOn := not FTextBlinkOn; + FLastBlinkTick := Now; + FBlinkOn := not FBlinkOn; + FTextBlinkOn := not FTextBlinkOn; DirtyBlinkRows; + FlipToScreen; end; - - { Render blink changes and any stale dirty rows } - FlipToScreen; end; diff --git a/delphi/KPCOMM.PAS b/delphi/KPCOMM.PAS index 224e71e..b76f178 100644 --- a/delphi/KPCOMM.PAS +++ b/delphi/KPCOMM.PAS @@ -5,40 +5,16 @@ unit KPComm; { TKPComm is a non-visual TComponent descendant providing RS-232 serial } { I/O via the Windows 3.1 comm API. Installs to the "KP" palette tab. } { } -{ Port lifecycle: OpenComm -> BuildCommDCB + SetCommState -> } -{ EnableCommNotification -> CloseComm. } +{ Port lifecycle: OpenComm -> BuildCommDCB + SetCommState -> CloseComm. } { } -{ WM_COMMNOTIFY messages are received through a hidden utility window } -{ with a registered class and dispatched to ProcessReceiveNotify, } -{ ProcessTransmitNotify, and ProcessEventNotify. } -{ } -{ Modem line status (CTS/DSR/CD) is tracked via shadow booleans toggled } -{ on transition events from GetCommEventMask, since the 16-bit comm API } -{ only provides transition events, not absolute line levels. } +{ Data is read by polling Input (ReadComm) from a PeekMessage main loop } +{ rather than through WM_COMMNOTIFY event dispatch. } interface uses SysUtils, Classes, WinTypes, WinProcs, Messages; -const - { Communication events } - comEvReceive = 1; - comEvSend = 2; - comEvCTS = 3; - comEvDSR = 4; - comEvCD = 5; - comEvRing = 6; - comEvEOF = 7; - - { Error events } - comEvtBreak = 1001; - comEvtFrame = 1004; - comEvtOverrun = 1006; - comEvtRxOver = 1008; - comEvtParity = 1009; - comEvtTxFull = 1010; - type THandshaking = (hsNone, hsXonXoff, hsRtsCts, hsBoth); TInputMode = (imText, imBinary); @@ -47,7 +23,6 @@ type private { Port state } FCommId: Integer; { Comm port handle from OpenComm (-1 = closed) } - FHWndNotify: HWnd; { Hidden window receiving WM_COMMNOTIFY } { Configuration (published properties) } FCommPort: Integer; { Port number (1-based: 1=COM1, 2=COM2, ...) } @@ -55,8 +30,6 @@ type FPortOpen: Boolean; { True while port is open and operational } FInBufferSize: Integer; { Receive ring buffer size in bytes } FOutBufferSize: Integer; { Transmit ring buffer size in bytes } - FRThreshold: Integer; { RX byte count triggering CN_RECEIVE (0=disabled) } - FSThreshold: Integer; { TX free space triggering CN_TRANSMIT (0=disabled) } FHandshaking: THandshaking; { Flow control mode (none/XonXoff/RtsCts/both) } FInputLen: Integer; { Max bytes per Input read (0=up to 255) } FInputMode: TInputMode; { Text or binary read mode } @@ -72,26 +45,14 @@ type { Runtime state } FBreakState: Boolean; { True while break signal is being sent } - FCommEvent: Integer; { Last event code passed to OnComm (comEv* const) } - { Modem line shadow state (toggled on transition events from driver) } - FCTSState: Boolean; { CTS line level (toggled on ev_CTS) } - FDSRState: Boolean; { DSR line level (toggled on ev_DSR) } - FCDState: Boolean; { DCD/RLSD line level (toggled on ev_RLSD) } - - { Event handler } - FOnComm: TNotifyEvent; { Fired on comm events; check CommEvent for code } procedure ApplyHandshaking; procedure ApplyOptions; procedure ClosePort; - procedure DoCommEvent(EventCode: Integer); function GetInBufferCount: Integer; function GetInput: string; function GetOutBufferCount: Integer; procedure OpenPort; - procedure ProcessEventNotify; - procedure ProcessReceiveNotify; - procedure ProcessTransmitNotify; procedure SetBreak(Value: Boolean); procedure SetCommPort(Value: Integer); procedure SetDTREnable(Value: Boolean); @@ -111,19 +72,13 @@ type property Output: string write SetOutput; property InBufferCount: Integer read GetInBufferCount; property OutBufferCount: Integer read GetOutBufferCount; - property CDHolding: Boolean read FCDState; - property CTSHolding: Boolean read FCTSState; - property DSRHolding: Boolean read FDSRState; property Break: Boolean read FBreakState write SetBreak; - property CommEvent: Integer read FCommEvent; published property CommPort: Integer read FCommPort write SetCommPort default 1; property Settings: string read FSettings write SetSettings; property PortOpen: Boolean read FPortOpen write SetPortOpen default False; property InBufferSize: Integer read FInBufferSize write SetInBufferSize default 4096; property OutBufferSize: Integer read FOutBufferSize write SetOutBufferSize default 4096; - property RThreshold: Integer read FRThreshold write FRThreshold default 0; - property SThreshold: Integer read FSThreshold write FSThreshold default 0; property Handshaking: THandshaking read FHandshaking write SetHandshaking default hsNone; property InputLen: Integer read FInputLen write FInputLen default 0; property InputMode: TInputMode read FInputMode write FInputMode default imText; @@ -132,7 +87,6 @@ type property NullDiscard: Boolean read FNullDiscard write SetNullDiscard default False; property EOFEnable: Boolean read FEOFEnable write FEOFEnable default False; property ParityReplace: string read FParityReplace write SetParityReplace; - property OnComm: TNotifyEvent read FOnComm write FOnComm; end; procedure Register; @@ -140,11 +94,6 @@ procedure Register; implementation const - { WM_COMMNOTIFY notification codes } - CN_RECEIVE = $0001; - CN_TRANSMIT = $0002; - CN_EVENT = $0004; - { DCB Flags field bit masks. The Windows 3.1 DCB packs thirteen 1-bit } { fields into a single UINT (Word) at offset 12 of the structure. } { Delphi 1.0 maps this UINT to TDCB.Flags. } @@ -162,46 +111,6 @@ const dcbDtrflow = $0800; dcbRtsflow = $1000; - { Hidden notification window class name } - NotifyClassName = 'KPCommNotify'; - -var - NotifyClassRegistered: Boolean; { True after RegisterClass for notification window } - - -{ ----------------------------------------------------------------------- } -{ Hidden notification window procedure } -{ } -{ Receives WM_COMMNOTIFY from the comm driver. Retrieves the TKPComm } -{ instance pointer stored in the window's extra bytes (offset 0) and } -{ dispatches to the appropriate Process* method. } -{ ----------------------------------------------------------------------- } - -function NotifyWndProc(Wnd: HWnd; Msg: Word; - WParam: Word; LParam: Longint): Longint; export; -var - Comm: TKPComm; - NotifyCode: Word; -begin - if Msg = wm_CommNotify then - begin - Comm := TKPComm(GetWindowLong(Wnd, 0)); - if (Comm <> nil) and Comm.FPortOpen and (Comm.FCommId >= 0) then - begin - NotifyCode := Word(LParam); - if (NotifyCode and CN_RECEIVE) <> 0 then - Comm.ProcessReceiveNotify; - if (NotifyCode and CN_TRANSMIT) <> 0 then - Comm.ProcessTransmitNotify; - if (NotifyCode and CN_EVENT) <> 0 then - Comm.ProcessEventNotify; - end; - Result := 0; - end - else - Result := DefWindowProc(Wnd, Msg, WParam, LParam); -end; - { ----------------------------------------------------------------------- } { TKPComm } @@ -277,18 +186,11 @@ end; procedure TKPComm.ClosePort; -var - Msg: TMsg; begin - { Set FPortOpen first to prevent stale WM_COMMNOTIFY processing } FPortOpen := False; if FCommId >= 0 then begin - { Disable notifications BEFORE dropping modem lines so the ISR } - { stops posting WM_COMMNOTIFY while we tear down. } - EnableCommNotification(FCommId, 0, -1, -1); - if FBreakState then begin ClearCommBreak(FCommId); @@ -299,22 +201,6 @@ begin CloseComm(FCommId); FCommId := -1; end; - - if FHWndNotify <> 0 then - begin - { Flush any stale WM_COMMNOTIFY that the ISR posted before we } - { disabled notifications. Without this, DispatchMessage would } - { dereference the freed window structure and lock up. } - while PeekMessage(Msg, FHWndNotify, wm_CommNotify, - wm_CommNotify, pm_Remove) do - { discard }; - DestroyWindow(FHWndNotify); - FHWndNotify := 0; - end; - - FCTSState := False; - FDSRState := False; - FCDState := False; end; @@ -322,14 +208,11 @@ constructor TKPComm.Create(AOwner: TComponent); begin inherited Create(AOwner); FCommId := -1; - FHWndNotify := 0; FCommPort := 1; FSettings := '9600,N,8,1'; FPortOpen := False; FInBufferSize := 4096; FOutBufferSize := 4096; - FRThreshold := 0; - FSThreshold := 0; FHandshaking := hsNone; FInputLen := 0; FInputMode := imText; @@ -339,10 +222,6 @@ begin FEOFEnable := False; FParityReplace := '?'; FBreakState := False; - FCommEvent := 0; - FCTSState := False; - FDSRState := False; - FCDState := False; end; @@ -354,14 +233,6 @@ begin end; -procedure TKPComm.DoCommEvent(EventCode: Integer); -begin - FCommEvent := EventCode; - if Assigned(FOnComm) then - FOnComm(Self); -end; - - function TKPComm.GetInBufferCount: Integer; var Stat: TComStat; @@ -416,12 +287,9 @@ end; procedure TKPComm.OpenPort; var - WC: TWndClass; DCB: TDCB; Buf: array[0..255] of Char; Setting: string; - RxTh: Integer; - TxTh: Integer; begin { Open the comm port } StrPCopy(Buf, 'COM' + IntToStr(FCommPort)); @@ -472,137 +340,10 @@ begin else EscapeCommFunction(FCommId, CLRRTS); - { Register notification window class (once per process) } - if not NotifyClassRegistered then - begin - FillChar(WC, SizeOf(WC), 0); - WC.lpfnWndProc := @NotifyWndProc; - WC.cbWndExtra := SizeOf(Longint); - WC.hInstance := HInstance; - WC.lpszClassName := NotifyClassName; - if not RegisterClass(WC) then - begin - CloseComm(FCommId); - FCommId := -1; - raise Exception.Create('RegisterClass failed'); - end; - NotifyClassRegistered := True; - end; - - { Create hidden notification window and store Self for dispatch } - FHWndNotify := CreateWindow(NotifyClassName, '', ws_Popup, - 0, 0, 0, 0, 0, 0, HInstance, nil); - if FHWndNotify = 0 then - begin - CloseComm(FCommId); - FCommId := -1; - raise Exception.Create('CreateWindow failed'); - end; - SetWindowLong(FHWndNotify, 0, Longint(Self)); - - { Enable event mask for modem status, errors, and breaks. } - { ev_RxChar is deliberately excluded: it fires per received byte, } - { flooding WM_COMMNOTIFY with CN_EVENT on every ISR. Data arrival } - { is already handled by CN_RECEIVE via EnableCommNotification. } - SetCommEventMask(FCommId, - ev_CTS or ev_DSR or ev_RLSD or ev_Ring or - ev_Err or ev_Break); - - { Enable comm notifications -- -1 disables the notification } - if FRThreshold > 0 then - RxTh := FRThreshold - else - RxTh := -1; - if FSThreshold > 0 then - TxTh := FSThreshold - else - TxTh := -1; - EnableCommNotification(FCommId, FHWndNotify, RxTh, TxTh); - - { Reset modem line shadow state } - FCTSState := False; - FDSRState := False; - FCDState := False; - FPortOpen := True; end; -procedure TKPComm.ProcessEventNotify; -var - EvtMask: Word; - Stat: TComStat; - ErrFlags: Integer; -begin - EvtMask := GetCommEventMask(FCommId, - ev_CTS or ev_DSR or ev_RLSD or ev_Ring or ev_Err or ev_Break); - - if (EvtMask and ev_Break) <> 0 then - DoCommEvent(comEvtBreak); - - { Modem line shadow state: toggle on each transition event. } - { The 16-bit comm API only provides transition events, not } - { absolute line levels. } - if (EvtMask and ev_CTS) <> 0 then - begin - FCTSState := not FCTSState; - DoCommEvent(comEvCTS); - end; - - if (EvtMask and ev_DSR) <> 0 then - begin - FDSRState := not FDSRState; - DoCommEvent(comEvDSR); - end; - - if (EvtMask and ev_RLSD) <> 0 then - begin - FCDState := not FCDState; - DoCommEvent(comEvCD); - end; - - if (EvtMask and ev_Ring) <> 0 then - DoCommEvent(comEvRing); - - if (EvtMask and ev_Err) <> 0 then - begin - ErrFlags := GetCommError(FCommId, Stat); - if (ErrFlags and ce_Frame) <> 0 then - DoCommEvent(comEvtFrame); - if (ErrFlags and ce_Overrun) <> 0 then - DoCommEvent(comEvtOverrun); - if (ErrFlags and ce_RxOver) <> 0 then - DoCommEvent(comEvtRxOver); - if (ErrFlags and ce_RxParity) <> 0 then - DoCommEvent(comEvtParity); - if (ErrFlags and ce_TxFull) <> 0 then - DoCommEvent(comEvtTxFull); - end; -end; - - -procedure TKPComm.ProcessReceiveNotify; -begin - if FRThreshold <= 0 then - Exit; - { WM_COMMNOTIFY with CN_RECEIVE means data is available -- the driver } - { already checked the threshold. No need to call GetCommError here. } - DoCommEvent(comEvReceive); -end; - - -procedure TKPComm.ProcessTransmitNotify; -var - Stat: TComStat; -begin - if FSThreshold <= 0 then - Exit; - GetCommError(FCommId, Stat); - if Integer(Stat.cbOutQue) <= FSThreshold then - DoCommEvent(comEvSend); -end; - - procedure TKPComm.SetBreak(Value: Boolean); begin FBreakState := Value; @@ -685,7 +426,7 @@ begin begin Written := WriteComm(FCommId, @Value[1], Length(Value)); if Written < 0 then - DoCommEvent(comEvtTxFull); + raise Exception.Create('WriteComm failed'); end; end; diff --git a/delphi/KPTEST.DPR b/delphi/KPTEST.DPR index f51c4f1..fa7faab 100644 --- a/delphi/KPTEST.DPR +++ b/delphi/KPTEST.DPR @@ -8,5 +8,5 @@ uses begin Application.CreateForm(TMainForm, MainForm); - Application.Run; + MainForm.Run; end. diff --git a/delphi/TESTMAIN.PAS b/delphi/TESTMAIN.PAS index 2f3a5db..f3f4f70 100644 --- a/delphi/TESTMAIN.PAS +++ b/delphi/TESTMAIN.PAS @@ -5,8 +5,8 @@ unit TestMain; { } { Layout: toolbar row at top (port, settings, open/close, status), } { TKPAnsi terminal filling the rest of the form. Received serial data } -{ is fed to the terminal via TKPAnsi.Write; keystrokes from the terminal } -{ are sent out via TKPComm.Output. } +{ is polled from TKPComm.Input in a PeekMessage main loop; keystrokes } +{ from the terminal are sent out via TKPComm.Output. } interface @@ -29,13 +29,15 @@ type FBtnOpen: TButton; { Opens the serial port } FBtnClose: TButton; { Closes the serial port } FLabelStatus: TLabel; { Displays "Open" or "Closed" } + + FDone: Boolean; { True when WM_QUIT received } procedure AnsiKeyData(Sender: TObject; const Data: string); procedure BtnCloseClick(Sender: TObject); procedure BtnOpenClick(Sender: TObject); - procedure CommEvent(Sender: TObject); procedure UpdateStatus; public constructor Create(AOwner: TComponent); override; + procedure Run; end; var @@ -68,10 +70,9 @@ end; procedure TMainForm.BtnOpenClick(Sender: TObject); begin try - FComm.CommPort := StrToInt(FEditPort.Text); - FComm.Settings := FEditSettings.Text; - FComm.RThreshold := 1; - FComm.PortOpen := True; + FComm.CommPort := StrToInt(FEditPort.Text); + FComm.Settings := FEditSettings.Text; + FComm.PortOpen := True; except on E: Exception do FAnsi.Write('Open failed: ' + E.Message + #13#10); @@ -81,28 +82,6 @@ begin end; -procedure TMainForm.CommEvent(Sender: TObject); -var - S: string; -begin - case FComm.CommEvent of - comEvReceive: - begin - { Drain all available data. The driver uses edge-triggered } - { CN_RECEIVE: it posts once when rxCount crosses the threshold } - { and won't re-post until rxCount drops below and re-crosses. } - { If we don't drain here, remaining data stalls permanently. } - { Write renders each chunk immediately (no batching). } - repeat - S := FComm.Input; - if Length(S) > 0 then - FAnsi.Write(S); - until Length(S) = 0; - end; - end; -end; - - constructor TMainForm.Create(AOwner: TComponent); begin inherited CreateNew(AOwner); @@ -114,7 +93,6 @@ begin { Serial component } FComm := TKPComm.Create(Self); - FComm.OnComm := CommEvent; { Row 1: Port and Settings } FLabelPort := TLabel.Create(Self); @@ -177,15 +155,54 @@ begin { Font diagnostic: write known CP437 box-drawing characters. } { If the OEM font is working, you should see: } - { Line 1: single-line box top ┌───┐ } + { Line 1: single-line box top +---+ } { Line 2: shade + full block ░▒▓█ } - { Line 3: single-line box bottom └───┘ } - { If you see accented letters (Ú Ä ¿ ° ± ² Û À Ù), the font is } - { ANSI_CHARSET instead of OEM_CHARSET. } + { Line 3: single-line box bottom +---+ } + { If you see accented letters, the font is ANSI_CHARSET instead of } + { OEM_CHARSET. } FAnsi.Write(#$DA#$C4#$C4#$C4#$BF' '#$B0#$B1#$B2#$DB' '#$C0#$C4#$C4#$C4#$D9#13#10); end; +procedure TMainForm.Run; +var + Msg: TMsg; + S: string; +begin + FDone := False; + while not FDone do + begin + { Process all pending Windows messages (keyboard, paint, scrollbar) } + while PeekMessage(Msg, 0, 0, 0, pm_Remove) do + begin + if Msg.message = wm_Quit then + begin + FDone := True; + Break; + end; + TranslateMessage(Msg); + DispatchMessage(Msg); + end; + + if FDone then + Break; + + { Poll serial data directly -- no WM_COMMNOTIFY, no events } + if FComm.PortOpen then + begin + repeat + S := FComm.Input; + if Length(S) > 0 then + FAnsi.Write(S); + until Length(S) = 0; + end; + + { Tick blink state (uses GetTickCount, no WM_TIMER) } + FAnsi.TickBlink; + end; +end; + + procedure TMainForm.UpdateStatus; begin if FComm.PortOpen then diff --git a/drv/commdrv.c b/drv/commdrv.c index 32c6af8..c935692 100644 --- a/drv/commdrv.c +++ b/drv/commdrv.c @@ -215,16 +215,7 @@ PortStateT ports[MAX_PORTS]; // ----------------------------------------------------------------------- static HANDLE ghInstance = NULL; -// ----------------------------------------------------------------------- -// Dynamically resolved PostMessage -// -// comm.drv is loaded from [boot] before USER.EXE, so we can't have a -// static import for PostMessage. Resolve it on first use via -// GetProcAddress(GetModuleHandle("USER"), "PostMessage"). -// ----------------------------------------------------------------------- -PostMessageProcT pfnPostMessage = NULL; - -// ISR hit counter for diagnostics +// ISR hit counter for diagnostics (incremented in isr4, wraps at 65535) volatile uint16_t isrHitCount = 0; @@ -577,7 +568,6 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue) port->rxHead = 0; port->rxTail = 0; port->rxCount = 0; - port->rxNotifySent = 0; port->comDeb.qInHead = 0; port->comDeb.qInTail = 0; port->comDeb.qInCount = 0; @@ -589,7 +579,6 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue) port->txHead = 0; port->txTail = 0; port->txCount = 0; - port->txNotifySent = 0; port->comDeb.qOutHead = 0; port->comDeb.qOutTail = 0; port->comDeb.qOutCount = 0; @@ -726,42 +715,27 @@ void enableFifo(PortStateT *port) // ----------------------------------------------------------------------- // enableNotification - Register window for WM_COMMNOTIFY (ordinal 100) +// +// Retained as a no-op stub for API compatibility. Stores threshold +// values but nothing reads them -- polling replaces notifications. // ----------------------------------------------------------------------- int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh) { PortStateT *port; - dbgInt16("KPCOMM: enableNotif Id", commId); - dbgHex16("KPCOMM: enableNotif hwnd", (uint16_t)hwnd); - dbgInt16("KPCOMM: enableNotif rxTh", rxThresh); - dbgInt16("KPCOMM: enableNotif txTh", txThresh); - if (commId < 0 || commId >= MAX_PORTS) { return FALSE; } port = &ports[commId]; if (!port->isOpen) { - dbgStr("KPCOMM: enableNotif not open\r\n"); return FALSE; } - // Lazily resolve PostMessage from USER.EXE - // (not available at boot when comm.drv is loaded from [boot]) - if (!pfnPostMessage) { - HMODULE hUser = GetModuleHandle("USER"); - if (hUser) { - pfnPostMessage = (PostMessageProcT)GetProcAddress(hUser, "PostMessage"); - } - } - port->hwndNotify = hwnd; port->rxNotifyThresh = rxThresh; port->txNotifyThresh = txThresh; - port->rxNotifySent = 0; - port->txNotifySent = 0; - dbgStr("KPCOMM: enableNotif OK\r\n"); return TRUE; } @@ -1095,8 +1069,6 @@ static void initPortState(PortStateT *port, int16_t commId) port->txImmediate = -1; port->rxNotifyThresh = -1; port->txNotifyThresh = -1; - port->rxNotifySent = 0; - port->txNotifySent = 0; } @@ -1294,12 +1266,6 @@ int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len) port->rxCount--; bytesRead++; } - // Reset edge flag under CLI so the next ISR will re-post - // CN_RECEIVE when new data crosses the threshold again. - if (port->rxNotifyThresh >= 0 && - port->rxCount < (uint16_t)port->rxNotifyThresh) { - port->rxNotifySent = 0; - } _enable(); // If flow control was asserted and buffer has drained, deassert @@ -1458,15 +1424,13 @@ int16_t FAR PASCAL _export setque(int16_t commId, int16_t rxSz, int16_t txSz) port->rxSize = (uint16_t)rxSz; port->rxHead = 0; port->rxTail = 0; - port->rxCount = 0; - port->rxNotifySent = 0; - port->txBufH = newTxH; - port->txBuf = newTxBuf; - port->txSize = (uint16_t)txSz; - port->txHead = 0; - port->txTail = 0; - port->txCount = 0; - port->txNotifySent = 0; + port->rxCount = 0; + port->txBufH = newTxH; + port->txBuf = newTxBuf; + port->txSize = (uint16_t)txSz; + port->txHead = 0; + port->txTail = 0; + port->txCount = 0; port->comDeb.qInSize = (uint16_t)rxSz; port->comDeb.qInCount = 0; port->comDeb.qInHead = 0; @@ -1534,14 +1498,6 @@ int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len) port->txCount++; bytesWritten++; } - // Reset TX edge flag under CLI so the ISR will re-post - // CN_TRANSMIT when free space crosses the threshold again. - if (port->txNotifyThresh >= 0) { - uint16_t txFree = port->txSize - port->txCount; - if (txFree < (uint16_t)port->txNotifyThresh) { - port->txNotifySent = 0; - } - } _enable(); // Sync ComDEB queue counts diff --git a/drv/commdrv.h b/drv/commdrv.h index 026fabe..0f0eb2a 100644 --- a/drv/commdrv.h +++ b/drv/commdrv.h @@ -324,7 +324,7 @@ typedef struct { // Port state structure // // One per COM port. Accessed from both application context (reccom, -// sndcom, etc.) and ISR context (handleRx, handleTx, checkNotify). +// sndcom, etc.) and ISR context (handleRx, handleTx). // Fields shared between contexts are protected by _disable()/_enable(). // ----------------------------------------------------------------------- typedef struct { @@ -380,12 +380,10 @@ typedef struct { // Error accumulator (sticky CE_* bits, cleared by stacom/GetCommError) uint16_t errorFlags; // Accumulated CE_RXOVER, CE_OVERRUN, CE_FRAME, etc. - // WM_COMMNOTIFY event notification + // WM_COMMNOTIFY event notification (stored for API compat, not used) HWND hwndNotify; // Target window for PostMessage (0=disabled) - int16_t rxNotifyThresh; // CN_RECEIVE fires when rxCount >= this (-1=disabled) - int16_t txNotifyThresh; // CN_TRANSMIT fires when txFree >= this (-1=disabled) - uint8_t rxNotifySent; // Edge flag: 1 = CN_RECEIVE posted, suppresses repeats - uint8_t txNotifySent; // Edge flag: 1 = CN_TRANSMIT posted, suppresses repeats + int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled) + int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled) // ISR chaining and PIC management void (FAR *prevIsr)(void); // Previous ISR vector (restored on unhook) @@ -404,16 +402,6 @@ typedef struct { // ----------------------------------------------------------------------- extern PortStateT ports[MAX_PORTS]; -// ----------------------------------------------------------------------- -// Dynamically resolved PostMessage -// -// COMM.DRV loads at boot from [boot] in SYSTEM.INI, before USER.EXE -// is available. PostMessage is resolved lazily on first use via -// GetModuleHandle("USER") + GetProcAddress. NULL until resolved. -// ----------------------------------------------------------------------- -typedef BOOL (FAR PASCAL *PostMessageProcT)(HWND, UINT, WPARAM, LPARAM); -extern PostMessageProcT pfnPostMessage; - // ISR hit counter for diagnostics (incremented in isr4, wraps at 65535) extern volatile uint16_t isrHitCount; diff --git a/drv/isr.c b/drv/isr.c index db8743b..c811323 100644 --- a/drv/isr.c +++ b/drv/isr.c @@ -10,7 +10,6 @@ // Prototypes // ----------------------------------------------------------------------- static void checkFlowRx(PortStateT *port); -static void checkNotify(PortStateT *port); static void handleLsr(PortStateT *port, uint8_t lsr); static void handleMsr(PortStateT *port); static void handleRx(PortStateT *port, uint8_t lsr); @@ -82,62 +81,6 @@ static void checkFlowRx(PortStateT *port) } -// ----------------------------------------------------------------------- -// checkNotify - Post WM_COMMNOTIFY if threshold conditions met -// -// Called at end of ISR dispatch, outside the IIR loop. -// Uses PostMessage to avoid reentrancy issues. -// ----------------------------------------------------------------------- -static void checkNotify(PortStateT *port) -{ - uint16_t notifyBits; - - if (!port->hwndNotify) { - return; - } - - notifyBits = 0; - - // CN_RECEIVE: edge-triggered -- post once when rxCount reaches - // threshold, suppress until count drops below and re-crosses. - // Without edge detection, every ISR with rxCount >= 1 floods the - // message queue with stale WM_COMMNOTIFY, delaying keyboard input. - if (port->rxNotifyThresh >= 0) { - if (port->rxCount >= (uint16_t)port->rxNotifyThresh) { - if (!port->rxNotifySent) { - notifyBits |= CN_RECEIVE; - port->rxNotifySent = 1; - } - } else { - port->rxNotifySent = 0; - } - } - - // CN_TRANSMIT: edge-triggered -- post once when free space reaches - // threshold, suppress until space drops below and re-crosses. - if (port->txNotifyThresh >= 0) { - uint16_t txFree = port->txSize - port->txCount; - if (txFree >= (uint16_t)port->txNotifyThresh) { - if (!port->txNotifySent) { - notifyBits |= CN_TRANSMIT; - port->txNotifySent = 1; - } - } else { - port->txNotifySent = 0; - } - } - - // CN_EVENT: any event bits accumulated - if (port->comDeb.evtWord & port->comDeb.evtMask) { - notifyBits |= CN_EVENT; - } - - if (notifyBits && pfnPostMessage) { - pfnPostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits); - } -} - - // ----------------------------------------------------------------------- // handleLsr - Process line status errors // ----------------------------------------------------------------------- @@ -453,8 +396,6 @@ void isrDispatch(PortStateT *port) port->comDeb.qOutCount = port->txCount; port->comDeb.qOutHead = port->txHead; port->comDeb.qOutTail = port->txTail; - - checkNotify(port); }