Per Accessory in den Tastaturpuffer

Jeder wird schon einmal das folgende Problem gehabt und sich darüber geärgert haben: Man ist gerade dabei, ein Protokoll oder ähnliches zu schreiben, und muß dafür zwischendurch eine kleine Rechnung durchführen. Da man ja ein GEM-Programm benutzt, ruft man einfach sein Taschenrechner-Accessory auf, führt seine Rechnung durch und steht dann vor einem Problem: Es wäre schön, wenn das Ergebnis unserer Rechnung direkt an die Textverarbeitung ausgegeben würde. Da dies aber nicht geschieht, müssen wir Zusatz-Hardware (Schmierzettel) verwenden.

Dies bedeutet einen erheblichen Mehraufwand, da unser Ergebnis erst auf den Schmierzettel und danach noch einmal in die Textverarbeitung eingegeben werden muß. Da ich viele Rechenprogramme selber schreibe, habe ich mir die folgenden Routinen überlegt, um wenigstens bei der eigenen Software dieses Manko umgehen zu können.

Diese Routinen ermöglichen wahlweise die Ausgabe eines Strings über den Puffer des Tastaturprozessors oder den GEM-konformeren Weg über das Clipboard. Beginnen wir mit der Ausgabe über den Tastaturpuffer.

XBIOS hilft

Das XBIOS stellt die Funktion IOREC zur Verfügung, mit der man die Adressen der Pufferspeicher der seriellen Geräte ermitteln kann. Die zurückgegebenen Werte werden in einem Record abgelegt. Dieses enthält neben der Adresse (ibuf) auch noch Angaben überdie Puffergröße (ibufsiz), die Schreibposition des nächsten Zeichens im Puffer (ibufhd), dann folgt die Leseposition des nächsten Zeichens im Puffer (ibuftl) und zum Schluß noch die Minimalmarke (ibuflow) und die Maximalmarke (ibufhi).

Die Prozedur Tastatur funktioniert wie folgt. Nachdem wir die Adresse des Puffers haben, wird der übergebene String in seine Einzelteile zerlegt. Mit dem ASCII-Wert des Zeichens wird der dazugehörige Scancode ermittelt und dieser dann in den Tastaturpuffer gepoket. Es folgt die Anpassung der Schreib-/Leseposition an die neuen Verhältnisse.

Das in den Puffer gepoket werden muß, ist zwar nicht besonders elegant, jedoch läßt es sich über Zeigermanipulationen nicht durchführen, so daß man leider diesen Weg einschlagen muß. Da jedoch das Betriebssystem die Adresse liefert und diese im Programm nicht fest verankert ist, sollte das auch in neuen Betriebssystem- und Rechnerversionen funktionieren.

Den umfangreichsten Teil dieser Routine nimmt die Scancode-Tabelle ein. Sie enthält den Code der ASCII-Werte 0-127 sowie der deutschen Sonderzeichen. Wer will, kann sie noch erweitern. Oft möchte man eine Eingabe mit einem CR und LF abschließen. Dies ist im Beispiel bereits durchgeführt. feld[13] hat hier nicht mehr den Scancode $32000D (Ctrl. M) sondern $1C000D (entspricht der Return-Taste), und feld[10] kann in $00000A (LF) umgewandelt werden. Hängt man nun an den Ausgabe-String ein chr(13) an, wird dies mit einem CR/LF im Editor umgesetzt.

Leider nicht überall

Eine Einschränkung ist leider zu machen. So funktioniert diese Routine nicht mit Programmen, die eine eigene Scancode-Tabelle verwenden wie z.B. Script.

Um dem verwendeten Editor Zeit zu geben, auf die Tasteneingaben zu reagieren, wird nach jeder Übergabe ein event_timer-Aufruf gemacht, damit die Applikation des Editors wieder ablaufen kann. Im allgemeinen reichen etwa 25 ms dafür aus. Da es aber auch langsamer reagierende Programme gibt, kann dieser Wert in 25ms-Schritten verändert werden. Bei 7UP z.B. liegt dieser Wert bei etwa 50 ms. Ist die Übergabe zu schnell, werden Zeichen verschluckt. Es kann weiterhin Vorkommen, daß das erste Zeichen des Strings verschluckt wird. Um dies abzufangen, wird vor der Übertragung noch einmal eine Pause von 100 ms eingelegt, um die Eingabebereitschaft des Editors zu gewährleisten.

GEM-Clipboard

