Optimize TKPAnsi rendering with batched TextOut and dirty row tracking
Separate parsing from rendering to eliminate per-character GDI calls. ProcessChar now only updates cell data in memory; rendering is deferred to FlipToScreen which batches consecutive same-color cells into single TextOut calls (~5-10 per row instead of 80). Partial BitBlt transfers only the dirty row band to the screen. Non-blinking rows render to one buffer and BitBlt to the second, halving GDI work for typical content. Also removes redundant GetCommError from KPComm receive path and adds BeginUpdate/EndUpdate batching in the test app's CommEvent handler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
be566a5767
commit
ca99d1d21b
4 changed files with 576 additions and 235 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -351,7 +351,6 @@ end;
|
|||
|
||||
function TKPComm.GetInput: string;
|
||||
var
|
||||
Stat: TComStat;
|
||||
BytesToRead: Integer;
|
||||
BytesRead: Integer;
|
||||
Buf: array[0..255] of Char;
|
||||
|
|
@ -360,15 +359,12 @@ begin
|
|||
if not FPortOpen or (FCommId < 0) then
|
||||
Exit;
|
||||
|
||||
GetCommError(FCommId, Stat);
|
||||
BytesToRead := Stat.cbInQue;
|
||||
|
||||
{ Read directly without querying GetCommError first. ReadComm }
|
||||
{ returns the number of bytes actually available (up to BytesToRead) }
|
||||
{ so the extra GetCommError round-trip is unnecessary overhead. }
|
||||
BytesToRead := 255;
|
||||
if (FInputLen > 0) and (BytesToRead > FInputLen) then
|
||||
BytesToRead := FInputLen;
|
||||
if BytesToRead > 255 then
|
||||
BytesToRead := 255;
|
||||
if BytesToRead <= 0 then
|
||||
Exit;
|
||||
|
||||
BytesRead := ReadComm(FCommId, @Buf, BytesToRead);
|
||||
if BytesRead <= 0 then
|
||||
|
|
@ -558,14 +554,12 @@ end;
|
|||
|
||||
|
||||
procedure TKPComm.ProcessReceiveNotify;
|
||||
var
|
||||
Stat: TComStat;
|
||||
begin
|
||||
if FRThreshold <= 0 then
|
||||
Exit;
|
||||
GetCommError(FCommId, Stat);
|
||||
if Integer(Stat.cbInQue) >= FRThreshold then
|
||||
DoCommEvent(comEvReceive);
|
||||
{ WM_COMMNOTIFY with CN_RECEIVE means data is available -- the driver }
|
||||
{ already checked the threshold. No need to call GetCommError here. }
|
||||
DoCommEvent(comEvReceive);
|
||||
end;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -85,9 +85,19 @@ begin
|
|||
case FComm.CommEvent of
|
||||
comEvReceive:
|
||||
begin
|
||||
S := FComm.Input;
|
||||
if Length(S) > 0 then
|
||||
FAnsi.Write(S);
|
||||
{ Drain all available data in a single update batch. This }
|
||||
{ suppresses per-Write rendering so we get one paint at the }
|
||||
{ end instead of one per 255-byte chunk. }
|
||||
FAnsi.BeginUpdate;
|
||||
try
|
||||
repeat
|
||||
S := FComm.Input;
|
||||
if Length(S) > 0 then
|
||||
FAnsi.Write(S);
|
||||
until Length(S) = 0;
|
||||
finally
|
||||
FAnsi.EndUpdate;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -131,7 +141,7 @@ begin
|
|||
FEditSettings.Left := 148;
|
||||
FEditSettings.Top := 8;
|
||||
FEditSettings.Width := 140;
|
||||
FEditSettings.Text := '9600,N,8,1';
|
||||
FEditSettings.Text := '115200,N,8,1';
|
||||
|
||||
FBtnOpen := TButton.Create(Self);
|
||||
FBtnOpen.Parent := Self;
|
||||
|
|
@ -164,6 +174,15 @@ begin
|
|||
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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -259,6 +259,13 @@ void applyBaudRate(PortStateT *port, uint16_t baud)
|
|||
base = port->baseAddr;
|
||||
divisor = (uint16_t)(BAUD_DIVISOR_BASE / actualBaud);
|
||||
|
||||
// Guard: divisor 0 means the UART treats it as 65536, giving ~1.76 baud.
|
||||
// This can happen when BuildCommDCB stores a raw truncated value for
|
||||
// 115200 (e.g. 0xE101 = 57601) and a future rate exceeds 115200.
|
||||
if (divisor == 0) {
|
||||
divisor = 1;
|
||||
}
|
||||
|
||||
// Set DLAB to access divisor latch
|
||||
lcr = (uint8_t)_inp(base + UART_LCR);
|
||||
_outp(base + UART_LCR, lcr | LCR_DLAB);
|
||||
|
|
@ -315,6 +322,11 @@ void applyLineParams(PortStateT *port, uint8_t byteSize, uint8_t parity, uint8_t
|
|||
break;
|
||||
}
|
||||
|
||||
dbgHex16("KPCOMM: applyLine byteSize", (uint16_t)byteSize);
|
||||
dbgHex16("KPCOMM: applyLine parity", (uint16_t)parity);
|
||||
dbgHex16("KPCOMM: applyLine stopBits", (uint16_t)stopBits);
|
||||
dbgHex16("KPCOMM: applyLine LCR", (uint16_t)lcr);
|
||||
|
||||
_outp(base + UART_LCR, lcr);
|
||||
|
||||
port->byteSize = byteSize;
|
||||
|
|
@ -948,10 +960,12 @@ int16_t FAR PASCAL _export inicom(DCB FAR *dcb)
|
|||
_inp(port->baseAddr + UART_RBR);
|
||||
|
||||
// Populate ComDEB for third-party compatibility
|
||||
port->comDeb.port = port->baseAddr;
|
||||
port->comDeb.port = port->baseAddr;
|
||||
port->comDeb.baudRate = port->baudRate;
|
||||
port->comDeb.qInSize = port->rxSize;
|
||||
port->comDeb.qOutSize = port->txSize;
|
||||
port->comDeb.lcrShadow = (uint8_t)_inp(port->baseAddr + UART_LCR);
|
||||
port->comDeb.mcrShadow = (uint8_t)_inp(port->baseAddr + UART_MCR);
|
||||
|
||||
// Enable receive and line status interrupts
|
||||
_outp(port->baseAddr + UART_IER, IER_RDA | IER_LSI | IER_MSI);
|
||||
|
|
@ -1101,7 +1115,9 @@ void primeTx(PortStateT *port)
|
|||
// reactivateOpenCommPorts - Reactivate all ports after task switch (ordinal 18)
|
||||
//
|
||||
// Called by Windows when switching back to this VM.
|
||||
// Re-enables interrupts and restores MCR state.
|
||||
// Restores full UART state: baud rate, line params (LCR), MCR, FIFOs,
|
||||
// and re-enables interrupts. A DOS fullscreen app or VM switch may
|
||||
// have reprogrammed the UART, so we must restore everything.
|
||||
// -----------------------------------------------------------------------
|
||||
void FAR PASCAL _export reactivateOpenCommPorts(void)
|
||||
{
|
||||
|
|
@ -1117,6 +1133,12 @@ void FAR PASCAL _export reactivateOpenCommPorts(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Restore baud rate (sets DLAB, writes divisor, clears DLAB)
|
||||
applyBaudRate(port, port->baudRate);
|
||||
|
||||
// Restore line parameters (word length, parity, stop bits)
|
||||
applyLineParams(port, port->byteSize, port->parity, port->stopBits);
|
||||
|
||||
// Restore MCR (DTR, RTS, OUT2)
|
||||
mcr = MCR_OUT2;
|
||||
if (port->dtrState) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue