WinComm/delphi/TESTMAIN.PAS
Scott Duensing c8ccedf569 Change default FIFO trigger from 8 to 1, yield CPU when idle
Default RX FIFO trigger level of 8 (FCR_TRIG_8) caused exactly 8
characters to buffer in hardware before the ISR fired. Change default
to 1 (FCR_TRIG_1) for responsive single-character interrupts while
keeping the 16-byte FIFO enabled as a safety buffer. COMnRxTRIGGER
SYSTEM.INI key still allows override.

The PM_NOYIELD polling loop never yielded the CPU timeslice, starving
other Windows applications. Add Yield call when no serial data is
flowing so other apps get CPU time. During bulk data flow HasData
stays true and the loop runs at full speed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:44:14 -06:00

230 lines
6.2 KiB
ObjectPascal

unit TestMain;
{ Test application for TKPComm and TKPAnsi components. }
{ Form and all controls are created in code (no DFM required). }
{ }
{ Layout: toolbar row at top (port, settings, open/close, status), }
{ TKPAnsi terminal filling the rest of the form. Received serial data }
{ is polled from TKPComm.Input in a PeekMessage main loop; keystrokes }
{ from the terminal are sent out via TKPComm.Output. }
interface
uses
SysUtils, Classes, WinTypes, WinProcs, Messages,
Forms, Controls, StdCtrls, KPComm, KPAnsi;
type
TMainForm = class(TForm)
private
{ Components (owned by Self, freed automatically) }
FComm: TKPComm; { Serial communications component }
FAnsi: TKPAnsi; { ANSI terminal display }
{ Toolbar controls }
FLabelPort: TLabel; { "Port:" label }
FEditPort: TEdit; { COM port number entry }
FLabelSettings: TLabel; { "Settings:" label }
FEditSettings: TEdit; { Baud/parity/data/stop entry }
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 UpdateStatus;
public
constructor Create(AOwner: TComponent); override;
procedure Run;
end;
var
MainForm: TMainForm;
implementation
procedure TMainForm.AnsiKeyData(Sender: TObject; const Data: string);
begin
if FComm.PortOpen and (Length(Data) > 0) then
begin
try
FComm.Output := Data;
except
on E: Exception do
{ Ignore send errors from keyboard input }
end;
end;
end;
procedure TMainForm.BtnCloseClick(Sender: TObject);
begin
FComm.PortOpen := False;
UpdateStatus;
end;
procedure TMainForm.BtnOpenClick(Sender: TObject);
begin
try
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);
end;
UpdateStatus;
FAnsi.SetFocus;
end;
constructor TMainForm.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
Caption := 'KPComm ANSI Terminal';
Width := 660;
Height := 460;
BorderStyle := bsSingle;
{ Serial component }
FComm := TKPComm.Create(Self);
{ Row 1: Port and Settings }
FLabelPort := TLabel.Create(Self);
FLabelPort.Parent := Self;
FLabelPort.Left := 8;
FLabelPort.Top := 12;
FLabelPort.Caption := 'Port:';
FEditPort := TEdit.Create(Self);
FEditPort.Parent := Self;
FEditPort.Left := 44;
FEditPort.Top := 8;
FEditPort.Width := 32;
FEditPort.Text := '1';
FLabelSettings := TLabel.Create(Self);
FLabelSettings.Parent := Self;
FLabelSettings.Left := 88;
FLabelSettings.Top := 12;
FLabelSettings.Caption := 'Settings:';
FEditSettings := TEdit.Create(Self);
FEditSettings.Parent := Self;
FEditSettings.Left := 148;
FEditSettings.Top := 8;
FEditSettings.Width := 140;
FEditSettings.Text := '115200,N,8,1';
FBtnOpen := TButton.Create(Self);
FBtnOpen.Parent := Self;
FBtnOpen.Left := 300;
FBtnOpen.Top := 8;
FBtnOpen.Width := 65;
FBtnOpen.Height := 25;
FBtnOpen.Caption := 'Open';
FBtnOpen.OnClick := BtnOpenClick;
FBtnClose := TButton.Create(Self);
FBtnClose.Parent := Self;
FBtnClose.Left := 372;
FBtnClose.Top := 8;
FBtnClose.Width := 65;
FBtnClose.Height := 25;
FBtnClose.Caption := 'Close';
FBtnClose.Enabled := False;
FBtnClose.OnClick := BtnCloseClick;
FLabelStatus := TLabel.Create(Self);
FLabelStatus.Parent := Self;
FLabelStatus.Left := 450;
FLabelStatus.Top := 12;
FLabelStatus.Caption := 'Closed';
{ ANSI terminal }
FAnsi := TKPAnsi.Create(Self);
FAnsi.Parent := Self;
FAnsi.Left := 0;
FAnsi.Top := 38;
FAnsi.OnKeyData := AnsiKeyData;
{ Font diagnostic: write known CP437 box-drawing characters. }
{ If the OEM font is working, you should see: }
{ 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. }
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;
HasData: Boolean;
begin
Show;
FDone := False;
while not FDone do
begin
{ Process all pending Windows messages (keyboard, paint, scrollbar) }
while PeekMessage(Msg, 0, 0, 0, pm_Remove or pm_NoYield) 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 -- read one chunk per iteration }
HasData := False;
if FComm.PortOpen then
begin
S := FComm.Input;
if Length(S) > 0 then
begin
FAnsi.WriteDeferred(S);
HasData := True;
end;
end;
{ Tick blink (dirties rows if interval elapsed), then render }
FAnsi.TickBlink;
FAnsi.FlipToScreen;
{ Yield CPU to other apps when no serial data is flowing. }
{ PM_NOYIELD keeps message draining fast; Yield here gives other }
{ apps a timeslice only when idle. During bulk data flow, HasData }
{ stays True and the loop runs at full speed. }
if not HasData then
Yield;
end;
end;
procedure TMainForm.UpdateStatus;
begin
if FComm.PortOpen then
FLabelStatus.Caption := 'Open'
else
FLabelStatus.Caption := 'Closed';
FBtnOpen.Enabled := not FComm.PortOpen;
FBtnClose.Enabled := FComm.PortOpen;
end;
end.