einige von euch haben ja schon beim meinem Programm 'Z180SoftSystem' herein geschaut.
Dort bin ich gerade dabei die Terminal Emulation in einem eigenen Thread laufen zu lassen, um diese von der eigentlichen Emulation des Z180 Prozessor System zu entkoppeln.
Das funktioniert soweit auch, jedoch hätte ich noch ein kleines Problem für welches ich eine evtl. elegantere oder generell bessere Lösung suche.
Ich habe den Terminal Code auf das wesentliche reduziert (wären ansonsten über 1000 Zeilen):
Code: Alles auswählen
unit System_Terminal;
{$mode objfpc}{$H+}
interface
uses SysUtils, Graphics, Classes;
type
TTerminalEmulation = class(TThread)
private
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: boolean);
end;
...
var
...
TerminalEmulation: TTerminalEmulation;
procedure writeTerminalCharacter(character: byte);
implementation
type
TTermMode = (STANDARD, VT52_ESC, ANSI_ESC, ANSI_ESC_PAR, DCA_ROW, DCA_COLUMN);
var
termMode: TTermMode;
newCharWriting: boolean;
...
characterBuffer: string;
...
enableTerminalLogging: boolean;
loggingFile: file of char;
...
// --------------------------------------------------------------------------------
procedure writeTerminalCharacter(character: byte);
begin
if (character > $00) then begin
newCharWriting := True;
characterBuffer := characterBuffer + char(character);
newCharWriting := False;
end;
end;
// --------------------------------------------------------------------------------
procedure TTerminalEmulation.Execute;
var
character: byte;
...
// ----------------------------------------
begin
repeat
if ((characterBuffer.Length = 0) or (newCharWriting)) then begin
Sleep(10);
end
else begin
character := byte(characterBuffer[1]);
Delete(characterBuffer, 1, 1);
case termMode of
STANDARD: normalTerminalMode;
VT52_ESC: vt52EscapeMode;
ANSI_ESC: ansiEscapeMode;
ANSI_ESC_PAR: ansiEscapeModeParameter;
DCA_ROW: begin
if (character >= $20) then begin
dcaRow := character - $20;
termMode := DCA_COLUMN;
end
else begin
termMode := STANDARD;
end;
end;
DCA_COLUMN: begin
if (character >= $20) then begin
setCursorPosition(dcaRow, character - $20);
end;
termMode := STANDARD;
end;
end;
if (enableTerminalLogging) then begin
Write(loggingFile, chr(character));
end;
end;
until (Terminated);
end;
// --------------------------------------------------------------------------------
constructor TTerminalEmulation.Create(CreateSuspended: boolean);
begin
FreeOnTerminate := True;
inherited Create(CreateSuspended);
end;
// --------------------------------------------------------------------------------
initialization
begin
newCharWriting := True;
...
characterBuffer := '';
end;
// --------------------------------------------------------------------------------
end.
In der Threaded-Methode 'execute' wird dieser ausgelesen und entsprechend ausgegeben oder eben die Steuerzeichen verarbeitet. Wenn nun aber die Prozessor Emulation gerade ein Zeichen in den Buffer schreibt und gleichzeitig die 'execute'-Methode daraus lesen will, ergeben sich sporadisch fehlende oder falsche Zeichen auf dem Terminal Display.
Das habe ich mit dem Flag 'newCharWriting' versucht zu umgehen. Funktioniert auch bis auf sehr sehr seltene Fehler. Mich würde nun interessieren ob es da wie schon geschrieben eine elegantere oder generell besser Variante gibt.
Ich habe bis jetzt noch nichts mit Threading programmiert. Im Wiki habe ich vom CriticalSections gelesen,
konnte das aber bis jetzt nicht für mein Problem adaptieren.
Würde mich über Hilfe sehr freuen.
Grüße
HobbyProgrammer