home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / 1989 / 09 / ldm / makeckbd.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-06-15  |  13.6 KB  |  421 lines

  1. (* ------------------------------------------------------ *)
  2. (*                   MAKECKBD.PAS                         *)
  3. (*        (c)  1989  Olaf Stoyke  &  TOOLBOX              *)
  4. (* ------------------------------------------------------ *)
  5. PROGRAM MakeCKBD;
  6.  
  7. USES Dos;
  8.  
  9. Const
  10.   CRLF        = #13#10;
  11.   NUL         = #0;
  12.   QUOTE1      = '''';
  13.   QUOTE2      = '"';
  14.   BACKSLASH   = '\';
  15.   SEMICOLON   = ';';
  16.   BUFSIZE     = 2048;      { Für bessere I/O-Performance }
  17.   CKBD1SIZE   = 166;       { Größe von CKBD1.BIN }
  18.   CKBD2SIZE   = 378;       { Größe von CKBD2.BIN }
  19.   INITOFSOFS  = 10;        { Offset von 'initofs' in CKBD1 }
  20.      { MAXOFS gibt die maximale Größe des Textspeichers an }
  21.   MAXOFS      = 65535 - CKBD2SIZE;
  22.                                { Für Dez/Hex-Umwandlungen: }
  23.   Digits      : Array[0..15] Of Char = '0123456789ABCDEF';
  24.  
  25. Type
  26.   KeyList     = ^KeyString;
  27.   KeyString   = RECORD
  28.                        { Scancode und Offset werden in die
  29.                        Scancodetabelle geschrieben }
  30.                   ScanCode,
  31.                   Offset    : WORD;
  32.                        { Wird gesetzt, wenn ein Scancode
  33.                          zum zweiten Mal gelesen wird }
  34.                   DefTwice  : BOOLEAN;
  35.                        { Der Text für die Taste: }
  36.                   KeyText   : STRING[81];
  37.                   Next      : KeyList;
  38.                 END;
  39.  
  40. VAR
  41.   Root        : KeyList;   { Liste aller Definitionen }
  42.   KeyDefs     : WORD;      { Anzahl gültiger Definitionen }
  43.   SourceName,              { Dateinamen ... }
  44.   CKBDName    : DirStr;    { 'DirStr' kommt aus 'Dos' }
  45.   IOBuffer    : ARRAY[0..BUFSIZE - 1] OF BYTE;
  46.  
  47.   PROCEDURE CKBD1; { Binärcode von CKBD1 }
  48.   {$L CKBD1.OBJ }
  49.   EXTERNAL;
  50.  
  51.   PROCEDURE CKBD2; { Binärcode von CKBD2 }
  52.   {$L CKBD2.OBJ }
  53.   EXTERNAL;
  54.  
  55.   PROCEDURE GetNames;
  56. {  Es werden die Dateinamen erfragt, wobei gleichzeitig
  57.     getestet wird, ob die Quelldatei existiert und ob der
  58.     Anwender damit einverstanden ist, daß wenn die Datei
  59.     schon existiert, CKBD.COM überschrieben wird.  }
  60.   VAR
  61.     Flag : BOOLEAN;
  62.     F    : SearchRec;
  63.  
  64.     FUNCTION Yes : BOOLEAN;
  65.   { Ja/Nein-Abfrage, Version in Neudeutsch. }
  66.     VAR
  67.       R : Registers;
  68.     BEGIN
  69.       WITH R DO BEGIN
  70.         ah := 0;
  71.         Intr($16, R);
  72.         Yes := (al = 89) Or (al = 121);
  73.       END; { With }
  74.     END; { Yes }
  75.  
  76.   BEGIN
  77.     REPEAT
  78.       WriteLn('Enter source filename:');
  79.       ReadLn(SourceName);
  80.       FindFirst(SourceName, Archive, F);
  81.     UNTIL DosError = 0;
  82.     REPEAT
  83.       WriteLn('Enter CKBD.COM path: ');
  84.       ReadLn(CKBDName);
  85.       If CKBDName[Length(CKBDName)] <> '\' THEN
  86.         CKBDName := CKBDName + '\';
  87.       CKBDName := CKBDName + 'CKBD.COM';
  88.       FindFirst(CKBDName, Archive, F);
  89.       If DosError = 0 THEN BEGIN
  90.         WriteLn('Overwrite ', CKBDName, ' ? (Y/N)');
  91.         Flag := Yes;
  92.       END { If } ELSE
  93.         Flag := True;
  94.     UNTIL Flag;
  95.     WriteLn;
  96.   END; { GetNames }
  97.  
  98.   FUNCTION HexStr(W : WORD) : STRING;
  99.   VAR
  100.     I      : WORD;
  101.     HS     : STRING[4];
  102.   BEGIN
  103.     I     := 1;
  104.     HS[0] := #4;
  105.     WHILE I < 5 DO BEGIN
  106.       HS[5 - I] := Digits[W MOD 16];
  107.       W := W SHR 4;
  108.       Inc(I);
  109.     END; { While }
  110.     HexStr := HS;
  111.   END; { HexStr }
  112.  
  113.   PROCEDURE ReadSource;
  114. { Hyper-Prozedur mit Lesen der Quelldatei und Compi-
  115.   lieren derselben. Weil Strings unter Turbo Pascal
  116.   auf maximal 255 Zeichen Länge begrenzt sind, wird
  117.   mittels 'WHILE NOT EoLn(Source) DO ...' die gesamte
  118.   Zeile ein- bzw. überlesen ohne irgendwelche internen
  119.   Grenzen zu überschreiten ... }
  120.   CONST
  121.     STRSIZE = 255;     { Maximale Stringlänge }
  122.   VAR
  123.     Source  : TEXT;    { Quelldatei }
  124.     Buffer  : STRING;  { Übertragungspuffer }
  125.     LC,                { Zeilenzähler }
  126.     Index   : WORD;    { Index für den 'Buffer' }
  127.     C       : CHAR;    { Für Datei-I/O }
  128.  
  129.     PROCEDURE Compile(VAR Line : STRING);
  130.   { Die übergebene Zeile wird compiliert, das heißt
  131.     es wird versucht, sie zu "verstehen".  }
  132.     VAR
  133.       Buffer : STRING;  { Der String kommt hier hin }
  134.       I, J,             { Quell- / Pufferindex }
  135.       V, SC  : WORD;    { V: Dummy, SC: Scancode }
  136.       S, Ch  : CHAR;
  137.  
  138.       PROCEDURE ListInsert(SCode : WORD; TData : STRING);
  139.     { Einfügen einer neuen Tastendefinition, bestehend
  140.       aus Scancode 'SCode' und Text 'TData', in die
  141.       Liste. Wenn 'SCode' schon in der Liste anzutref-
  142.       fen ist, wird der alte Eintrag als Doppeldefini-
  143.       tion vermerkt und die neue Definition ignoriert. }
  144.       VAR
  145.         HelpPtr : KeyList;
  146.       BEGIN
  147.         HelpPtr := Root;
  148.         WHILE (HelpPtr <> Nil) AND
  149.               (HelpPtr^.Scancode <> SCode) DO
  150.           HelpPtr := HelpPtr^.Next;
  151.         IF HelpPtr = Nil THEN BEGIN
  152.           New(HelpPtr);
  153.           WITH HelpPtr^ DO BEGIN
  154.             Next := Root;
  155.             Scancode := SCode;
  156.             KeyText := TData;
  157.             DefTwice := FALSE;
  158.             { Mit der nächsten Zuweisung wird das Offset
  159.               auf einen Wert gesetzt der sich aus der
  160.               Länge des PSP und der Größe von CKBD1.BIN
  161.               ergibt und sich auf die ausführbare Datei
  162.               CKBD.COM bezieht: }
  163.             Offset := 256 + CKBD1SIZE;
  164.           END; { With }
  165.           Root := HelpPtr;
  166.           Inc(KeyDefs);
  167.         END { If } ELSE
  168.           HelpPtr^.DefTwice := TRUE;
  169.       END; { ListInsert }
  170.  
  171.       FUNCTION CharsLeft : BOOLEAN;
  172.       {  Sucht von der aktuellen Position innerhalb
  173.          der Zeile an ein Zeichen, daß kein Blank oder
  174.          Steuerzeichen ist und gibt 'True' aus, wenn
  175.          ein solches Zeichen existiert.  }
  176.       BEGIN
  177.         WHILE (I <= Length(Line)) AND (Line[I] < '!') DO
  178.           Inc(I);
  179.         CharsLeft := I <= Length(Line);
  180.       END; { CharsLeft }
  181.  
  182.       PROCEDURE Message(LC, PC : WORD; Msg : STRING);
  183.       BEGIN
  184.         WriteLn('Error at line ',
  185.                              LC, ', pos ', PC, ': ', Msg);
  186.       END; { Message }
  187.  
  188.     BEGIN
  189.       I := 1;
  190.       IF NOT CharsLeft THEN Exit;
  191.       IF Line[I] <> SEMICOLON THEN BEGIN
  192.         SC := 0;
  193.         FOR J := 0 TO 3 DO BEGIN
  194.           V := Pos(UpCase(Line[I + J]), Digits);
  195.           IF V = 0 THEN BEGIN
  196.             Message(LC, I + J, 'Error in base 16 const.');
  197.             Exit;
  198.           END; { If }
  199.           SC := V - 1 + SC SHL 4;
  200.         END; { For }
  201.         I := I + J + 1;
  202.         IF NOT CharsLeft THEN
  203.           Message(LC, I, 'Unexpected end of line.')
  204.         ELSE BEGIN
  205.           S := Line[I];
  206.           IF (S = QUOTE1) OR (S = QUOTE2) THEN BEGIN
  207.             J := 1; { Für 1. Zeichen im Puffer }
  208.             Inc(I);
  209.             WHILE (I <= Length(Line)) AND
  210.                   (Line[I] <> S) DO BEGIN
  211.               Ch := Line[I];
  212.               { Falls \, Rest analysieren. Zahlen-
  213.                 konstante als ASCII-Wert interpretie-
  214.                 ren, sonst normales Zeichen ... }
  215.               IF Ch = BACKSLASH THEN BEGIN
  216.                 Inc(I);
  217.                 IF ('/' < Line[I]) AND
  218.                    (Line[I] < ':') THEN BEGIN
  219.                   V := 0; { Puffer für ASCII-Code }
  220.                   REPEAT
  221.                     V := V * 10 + Ord(Line[I]) - 48;
  222.                     Inc(I);
  223.                   UNTIL (Line[I] < '0') Or ('9' < Line[I]);
  224.                   { Da unten steht ein Inc(I), also kommt
  225.                     hier ein Dec(I) hin ... }
  226.                   Dec(I);
  227.                   If V > 255 THEN BEGIN
  228.                     Message(LC, I, 'Invalid ascii const.');
  229.                     Exit;
  230.                   END { If } ELSE
  231.                     Ch := Chr(V);
  232.                 END { If } ELSE
  233.                   Ch := Line[I];
  234.               END; { If }
  235.               IF Ch = NUL THEN BEGIN
  236.                 Message(LC, I, 'Invalid NUL in string.');
  237.                 Exit;
  238.               END ELSE
  239.                 Buffer[J] := Ch;
  240.               Inc(I); { Nächstes Zeichen Quellzeile }
  241.               Inc(J); { Nächstes Zeichen Zielpuffer }
  242.             END; { While }
  243.             { J ist Anzahl gelesener Zeichen plus 1 }
  244.             Buffer[J] := NUL;
  245.             Buffer[0] := Chr(J);
  246.             IF I > Length(Line) THEN BEGIN
  247.               Message(LC, I, 'Unexpected end of line.');
  248.               Exit;
  249.             END; { If }
  250.             IF J = 1 THEN BEGIN
  251.               Message(LC, J, 'Invalid string.');
  252.               Exit;
  253.             END; { If }
  254.             Inc(I);
  255.             IF CharsLeft THEN
  256.               Message(LC, I, 'Extra chars on line.')
  257.             ELSE
  258.               ListInsert(SC, Buffer);
  259.           END { If } ELSE
  260.             Message(LC, I, '" or '' expected.');
  261.         END; { Else }
  262.       END; { If }
  263.     END; { Compile }
  264.  
  265.     PROCEDURE TestDefTwice;
  266.   { Durchsucht die Liste der Tastendfinitionen und
  267.     meldet sich, wenn eine Definition mit dem ge-
  268.     setzten Flag für eine Doppeldefinition gefunden
  269.     wurde. }
  270.     VAR
  271.       Walker : KeyList;
  272.     BEGIN
  273.       Walker := Root;
  274.       IF Walker = Nil THEN
  275.         WriteLn('No lines compiled.')
  276.       ELSE
  277.         REPEAT
  278.           WITH Walker^ DO BEGIN
  279.             IF DefTwice THEN
  280.               WriteLn(HexStr(Scancode), ' read twice.');
  281.             Walker := Next;
  282.           END; { With }
  283.         UNTIL Walker = Nil;
  284.     END; { TestDefTwice }
  285.  
  286.   BEGIN
  287.     {$I-}
  288.     Assign(Source, SourceName);
  289.     Reset(Source);
  290.     {$I+}
  291.     IF IOResult <> 0 THEN BEGIN
  292.       WriteLn(CRLF, 'Cannot open ', SourceName, '.');
  293.       Halt(1);
  294.     END { If } ELSE
  295.       SetTextBuf(Source, IOBuffer, BUFSIZE);
  296.     LC := 1;
  297.     WriteLn('Reading . . .');
  298.     WHILE NOT EoF(Source) DO BEGIN
  299.       Index := 1;
  300.       WHILE NOT EoLn(Source) DO BEGIN { Bis zum Zeilen- }
  301.         Read(Source, C);              { ende Zeichen }
  302.         IF Index <= STRSIZE THEN      { einlesen ... }
  303.           Buffer[Index] := C;
  304.         Inc(Index);
  305.       END; { While }
  306.       { 'Read(Source, C, C)' überliest das EoLn-Merkmal,
  307.         in der Regel ein CR und ein LF. (oder LF/CR ?) }
  308.       Read(Source, C, C);
  309.       IF Index > 1 THEN BEGIN
  310.         Buffer[0] := Chr(Index - 1); { Länge setzen }
  311.         Compile(Buffer);
  312.       END; { If }
  313.       Inc(LC);
  314.     END; { While }
  315.     Close(Source);
  316.     TestDefTwice;
  317.   END; { ReadSource }
  318.  
  319.   PROCEDURE MakeCode;
  320. { 'MakeCode' generiert das ready-to-run CKBD.COM
  321.    mittels der Tastendefinitionen in der Liste. }
  322.   CONST
  323.     { 'TabEnd' wird ans Ende der Scancodetabelle ange-
  324.       hängt, um CKBD mitzuteilen, wo die Tabelle endet. }
  325.     TabEnd    : LongInt = $FFFFFFFF;
  326.   VAR
  327.     Walker    : KeyList;
  328.     CKBD      : File; { Zieldatei }
  329.     TDLength  : Word; { Offset der Textdaten }
  330.  
  331.     PROCEDURE GenOffsets;
  332.     BEGIN
  333.       WriteLn(KeyDefs, ' line(s) compiled.');
  334.       TDLength := 4 + KeyDefs * 4;
  335.       Walker := Root;
  336.       WHILE Walker <> Nil DO
  337.         WITH Walker^ DO BEGIN
  338.           { siehe auch 'ListInsert' ... }
  339.           Inc(Offset, TDLength);
  340.           Inc(TDLength, Length(KeyText));
  341.           IF TDLength > MAXOFS THEN BEGIN
  342.             { Kein Platz mehr für CKBD2 }
  343.             WriteLn('Not enough memory for text data.');
  344.             Halt(1);
  345.           END; { If }
  346.           Walker := Next;
  347.         END; { With }
  348.       WriteLn(HexStr(TDLength), ' bytes text data.');
  349.       WriteLn;
  350.     END; { GenOffsets }
  351.  
  352.     PROCEDURE GenCode(VAR C; CodeL : WORD);
  353.     { Generelle Ausgabeprozedur mit zentraler Fehler-
  354.       behandlung. }
  355.       VAR
  356.         Result : WORD;
  357.       BEGIN
  358.         BlockWrite(CKBD, C, CodeL, Result);
  359.         IF Result < CodeL THEN BEGIN
  360.           WriteLn(CRLF, CKBDName, ': Write error.');
  361.           Halt(1);
  362.         END; { If }
  363.       END; { GenCode }
  364.  
  365.   BEGIN
  366.     {$I-}
  367.     Assign(CKBD, CKBDName);
  368.     Rewrite(CKBD, 1);
  369.     {$I+}
  370.     IF IOResult > 0 THEN BEGIN
  371.       WriteLn(CRLF, 'Cannot open ', CKBDName, '.');
  372.       Halt(1);
  373.     END; { If }
  374.     GenOffsets;
  375.     WriteLn('Writing . . .');
  376.     { Die folgende, kryptische Zuweisung setzt die Varia-
  377.       ble 'initofs' in CKBD1.BIN auf das Startoffset der
  378.       Initialisierungsroutine in CKBD2.BIN. Dieser be-
  379.       rechnet sich aus Länge des PSP (256) plus Länge von
  380.       CKBD1.BIN plus Länge der Textdaten ... }
  381.     MemW[Seg(CKBD1):Ofs(CKBD1) + INITOFSOFS] :=
  382.                                256 + CKBD1SIZE + TDLength;
  383.     GenCode(Addr(CKBD1)^, CKBD1SIZE);
  384.     Walker := Root;
  385.     WHILE Walker <> Nil DO
  386.       WITH Walker^ DO BEGIN   { Schreibe Scancodetabelle }
  387.         GenCode(ScanCode, 2); { 1) Den Scancode selbst }
  388.         GenCode(Offset, 2);   { 2) Das Offset dazu }
  389.         Walker := Next;
  390.       END; { With }
  391.     GenCode(TabEnd, 4); { Anhängen des Ende-Merkmals }
  392.     Walker := Root;
  393.     WHILE Walker <> Nil DO
  394.       WITH Walker^ DO BEGIN    { Ausgabe des Texts: }
  395.         GenCode(KeyText[1], Length(KeyText));
  396.         Walker := Next;
  397.       END; { With }
  398.     GenCode(Addr(CKBD2)^, CKBD2SIZE);
  399.     Close(CKBD);
  400.     WriteLn(HexStr(CKBD1SIZE + CKBD2SIZE + TDLength),
  401.                                        ' bytes written.');
  402.     WriteLn;
  403.   END; { MakeCode }
  404.  
  405. BEGIN
  406.   WriteLn(CRLF, 'MAKECKBD Version 1.30', CRLF,
  407.           'Copyright (c) 1989 by Olaf Stoyke & TOOLBOX',
  408.           CRLF);
  409.   KeyDefs := 0;   { Noch keine gültigen Zeilen gelesen }
  410.   Root := Nil;    { ... deshalb auch eine leere Liste }
  411.   GetNames;       { Dateinamen einlesen und testen }
  412.   ReadSource;     { Quelle lesen und compilieren }
  413.   IF Root <> Nil THEN { Wenn gültige Zeilen existieren }
  414.     MakeCode      { Zieldatei ausgeben: CKBD.COM }
  415.   ELSE
  416.     WriteLn;
  417.   WriteLn('Done.'); { Stimmt ! }
  418. END.
  419. (* ------------------------------------------------------ *)
  420. (*                Ende von MAKECKBD.PAS                   *)
  421.