Kommen wir zur zweiten Routine, der Clipboard-Benutzung. In diesem Falle stellt uns das AES die benötigten Funktionen zur Verfügung. Es sind dies die Opcode-Nr. 80 und 81 mit den Namen SCRAP_READ und SCRAP_WRITE. Ersteres erfragt den Pfad eines bereits gesetzten Clipboards, letzteres setzt diesen Pfad. In Maxon Pascal sind sie bereits vordefiniert und lassen sich daher einfach verwenden.

Nun macht die Clipboard-Routine folgendes: Zuerst sehen wir mit SCRP_READ nach, ob bereits ein Clipboard-Pfad vorhanden ist. Sollte dies nicht der Fall sein, wird die Prozedur Clip_schreiben aufgerufen. Hier wird das Boot-Laufwerk aus den Systemvariablen bestimmt und auf diesem der Clipboard-Ordnergeschrieben. Mit SCAP_WRITE wird dann noch dem AES der Pfad für diesen Ordner mitgeteilt. Nachdem der Pfad bekannt ist, wird das aktuelle Laufwerk zwischengespeichert und danach das Laufwerk des Clipboardes gesetzt. Es folgt eine Durchsuchung des Ordners nach Dateien, und, sofern vorhanden, werden diese gelöscht, da nur immer eine Datei im Clipboard sein sollte. In diesem Zusammenhang gibt es leider in der Literatur einige Widersprüche. So ist in früheren Ausgaben des Profibuches zu lesen, daß nur die zu ersetzende Datei gelöscht werden und das Clipboard auch mehrere Dateien enthalten darf. In der neuesten Ausgabe wird davon ausgegangen, daß nur jeweils eine Datei vorhanden sein sollte und überzählige Dateien zu löschen seien. Die hier vorgestellte Routine hält sich an die neuen Vorgaben. Danach bleibt nur noch, den String in eine Textdatei zu schreiben. Dazu wird dieser in einen C-String verwandelt und dann abgespeichert. Dieser ist ein packed array [0..len] of char und wird mit einem CHR(0) abgeschlossen. Deshalb muß, um einen Text abzuspeichern, nur dieses CHR(0) nicht mit abgespeichert werden, und man erhält einen einfachen ASCII-Text. Ist unsere Datei geschrieben, wird wieder das alte Laufwerk gesetzt.

Der beigefügte Sourcecode erzeugt ein Accessory, welches einen String über den Tastaturpuffer ausgibt und diesen in das Clipboard schreibt. Deshalb sollten Sie, bevor Sie es aufrufen, einen Editor starten, damit Sie die Ausgabe über den Tastaturpuffer sehen können. Das Shareware-Programm 7UP eignet sich dafür besonders gut, weil es auch das Clipboard direkt unterstützt.

Neben der Ausgabe von schnöden Berechnungen lassen sich natürlich noch viele andere Anwendungsmöglichkeiten für diese Routinen finden. Wie wäre es beispielsweise mit einem Floskelgenerator oder einer Ausgabe Ihrer Anschrift im Briefkopf mit Datum usw. auf Knopfdruck?

Literatur:
Maxon Pascal Handbuch
Atari Profibuch ST,STE,TT Sybex Verlag
ST-Computer 1/91, S.72, Jump von Stefan Dreckmann

(* Tastaturpuffer per Accessory füllen *)
(* (c)1992 by MAXON-Computer *)
(* Autor: Rainer Esser *)

{$M 4,4,10,20}
PROGRAM ausgabe demo;

uses stpascal,dos,bios,gemdecl,gemaes;

type
    str255=string[255];

VAR
    menu id, ap_id: integer; 
    name,kette:str255;

PROCEDURE tastatur(ausgabe:str255;zeit:integer);
TYPE
    puffer=RECORD 
        ibuf:longint; 
        ibufsiz:integer; 
        ibufhd:integer; 
        ibuftl:integer; 
        ibuflow:integer; 
        ibufhi:integer;
    END;
    zeiger=^puffer;
VAR
    t,i,event,dummy,timer:integer; 
    aus:longint; 
    zeiger1:^puffer; 
    feld:ARRAY[0..158] OF longint; 
    msg:array_8; 
    p:^longint;

PROCEDURE tabelle;
BEGIN
    feld[0]:=$030000; 
    feld[1]:=$120001; 
    feld[2]:=$300002; 
    feld[3]:=$2E0003; 
    feld[4]:=$200004; 
    feld[5]:=$120005; 
    feld[6]:=$210006; 
    feld[7]:=$220007; 
    feld[8]:=$230008; 
    feld[9]:=$170009; 
    feld[10]:=$24000A; 
    feld[11]:=$25000B; 
    feld[12]:=$26000C; 
    feld[13]:=$1C000D; 
    feld[14):=$31000E; 
    feld[15]:=$18000F; 
    feld[16]:=$190010; 
    feld[17]:=$100011; 
    feld[18]:=$130012; 
    feld[19]:=$1F0013; 
    feld[20]:=$140014; 
    feld[21]:=$160015; 
    feld[22]:=$2F0016; 
    feld[23]:=$110017; 
    feld[24]:=$2D0018; 
    feld[25]:=$150019; 
    feld[26]:=$2C001A; 
    feld[27]:=$1A001B; 
    feld[28]:=$28001C; 
    feld[29]:=$1B001D; 
    feld[30]:=$07001E; 
    feld[31]:=$07001E; 
    feld[32]:=$390020; 
    feld[33]:=$020021;
    feld[34]:=$280022; 
    feld[35]:=$040023; 
    feld[36]:=$050024; 
    feld[37]:=$060025; 
    feld[38]:=$080026; 
    feld[39]:=$280027; 
    feld[40]:=$0A0028; 
    feld[41]:=$OB0029; 
    feld[42]:=$09002A;
    feld[43]:=$0D0O2B; 
    feld[44]:=$33002C; 
    feld[45]:=$0C002D; 
    feld[46]:=$34002E; 
    feld[47]:=$350022; 
    feld[48]:=$CB0030; 
    feid[49]:=$020031; 
    feld[50]:=$030032; 
    feld[51]:=$040033; 
    feld[52]:=$050034; 
    feld[53]:=$060035; 
    feld[54]:=$070036; 
    feld[55]:=$080037; 
    feld[56];=$090038; 
    feld[57]:=$0A0039; 
    feld[58]:=$27003A; 
    feld[59]:=$27003B; 
    feld[60]:=$33003C; 
    feld[61]:=$0D003D; 
    feld[62]:=$34003E; 
    feld[63]:=$350032; 
    feld[64]:=$030040; 
    feld[65]:=$1E0041; 
    feld[66]:=$300042; 
    feld[67]:=$2EO043; 
    feld[68]:=$200044; 
    feld[69]:=$120045; 
    feld[70]:=$210046; 
    feld[71]:=$220047; 
    feld[72]:=$230048; 
    feld[73]:=$170049; 
    feld[74]:=$24004A; 
    feld[75]:=$25004B; 
    feld[76];=$26C04C; 
    feld[77]:=$320040; 
    feld[78]:=$31004E; 
    feld[79]:=$180042; 
    feld[80]:=$190050; 
    feld[81]:=$100051; 
    feld[82]:=$130052; 
    feld[83]:=$1F0053; 
    feld[84]:=$140054; 
    feld[85]:=$160055; 
    feld[86]:=$220056; 
    feld[87]:=$110057; 
    feld[88]:=$2D0058; 
    feld[891:=$150059; 
    feld[90]:=$2C005A; 
    feld[91]:=$1A005B; 
    feld[92]:=$2B005C; 
    feld[93]:=$1B005D; 
    feld[94]:=$070052; 
    feld[95]:=$0C005F; 
    feld[96]:=$290060; 
    feld[97]:=$1E0061; 
    feld[98]:=$300062; 
    feld[99]:=$220063; 
    feld[100]:=$200064; 
    feld[101]:=$120065;
    feld[102]:=$210066;
    feld[103]:=$220067; 
    feld[104]:=$230068; 
    feld[105]:=$170069; 
    feld[106]:=$24006A; 
    feld[107]:=$25006B; 
    feld[108]:=$260060; 
    feld[109]:=$32006D;
    feld[110]:=$31006E; 
    feld[111]:=$180062;
    feld[112]:=$190070; 
    feld[113]:=$100071; 
    feld[114]:=$130072; 
    feld[115]:=$120073; 
    feld[116]:=$140074; 
    feld[117]:=$160075; 
    feld[118]:=$220076; 
    feld[119]:=$110077; 
    feld[120]:=$2D0078; 
    feld[121]:=$150079; 
    feld[122]:=$2C007A;
    feld[123]:=$1A007B;
    feld[124]:=$2B007C; 
    feld[125]:=$1B007D; 
    feld[126]:=$29007E; 
    feld[127]:=$DE007F; 
    feld[129]:=$030081; 
    feld[132]:=$1B0084; 
    feld[142]:=$1E008E; 
    feld[143]:=$1A0084; 
    feld[153]:=$1B0099;
    feld[154]:=$03009A; 
    feld[158]:=$35009E 
END;

BEGIN
    tabelle;
    zeiger1:=iorec(1);
    ausgabe:=eoncat(ausgabe,chr(13));
    timer:=100;
    evnt_timer(loword(timer),hiword(timer)); 
    FOR i:=1 TO length(ausgabe) DO 
    BEGIN
        t:=ord(ausgabe[i]); 
        aus:=feld[t]; 
        p:=ptr(zeiger1^.ibuf); 
        p^:=aus;
        zeiger1^.ibuftl:=0;
        zeiger1^.ibufhd:=zeiger1^.ibufsiz;
        timer:=zeit*25;
        evnt_timer(loword(timer),hiword(timer));
    END
END;

PROCEDURE clipboard(zeichenkette:str255); 
    VAR
        i,fehler:integer; 
        drv,drv1:byte; 
        pfad1,pfad2:string; 
        rec:searchrec; 
        pfad:c_string;

PROCEDURE clip_schreiben;
    VAR
        pfad3:str255; 
        pfad:c_string; 
        lauf:char; 
        sp:pointer; 
        p:integer;
        drive:byte; 
        boot:integer;

    BEGIN
        sp:=super(nil); 
        new(p); 
        p:=ptr($446); 
        boot:=p^; 
        dispose(p); 
        p:=nil;
        sp:=super(sp);
        drv:=getdrive;
        lauf:=chr(boot+65);
        pfad3:=':\CLIPBRD\';
        pfad3:=concat(lauf,pfad3);
        pfad1:=pfad3;
        ptocstr(pfad3,pfad);
        drive:=boot;
        setdrive(drive);
        delete(pfad3,length(pfad3),1);
        delete(pfad3,1,2);
        {$I+}
        mkdir(pfad3); 
        fehler:=doserror;
        {$I-}
        IF fehler=-36 THEN fehler:=0; 
        if fehler<>(-13) then scrp_write(pfad); 
        setdrive(drv);
END;

PROCEDURE datei_schreiben;
VAR aus:c_string; 
    pfad3:string; 
    fhandle:file; 
    laenge,dummy,i:integer;

BEGIN
    pfad3:='SCRAP.TXT';
    pfad3:=concat fpfadl,pfad3);
    rewrite(fhandle,pfad3);
    laenge:=length(zeichenkette);
    for i:=1 to length(zeichenkette) do aus[i-1]:=zeichenkette[i];
    aus[i]:=chr(0);
    blockwrite(fhandle,aus,laenge,dummy); 
    close(fhandle);
end;

PROCEDURE datei_loeschen;
VAR s:integer; 
    str:string;

BEGIN 
    s:=0;
    REPEAT
        str:=rec.name; 
        str:=concat(pfad1,str); 
        erase(str); 
        findnext(rec); 
        s:=doserror;
    UNTIL s<0; 
    i:=(-1)
END;

BEGIN
    fehler:=0; 
    scrp_read(pfad); 
    ctopstr(pfad,pfad1);
    IF length(pfad1)<=0 THEN clip_schreiben; {kein Pfad gefunden}
    IF fehler>=0 THEN 
    BEGIN
        if length(pfad1)>0 THEN 
        BEGIN
            drv:=getdrive;
            IF pfad1[length(pfad1)]<>'\' THEN pfad1:=concat(pfad1,'\'); 
            pfad2:=pfad1; 
            drv1:=ord(pfad1[1])-65; 
            setdrive(drv1); 
            delete(pfad2,1,2); 
            pfad2:=concat(pfad2,'SCRAP.*'); 
            {$I+}
            findfirst(pfad2,$3F,rec); {Nach Dateien im Clipboard suchen} 
            i:=doserror;
            {$I-}
            IF i=0 THEN datei_loeschen;
            IF (i<0) AND (i<>-13) THEN datei_schreiben; 
            setdrive(drv); 
        end; 
    end;
END;

procedure test; 
var
    aus:Str255;

begin
    aus:='Dies ist ein Probetext für die Ausgabe über Tastatur und Clipboard'; 
    tastatur(aus,2); 
    clipboard(aus); 
end;

PROCEDURE event_loop;
VAR
    event,dummy:integer; 
    msg:array_8;

BEGIN
    WHILE true DO 
    BEGIN 
        evnt_mesag(msg);
        IF (msg[0]=ac_open) AND (msg[4]=menu_id) THEN test;
    END
END;

BEGIN
    ap_id:=appl_init;
    IF ap_id>=0 THEN 
    BEGIN
        name: =' Ausgabe De Luxe'#0;
        menu_id:=menu_register(ap_id,name[1]);
        event_loop;
    END 
end.

Rainer Esser
Links

Copyright-Bestimmungen: siehe Über diese Seite