Browse Source

CHAR16 used

Arthur Yefimov 3 years ago
parent
commit
0b1bd7ab19
17 changed files with 961 additions and 131 deletions
  1. 1 1
      data/bin/compile.bat
  2. 1 1
      data/bin/compile.sh
  3. 14 7
      src/Dir.Mod
  4. 13 14
      src/Editor.Mod
  5. 16 14
      src/EditorText.Mod
  6. 57 40
      src/FreeOberon.Mod
  7. 20 33
      src/Graph.Mod
  8. 190 0
      src/In.Mod
  9. 0 2
      src/Int.Mod
  10. 9 10
      src/OV.Mod
  11. 319 0
      src/Out.Mod
  12. 1 1
      src/SDL2.Mod
  13. 1 1
      src/StrList.Mod
  14. 228 0
      src/Strings.Mod
  15. 2 3
      src/Terminal.Mod
  16. 73 0
      src/Utf8.Mod
  17. 16 4
      src/make.sh

+ 1 - 1
data/bin/compile.bat

@@ -9,7 +9,7 @@ SET CURDIR=%~dp0
 SET OFRDIR=%CURDIR%OfrontPlus\Target\Win64
 SET OFRDIR=%CURDIR%OfrontPlus\Target\Win64
 SET PATH=%OFRDIR%;%CURDIR%mingw32\bin;%PATH%
 SET PATH=%OFRDIR%;%CURDIR%mingw32\bin;%PATH%
 SET OBERON=.;%CURDIR%..\..\src;%OFRDIR%\Lib\Sym
 SET OBERON=.;%CURDIR%..\..\src;%OFRDIR%\Lib\Sym
-SET OFR=ofront+ -s -88 -7
+SET OFR=ofront+ -s -88 -7w
 
 
 ECHO ON
 ECHO ON
 @%OFR% %2 ..\Programs\%1
 @%OFR% %2 ..\Programs\%1

+ 1 - 1
data/bin/compile.sh

@@ -9,7 +9,7 @@ cd bin
 OFRDIR="../data/bin/OfrontPlus/Target/Linux_amd64"
 OFRDIR="../data/bin/OfrontPlus/Target/Linux_amd64"
 PATH="$OFRDIR:$PATH"
 PATH="$OFRDIR:$PATH"
 export OBERON=".:$PWD/../src:$OFRDIR/Lib/Sym"
 export OBERON=".:$PWD/../src:$OFRDIR/Lib/Sym"
-OFR="ofront+ -s -88 -7"
+OFR="ofront+ -s -88 -7w"
 
 
 
 
 $OFR $2 ../Programs/$1
 $OFR $2 ../Programs/$1

+ 14 - 7
src/Dir.Mod

@@ -1,9 +1,10 @@
 MODULE Dir; (*This is Oberon-07 style file*)
 MODULE Dir; (*This is Oberon-07 style file*)
-IMPORT C := arC, arCString, SYSTEM, Platform, Out;
+IMPORT C := arC, arCString, SYSTEM, Platform, Out, Utf8;
 
 
 TYPE
 TYPE
   DIR = SYSTEM.ADRINT;
   DIR = SYSTEM.ADRINT;
   DIRENT = SYSTEM.ADRINT;
   DIRENT = SYSTEM.ADRINT;
+  SHORTCHAR = SYSTEM.CHAR8;
 
 
   Rec* = RECORD
   Rec* = RECORD
     eod*: BOOLEAN; (* End of directory *)
     eod*: BOOLEAN; (* End of directory *)
@@ -17,7 +18,7 @@ TYPE
 
 
 PROCEDURE -AAincludeDirent "#include <dirent.h>";
 PROCEDURE -AAincludeDirent "#include <dirent.h>";
 
 
-PROCEDURE -opendir(name: ARRAY OF CHAR): DIR "(SYSTEM_ADRINT) opendir((const char *)name)";
+PROCEDURE -opendir(name: ARRAY OF SHORTCHAR): DIR "(SYSTEM_ADRINT) opendir((const char *)name)";
 PROCEDURE -closedir(dir: DIR) "closedir((DIR*) dir)";
 PROCEDURE -closedir(dir: DIR) "closedir((DIR*) dir)";
 PROCEDURE -readdir(dir: DIR): DIRENT "(SYSTEM_ADRINT) readdir((DIR*) dir)";
 PROCEDURE -readdir(dir: DIR): DIRENT "(SYSTEM_ADRINT) readdir((DIR*) dir)";
 PROCEDURE -rewinddir(dir: DIR) "rewinddir((DIR*) dir)";
 PROCEDURE -rewinddir(dir: DIR) "rewinddir((DIR*) dir)";
@@ -37,12 +38,14 @@ END Close;
 PROCEDURE Next*(VAR r: Rec);
 PROCEDURE Next*(VAR r: Rec);
 VAR ent: DIRENT;
 VAR ent: DIRENT;
   i, j: INTEGER;
   i, j: INTEGER;
+  s: ARRAY 2048 OF SYSTEM.CHAR8;
 BEGIN
 BEGIN
   IF r.dir # 0 THEN
   IF r.dir # 0 THEN
     r.res := 0;
     r.res := 0;
     ent := readdir(r.dir);
     ent := readdir(r.dir);
     IF ent # 0 THEN
     IF ent # 0 THEN
-      arCString.CopyToArray(getdirname(ent), r.name);
+      arCString.CopyToArray(getdirname(ent), s);
+      Utf8.Decode(s, r.name);
 
 
       (* To set the value of r.isDir, we (maybe) append '/' and name to path,
       (* To set the value of r.isDir, we (maybe) append '/' and name to path,
          pass it to DirExists and then restore the original value of path. *)
          pass it to DirExists and then restore the original value of path. *)
@@ -56,7 +59,8 @@ BEGIN
       END;
       END;
       IF j < LEN(r.path) - 1 THEN
       IF j < LEN(r.path) - 1 THEN
         r.path[j] := 0X;
         r.path[j] := 0X;
-        r.isDir := Platform.DirExists(r.path)
+        Utf8.Encode(r.path, s);
+        r.isDir := Platform.DirExists(s)
       ELSE r.isDir := FALSE; r.res := 3; r.eod := TRUE
       ELSE r.isDir := FALSE; r.res := 3; r.eod := TRUE
       END;
       END;
       r.path[r.pathlen] := 0X
       r.path[r.pathlen] := 0X
@@ -69,11 +73,12 @@ END Next;
 
 
 PROCEDURE First*(VAR r: Rec; path: ARRAY OF CHAR);
 PROCEDURE First*(VAR r: Rec; path: ARRAY OF CHAR);
 VAR i: INTEGER;
 VAR i: INTEGER;
+  s: ARRAY 2048 OF SHORTCHAR;
 BEGIN r.isDir := FALSE; r.name[0] := 0X;
 BEGIN r.isDir := FALSE; r.name[0] := 0X;
   i := 0; WHILE path[i] # 0X DO INC(i) END;
   i := 0; WHILE path[i] # 0X DO INC(i) END;
   IF i < LEN(r.path) - 13 THEN
   IF i < LEN(r.path) - 13 THEN
-    IF path = '' THEN r.dir := opendir('.'); r.path := './'; r.pathlen := 2
-    ELSE r.dir := opendir(path); r.path := path; r.pathlen := i
+    IF path[0] = 0X THEN r.dir := opendir('.'); r.path := './'; r.pathlen := 2
+    ELSE Utf8.Encode(path, s); r.dir := opendir(s); r.path := path; r.pathlen := i
     END;
     END;
     IF r.dir # 0 THEN r.eod := FALSE; Next(r)
     IF r.dir # 0 THEN r.eod := FALSE; Next(r)
     ELSE r.res := 1; r.eod := TRUE; r.path[0] := 0X
     ELSE r.res := 1; r.eod := TRUE; r.path[0] := 0X
@@ -90,7 +95,9 @@ BEGIN
 END Rewind;
 END Rewind;
 
 
 PROCEDURE IsDir*(name: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE IsDir*(name: ARRAY OF CHAR): BOOLEAN;
-RETURN (name = '') OR Platform.DirExists(name) END IsDir;
+VAR s: ARRAY 2048 OF SYSTEM.CHAR8;
+BEGIN Utf8.Encode(name, s)
+RETURN (name[0] = 0X) OR Platform.DirExists(s) END IsDir;
 
 
 PROCEDURE GetCwd*(VAR dir: ARRAY OF CHAR);
 PROCEDURE GetCwd*(VAR dir: ARRAY OF CHAR);
 VAR i: INTEGER;
 VAR i: INTEGER;

+ 13 - 14
src/Editor.Mod

@@ -47,8 +47,6 @@ CONST
   save* = 2;
   save* = 2;
 
 
 TYPE
 TYPE
-  CHAR = SHORTCHAR;
-
   FileDialog* = POINTER TO FileDialogDesc;
   FileDialog* = POINTER TO FileDialogDesc;
   FileDialogDesc* = RECORD(OV.WindowDesc)
   FileDialogDesc* = RECORD(OV.WindowDesc)
     type*: INTEGER; (* open or save *)
     type*: INTEGER; (* open or save *)
@@ -88,27 +86,28 @@ PROCEDURE FileDialogUpdateFileList*(c: FileDialog);
 VAR L: StrList.List;
 VAR L: StrList.List;
   r: Dir.Rec;
   r: Dir.Rec;
   s: ARRAY 512 OF CHAR;
   s: ARRAY 512 OF CHAR;
-  first: BOOLEAN;
   n: INTEGER;
   n: INTEGER;
 BEGIN L := c.colFiles.items;
 BEGIN L := c.colFiles.items;
   StrList.Clear(L);
   StrList.Clear(L);
-  Dir.First(r, c.path); first := TRUE;
+  Dir.First(r, c.path);
   WHILE ~r.eod DO
   WHILE ~r.eod DO
     IF r.name # '.' THEN s := r.name$;
     IF r.name # '.' THEN s := r.name$;
       IF r.isDir THEN
       IF r.isDir THEN
-        IF first THEN n := Strings.Length(s);
-          s[n] := '/'; s[n + 1] := 0X;
-          OV.EditSetCaption(c.edtFilename, s);
-          s[n] := ']'; Strings.Insert('[', 0, s)
-        ELSE Strings.Insert('[', 0, s); Strings.Append(']', s)
-        END
-      ELSIF first THEN OV.EditSetCaption(c.edtFilename, s)
+        Strings.Insert('[', 0, s); Strings.Append(']', s)
       END;
       END;
-      StrList.Append(L, s); first := FALSE
+      StrList.Append(L, s)
     END;
     END;
     Dir.Next(r)
     Dir.Next(r)
   END;
   END;
   StrList.Sort(L, FileNamesCmp);
   StrList.Sort(L, FileNamesCmp);
+
+  StrList.First(L, s);
+  IF L.eol THEN s := ''
+  ELSIF s[0] = '[' THEN Strings.Delete(s, 0, 1);
+    s[Strings.Length(s) - 1] := '/'
+  END;
+  OV.EditSetCaption(c.edtFilename, s);
+
   OV.ColumnListSetCur(c.colFiles, 0);
   OV.ColumnListSetCur(c.colFiles, 0);
   OV.NeedRedraw(c.app)
   OV.NeedRedraw(c.app)
 END FileDialogUpdateFileList;
 END FileDialogUpdateFileList;
@@ -961,9 +960,9 @@ BEGIN
   OV.WindowMouseMove(c, x, y, buttons)
   OV.WindowMouseMove(c, x, y, buttons)
 END EditorMouseMove;
 END EditorMouseMove;
 
 
-PROCEDURE EditorTextInput(c: OV.Control; ch: INTEGER);
+PROCEDURE EditorTextInput(c: OV.Control; ch: CHAR);
 BEGIN
 BEGIN
-  IF ch # 0 THEN c(Editor).text.InsertChar(OV.ToCP866(ch)); T.ResetCursorBlink;
+  IF ch # 0X THEN c(Editor).text.InsertChar(OV.ToCP866(ch)); T.ResetCursorBlink;
     c(Editor).text.selected := FALSE; PrintText(c(Editor))
     c(Editor).text.selected := FALSE; PrintText(c(Editor))
   END
   END
 END EditorTextInput;
 END EditorTextInput;

+ 16 - 14
src/EditorText.Mod

@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 You should have received a copy of the GNU General Public License
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
 *)
-IMPORT Config, Files, Strings, Out;
+IMPORT Config, Files, Strings, Out, Utf8;
 
 
 CONST
 CONST
   lineLen = 256;
   lineLen = 256;
@@ -29,8 +29,6 @@ CONST
   punctuation* = 4;
   punctuation* = 4;
 
 
 TYPE
 TYPE
-  CHAR = SHORTCHAR;
-
   String* = ARRAY lineLen OF CHAR;
   String* = ARRAY lineLen OF CHAR;
 
 
   Line* = POINTER TO LineDesc;
   Line* = POINTER TO LineDesc;
@@ -329,15 +327,15 @@ BEGIN
   i := 0; s[LEN(s) - 1] := 0X;
   i := 0; s[LEN(s) - 1] := 0X;
   WHILE s[i] # 0X DO
   WHILE s[i] # 0X DO
     ch := s[i];
     ch := s[i];
-    IF ORD(ch) < 128 THEN Files.WriteChar(r, ch) (* ASCII *)
+    IF ORD(ch) < 128 THEN Files.WriteChar(r, SHORT(ch)) (* ASCII *)
     ELSIF ORD(ch) = 240 THEN (* Big cyrillic Yo *)
     ELSIF ORD(ch) = 240 THEN (* Big cyrillic Yo *)
       Files.WriteChar(r, 0D0X); Files.WriteChar(r, 81X)
       Files.WriteChar(r, 0D0X); Files.WriteChar(r, 81X)
     ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
     ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
       Files.WriteChar(r, 0D0X);
       Files.WriteChar(r, 0D0X);
-      Files.WriteChar(r, CHR(ORD(ch) - 128 + 090H))
+      Files.WriteChar(r, SHORT(CHR(ORD(ch) - 128 + 090H)))
     ELSE
     ELSE
       Files.WriteChar(r, 0D1X);
       Files.WriteChar(r, 0D1X);
-      Files.WriteChar(r, CHR(ORD(ch) - 224 + 080H))
+      Files.WriteChar(r, SHORT(CHR(ORD(ch) - 224 + 080H)))
     END;
     END;
     INC(i)
     INC(i)
   END
   END
@@ -364,9 +362,11 @@ PROCEDURE (t: Text) SaveToFile*(fname: ARRAY OF CHAR): BOOLEAN, NEW;
 VAR f: Files.File; r: Files.Rider;
 VAR f: Files.File; r: Files.Rider;
   success: BOOLEAN;
   success: BOOLEAN;
   L: Line;
   L: Line;
+  s: ARRAY 2048 OF SHORTCHAR;
 BEGIN success := FALSE;
 BEGIN success := FALSE;
   FixFname(fname);
   FixFname(fname);
-  f := Files.New(fname);
+  Utf8.Encode(fname, s);
+  f := Files.New(s);
   IF f # NIL THEN (*!FIXME error checking is wrong *)
   IF f # NIL THEN (*!FIXME error checking is wrong *)
     Files.Set(r, f, 0);
     Files.Set(r, f, 0);
     L := t.first;
     L := t.first;
@@ -388,26 +388,26 @@ BEGIN success := FALSE;
 RETURN success END SaveToFile;
 RETURN success END SaveToFile;
 
 
 PROCEDURE ReadLine(VAR r: Files.Rider; L: Line);
 PROCEDURE ReadLine(VAR r: Files.Rider; L: Line);
-VAR ch: CHAR; i: INTEGER;
+VAR x: SHORTCHAR; ch: CHAR; i: INTEGER;
 BEGIN i := 0;
 BEGIN i := 0;
-  Files.ReadChar(r, ch);
+  Files.ReadChar(r, x); ch := LONG(x);
   WHILE ~r.eof & (i < LEN(L.s) - 1) & (ch # 0AX) & (ch # 0DX) DO
   WHILE ~r.eof & (i < LEN(L.s) - 1) & (ch # 0AX) & (ch # 0DX) DO
     IF ORD(ch) < 128 THEN L.s[i] := ch
     IF ORD(ch) < 128 THEN L.s[i] := ch
     ELSIF ch = 0D0X THEN
     ELSIF ch = 0D0X THEN
-      Files.ReadChar(r, ch);
+      Files.ReadChar(r, x); ch := LONG(x);
       IF ch = 081X THEN L.s[i] := CHR(240) (* Big Yo *)
       IF ch = 081X THEN L.s[i] := CHR(240) (* Big Yo *)
       ELSE L.s[i] := CHR(ORD(ch) - 16)
       ELSE L.s[i] := CHR(ORD(ch) - 16)
       END
       END
     ELSIF ch = 0D1X THEN
     ELSIF ch = 0D1X THEN
-      Files.ReadChar(r, ch); L.s[i] := CHR(ORD(ch) + 96)
+      Files.ReadChar(r, x); ch := LONG(x); L.s[i] := CHR(ORD(ch) + 96)
     END;
     END;
-    Files.ReadChar(r, ch);
+    Files.ReadChar(r, x); ch := LONG(x);
     INC(i)
     INC(i)
   END;
   END;
   L.s[i] := 0X;
   L.s[i] := 0X;
   IF ch = 0DX THEN
   IF ch = 0DX THEN
     L.lineEndLen := 2; (* Assume CRLF (DOS) *)
     L.lineEndLen := 2; (* Assume CRLF (DOS) *)
-    Files.ReadChar(r, ch) (* Skip LF *)
+    Files.ReadChar(r, x) (* Skip LF *)
   ELSE L.lineEndLen := 1 (* Assume LF (UNIX) *)
   ELSE L.lineEndLen := 1 (* Assume LF (UNIX) *)
   END
   END
 END ReadLine;
 END ReadLine;
@@ -416,10 +416,12 @@ PROCEDURE (t: Text) LoadFromFile*(fname: ARRAY OF CHAR): BOOLEAN, NEW;
 VAR f: Files.File; r: Files.Rider;
 VAR f: Files.File; r: Files.Rider;
   success: BOOLEAN;
   success: BOOLEAN;
   L: Line;
   L: Line;
+  s: ARRAY 2048 OF SHORTCHAR;
 BEGIN success := FALSE;
 BEGIN success := FALSE;
   IF fname[0] # 0X THEN
   IF fname[0] # 0X THEN
     FixFname(fname);
     FixFname(fname);
-    f := Files.Old(fname);
+    Utf8.Encode(fname, s);
+    f := Files.Old(s);
     IF f # NIL THEN
     IF f # NIL THEN
       Files.Set(r, f, 0);
       Files.Set(r, f, 0);
       L := NewLine();
       L := NewLine();

+ 57 - 40
src/FreeOberon.Mod

@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 You should have received a copy of the GNU General Public License
 along with Free Oberon.  If not, see <http://www.gnu.org/licenses/>.
 along with Free Oberon.  If not, see <http://www.gnu.org/licenses/>.
 *)
 *)
-IMPORT G := Graph, T := Terminal, Files, Args,
+IMPORT G := Graph, T := Terminal, Files, Args, Utf8,
        OV, Editor, Term, Config, Strings, Int, Out;
        OV, Editor, Term, Config, Strings, Int, Out;
 CONST
 CONST
   version* = '1.0.4';
   version* = '1.0.4';
@@ -47,8 +47,6 @@ CONST
   tokenComment = 4;
   tokenComment = 4;
 
 
 TYPE
 TYPE
-  CHAR = SHORTCHAR;
-
   StrList = POINTER TO StrListDesc;
   StrList = POINTER TO StrListDesc;
   StrListDesc = RECORD
   StrListDesc = RECORD
     s: ARRAY 256 OF CHAR; (* Module name *)
     s: ARRAY 256 OF CHAR; (* Module name *)
@@ -58,8 +56,8 @@ TYPE
   Fnames = ARRAY 32, 256 OF CHAR;
   Fnames = ARRAY 32, 256 OF CHAR;
 
 
 VAR
 VAR
-  progBuf: ARRAY 16300 OF CHAR; (* For interacting with a launched program *)
-  inputBuf: ARRAY 16300 OF CHAR; (* Saves entered chars before Enter pressed *)
+  progBuf: ARRAY 16300 OF SHORTCHAR; (* For interacting with a launched program *)
+  inputBuf: ARRAY 16300 OF SHORTCHAR; (* Saves entered chars before Enter pressed *)
   inputBufLen: INTEGER;
   inputBufLen: INTEGER;
   programFinished: BOOLEAN;
   programFinished: BOOLEAN;
   tempWindowed: BOOLEAN; (* TRUE if editor windowed while program is running *)
   tempWindowed: BOOLEAN; (* TRUE if editor windowed while program is running *)
@@ -302,8 +300,8 @@ BEGIN
   END
   END
 END PollProgram;
 END PollProgram;
 
 
-PROCEDURE WriteToProcess(s: ARRAY OF CHAR; len: INTEGER);
-VAR buf: ARRAY 2048 OF CHAR; i, bufLen: INTEGER; ch: CHAR;
+PROCEDURE WriteToProcess(s: ARRAY OF SHORTCHAR; len: INTEGER);
+VAR buf: ARRAY 2048 OF SHORTCHAR; i, bufLen: INTEGER; ch: SHORTCHAR;
 BEGIN
 BEGIN
   bufLen := 0; i := 0;
   bufLen := 0; i := 0;
   WHILE i < len DO
   WHILE i < len DO
@@ -315,11 +313,11 @@ BEGIN
       INC(bufLen, 2)
       INC(bufLen, 2)
     ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
     ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
       buf[bufLen] := 0D0X;
       buf[bufLen] := 0D0X;
-      buf[bufLen + 1] := CHR(ORD(ch) - 128 + 090H);
+      buf[bufLen + 1] := SHORT(CHR(ORD(ch) - 128 + 090H));
       INC(bufLen, 2)
       INC(bufLen, 2)
     ELSE
     ELSE
       buf[bufLen] := 0D1X;
       buf[bufLen] := 0D1X;
-      buf[bufLen + 1] := CHR(ORD(ch) - 224 + 080H);
+      buf[bufLen + 1] := SHORT(CHR(ORD(ch) - 224 + 080H));
       INC(bufLen, 2)
       INC(bufLen, 2)
     END;
     END;
     INC(i)
     INC(i)
@@ -328,7 +326,7 @@ BEGIN
 END WriteToProcess;
 END WriteToProcess;
 
 
 PROCEDURE HandleTerminalKeyDown(key: G.Key; VAR quit: BOOLEAN);
 PROCEDURE HandleTerminalKeyDown(key: G.Key; VAR quit: BOOLEAN);
-VAR code: INTEGER; ch: CHAR; buf: ARRAY 2 OF CHAR;
+VAR code: INTEGER; ch: CHAR; buf: ARRAY 2 OF SHORTCHAR;
 BEGIN
 BEGIN
   IF programFinished THEN
   IF programFinished THEN
     IF (key.code = G.kEnter) & (key.mod * G.mAlt # {}) THEN
     IF (key.code = G.kEnter) & (key.mod * G.mAlt # {}) THEN
@@ -341,7 +339,7 @@ BEGIN
       IF key.mod * G.mAlt # {} THEN T.ToggleFullscreen
       IF key.mod * G.mAlt # {} THEN T.ToggleFullscreen
       ELSE T.Ln;
       ELSE T.Ln;
         WriteToProcess(inputBuf, inputBufLen);
         WriteToProcess(inputBuf, inputBufLen);
-        inputBufLen := 0; buf[0] := 0AX;
+        inputBufLen := 0; buf[0] := SHORT(CHR(0AH));
         Term.WriteToProcess(buf, 1)
         Term.WriteToProcess(buf, 1)
       END
       END
     | G.kBackspace:
     | G.kBackspace:
@@ -358,10 +356,10 @@ BEGIN
   END
   END
 END HandleTerminalKeyDown;
 END HandleTerminalKeyDown;
 
 
-PROCEDURE HandleTerminalTextInput(s: ARRAY OF CHAR; ch: INTEGER);
+PROCEDURE HandleTerminalTextInput(s: ARRAY OF CHAR; ch: CHAR);
 BEGIN
 BEGIN
-  IF (ch # 0) & (inputBufLen < LEN(inputBuf)) THEN
-    inputBuf[inputBufLen] := OV.ToCP866(ch); INC(inputBufLen);
+  IF (ch # 0X) & (inputBufLen < LEN(inputBuf)) THEN
+    inputBuf[inputBufLen] := SHORT(OV.ToCP866(ch)); INC(inputBufLen);
     T.Write(OV.ToCP866(ch))
     T.Write(OV.ToCP866(ch))
   END
   END
 END HandleTerminalTextInput;
 END HandleTerminalTextInput;
@@ -395,7 +393,9 @@ RETURN p # NIL END IsSysModule;
 PROCEDURE ModuleExists(IN fname: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE ModuleExists(IN fname: ARRAY OF CHAR): BOOLEAN;
 VAR F: Files.File;
 VAR F: Files.File;
   exists: BOOLEAN;
   exists: BOOLEAN;
-BEGIN F := Files.Old(fname); exists := F # NIL;
+  s: ARRAY 2048 OF SHORTCHAR;
+BEGIN Utf8.Encode(fname, s);
+  F := Files.Old(s); exists := F # NIL;
   IF F # NIL THEN Files.Close(F) END ;
   IF F # NIL THEN Files.Close(F) END ;
 RETURN exists END ModuleExists;
 RETURN exists END ModuleExists;
 
 
@@ -438,11 +438,13 @@ RETURN ok END FindModule;
 PROCEDURE RunCommand(IN fname, mod: ARRAY OF CHAR;
 PROCEDURE RunCommand(IN fname, mod: ARRAY OF CHAR;
     link, graph, main: BOOLEAN; list: StrList): BOOLEAN;
     link, graph, main: BOOLEAN; list: StrList): BOOLEAN;
 CONST bufLen = 20480;
 CONST bufLen = 20480;
-VAR buf: ARRAY bufLen OF CHAR;
+VAR buf: ARRAY bufLen OF SHORTCHAR;
   e: Editor.Editor;
   e: Editor.Editor;
   p: StrList;
   p: StrList;
   len, err, line, col: INTEGER;
   len, err, line, col: INTEGER;
   command: ARRAY 32 OF CHAR;
   command: ARRAY 32 OF CHAR;
+  q: ARRAY 1024 OF SHORTCHAR;
+  z: ARRAY 1024 OF CHAR;
   cmd: ARRAY 1024 OF CHAR;
   cmd: ARRAY 1024 OF CHAR;
   s, sN: ARRAY 80 OF CHAR;
   s, sN: ARRAY 80 OF CHAR;
   success: BOOLEAN;
   success: BOOLEAN;
@@ -452,7 +454,8 @@ BEGIN
   ELSE command := 'link_console'
   ELSE command := 'link_console'
   END;
   END;
   IF Config.isWindows THEN
   IF Config.isWindows THEN
-    IF Term.SearchPath('cmd.exe', cmd) # 0 THEN
+    IF Term.SearchPath('cmd.exe', q) # 0 THEN
+      Utf8.Decode(q, cmd);
       Strings.Insert('"', 0, cmd);
       Strings.Insert('"', 0, cmd);
       Strings.Append('" /C data\bin\', cmd);
       Strings.Append('" /C data\bin\', cmd);
       Strings.Append(command, cmd);
       Strings.Append(command, cmd);
@@ -482,7 +485,8 @@ BEGIN
     END
     END
   END;
   END;
   (*Out.String('Running:'); Out.Ln; Out.String(cmd); Out.Ln;*)
   (*Out.String('Running:'); Out.Ln; Out.String(cmd); Out.Ln;*)
-  success := (Term.RunProcess(cmd, buf, bufLen, len, err) # 0) &
+  Utf8.Encode(cmd, q);
+  success := (Term.RunProcess(q, buf, bufLen, len, err) # 0) &
              (err = 0);
              (err = 0);
   IF ~success THEN
   IF ~success THEN
     s := ' Command returned '; Int.Append(err, s);
     s := ' Command returned '; Int.Append(err, s);
@@ -491,16 +495,17 @@ BEGIN
       IF buf[len - 1] = 0AX THEN buf[len - 1] := 0X
       IF buf[len - 1] = 0AX THEN buf[len - 1] := 0X
       ELSE buf[len] := 0X
       ELSE buf[len] := 0X
       END;
       END;
-      ParseErrors(buf, fname, line, col);
+      Utf8.Decode(buf, z);
+      ParseErrors(z, fname, line, col);
       FocusOrOpenFile(fname);
       FocusOrOpenFile(fname);
       e := app.windows(Editor.Editor);
       e := app.windows(Editor.Editor);
       e.text.MoveToLineCol(line, col, e.h - 2);
       e.text.MoveToLineCol(line, col, e.h - 2);
       Editor.PrintText(app.windows(Editor.Editor));
       Editor.PrintText(app.windows(Editor.Editor));
       T.ResetCursorBlink (* !FIXME *)
       T.ResetCursorBlink (* !FIXME *)
-    ELSIF link THEN buf := 'Linking failed.'
-    ELSE buf := 'Compilation failed.'
+    ELSIF link THEN z := 'Linking failed.'
+    ELSE z := 'Compilation failed.'
     END;
     END;
-    IF buf[0] = 0X THEN ShowErrors(s) ELSE ShowErrors(buf) END
+    IF z[0] = 0X THEN ShowErrors(s) ELSE ShowErrors(z) END
   END ;
   END ;
 RETURN success END RunCommand;
 RETURN success END RunCommand;
 
 
@@ -511,7 +516,8 @@ END Compile;
 PROCEDURE Link(IN fname, mod: ARRAY OF CHAR;
 PROCEDURE Link(IN fname, mod: ARRAY OF CHAR;
     graph: BOOLEAN; list: StrList; VAR exename: ARRAY OF CHAR): BOOLEAN;
     graph: BOOLEAN; list: StrList; VAR exename: ARRAY OF CHAR): BOOLEAN;
 VAR ok: BOOLEAN;
 VAR ok: BOOLEAN;
-  s: ARRAY 256 OF CHAR;
+  s: ARRAY 2048 OF CHAR;
+  e2, s2: ARRAY 2048 OF SHORTCHAR;
   res: INTEGER;
   res: INTEGER;
 BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list);
 BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list);
   IF ok THEN (* Move executable file if workDir is non-standard *)
   IF ok THEN (* Move executable file if workDir is non-standard *)
@@ -519,7 +525,10 @@ BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list);
     exename := 'bin/'; Strings.Append(s, exename);
     exename := 'bin/'; Strings.Append(s, exename);
     IF workDir # Editor.stdPath THEN
     IF workDir # Editor.stdPath THEN
       Strings.Insert(workDir, 0, s);
       Strings.Insert(workDir, 0, s);
-      Files.Rename(exename, s, res);
+
+      Utf8.Encode(exename, e2);
+      Utf8.Encode(s, s2);
+      Files.Rename(e2, s2, res);
       IF res = 0 THEN exename := s$ END
       IF res = 0 THEN exename := s$ END
     END
     END
   END ;
   END ;
@@ -567,12 +576,15 @@ RETURN ok END CompileAll;
 
 
 PROCEDURE RunProgram(IN prg: ARRAY OF CHAR);
 PROCEDURE RunProgram(IN prg: ARRAY OF CHAR);
 VAR dir: ARRAY 256 OF CHAR;
 VAR dir: ARRAY 256 OF CHAR;
+  s, d: ARRAY 2048 OF SHORTCHAR;
   i: INTEGER;
   i: INTEGER;
 BEGIN dir := prg$;
 BEGIN dir := prg$;
   i := 0; WHILE dir[i] # 0X DO INC(i) END;
   i := 0; WHILE dir[i] # 0X DO INC(i) END;
   WHILE (i # -1) & (dir[i] # '/') & (dir[i] # '\') DO DEC(i) END; INC(i);
   WHILE (i # -1) & (dir[i] # '/') & (dir[i] # '\') DO DEC(i) END; INC(i);
   dir[i] := 0X;
   dir[i] := 0X;
-  IF ~Term.StartProcessIn(prg, dir) THEN
+  Utf8.Encode(prg, s);
+  Utf8.Encode(dir, d);
+  IF ~Term.StartProcessIn(s, d) THEN
     T.PutString(0, T.charsY - 1, ' Program execution failed ', 15, 4, 0);
     T.PutString(0, T.charsY - 1, ' Program execution failed ', 15, 4, 0);
     IF T.Draw() THEN G.Flip; G.Pause END
     IF T.Draw() THEN G.Flip; G.Pause END
   ELSE
   ELSE
@@ -641,24 +653,25 @@ END FileSave;
 
 
 PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
 PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
 VAR last: CHAR;
 VAR last: CHAR;
-BEGIN last := ch; Files.ReadChar(R, ch);
+  x: SHORTCHAR;
+BEGIN last := ch; Files.ReadChar(R, x); ch := LONG(x);
   WHILE ~R.eof & ((last # '*') OR (ch # ')')) DO
   WHILE ~R.eof & ((last # '*') OR (ch # ')')) DO
     IF (last = '(') & (ch = '*') THEN SkipComment(R, ch, s) END;
     IF (last = '(') & (ch = '*') THEN SkipComment(R, ch, s) END;
-    last := ch; Files.ReadChar(R, ch)
+    last := ch; Files.ReadChar(R, x); ch := LONG(x)
   END;
   END;
-  IF ~R.eof THEN Files.ReadChar(R, ch) END;
-  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, ch) END
+  IF ~R.eof THEN Files.ReadChar(R, x); ch := LONG(x) END;
+  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, x); ch := LONG(x) END
 END SkipComment;
 END SkipComment;
 
 
 PROCEDURE GetSym(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
 PROCEDURE GetSym(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
-VAR i: INTEGER;
+VAR i: INTEGER; x: SHORTCHAR;
 BEGIN
 BEGIN
-  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, ch) END;
+  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, x); ch := LONG(x) END;
   i := 0;
   i := 0;
   IF ~R.eof THEN
   IF ~R.eof THEN
     IF ch = '(' THEN
     IF ch = '(' THEN
-      Files.ReadChar(R, ch);
-      IF ch = '*' THEN Files.ReadChar(R, ch); SkipComment(R, ch, s)
+      Files.ReadChar(R, x); ch := LONG(x);
+      IF ch = '*' THEN Files.ReadChar(R, x); ch := LONG(x); SkipComment(R, ch, s)
       ELSE s[i] := ch; INC(i)
       ELSE s[i] := ch; INC(i)
       END
       END
     END;
     END;
@@ -667,14 +680,14 @@ BEGIN
             (('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
             (('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
              ('0' <= ch) & (ch <= '9')) DO
              ('0' <= ch) & (ch <= '9')) DO
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
-        Files.ReadChar(R, ch)
+        Files.ReadChar(R, x); ch := LONG(x)
       END
       END
     ELSE
     ELSE
       WHILE ~R.eof & (ch > ' ') &
       WHILE ~R.eof & (ch > ' ') &
             ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
             ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
               ('0' <= ch) & (ch <= '9')) DO
               ('0' <= ch) & (ch <= '9')) DO
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
-        Files.ReadChar(R, ch)
+        Files.ReadChar(R, x); ch := LONG(x)
       END
       END
     END
     END
   END;
   END;
@@ -688,11 +701,14 @@ VAR F: Files.File;
   top, p: StrList;
   top, p: StrList;
   ch: CHAR;
   ch: CHAR;
   mod, s, fname2: ARRAY 256 OF CHAR;
   mod, s, fname2: ARRAY 256 OF CHAR;
+  q: ARRAY 2048 OF SHORTCHAR;
+  x: SHORTCHAR;
   exit: BOOLEAN;
   exit: BOOLEAN;
 BEGIN NEW(top); top.next := NIL; p := top;
 BEGIN NEW(top); top.next := NIL; p := top;
-  F := Files.Old(fname);
+  Utf8.Encode(fname, q);
+  F := Files.Old(q);
   IF F # NIL THEN
   IF F # NIL THEN
-    Files.Set(R, F, 0); Files.ReadChar(R, ch); GetSym(R, ch, s);
+    Files.Set(R, F, 0); Files.ReadChar(R, x); ch := LONG(x); GetSym(R, ch, s);
     ok := s = 'MODULE'; GetSym(R, ch, s); GetSym(R, ch, s); (*!FIXME check module name*)
     ok := s = 'MODULE'; GetSym(R, ch, s); GetSym(R, ch, s); (*!FIXME check module name*)
     IF ok THEN
     IF ok THEN
       ok := s = ';'; GetSym(R, ch, s);
       ok := s = ';'; GetSym(R, ch, s);
@@ -1068,14 +1084,15 @@ END ParseSize;
 PROCEDURE ParseArgs(VAR fs, sw: BOOLEAN; VAR w, h: INTEGER;
 PROCEDURE ParseArgs(VAR fs, sw: BOOLEAN; VAR w, h: INTEGER;
     VAR fnames: Fnames);
     VAR fnames: Fnames);
 VAR i, nofnames: INTEGER;
 VAR i, nofnames: INTEGER;
-  s: ARRAY 256 OF CHAR;
+  s: ARRAY 2048 OF CHAR;
+  q: ARRAY 2048 OF SHORTCHAR;
 BEGIN fs := TRUE; sw := FALSE; i := 1; nofnames := 0; w := -1; h := -1;
 BEGIN fs := TRUE; sw := FALSE; i := 1; nofnames := 0; w := -1; h := -1;
   WHILE i # Args.argc DO
   WHILE i # Args.argc DO
-    Args.Get(i, s);
+    Args.Get(i, q); Utf8.Decode(q, s);
     IF s = '--window' THEN fs := FALSE
     IF s = '--window' THEN fs := FALSE
     ELSIF s = '--software' THEN sw := TRUE
     ELSIF s = '--software' THEN sw := TRUE
     ELSIF s = '--size' THEN
     ELSIF s = '--size' THEN
-      IF i + 1 # Args.argc THEN INC(i); Args.Get(i, s); ParseSize(s, w, h) END
+      IF i + 1 # Args.argc THEN INC(i); Args.Get(i, q); Utf8.Decode(q, s); ParseSize(s, w, h) END
     ELSIF nofnames < LEN(fnames) THEN
     ELSIF nofnames < LEN(fnames) THEN
       ParseFileNameArg(s);
       ParseFileNameArg(s);
       fnames[nofnames] := s$;
       fnames[nofnames] := s$;

+ 20 - 33
src/Graph.Mod

@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 You should have received a copy of the GNU General Public License
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
 *)
-IMPORT SDL := SDL2, SYSTEM, Platform, Out;
+IMPORT SDL := SDL2, SYSTEM, Platform, Out, Utf8;
 
 
 CONST
 CONST
   (* Flip Flags *)
   (* Flip Flags *)
@@ -190,7 +190,6 @@ CONST
 
 
 TYPE
 TYPE
   ADRINT = SYSTEM.ADRINT;
   ADRINT = SYSTEM.ADRINT;
-  CHAR = SHORTCHAR;
   SET32 = SET;
   SET32 = SET;
 
 
   Bitmap* = POINTER TO BitmapDesc;
   Bitmap* = POINTER TO BitmapDesc;
@@ -228,8 +227,8 @@ TYPE
     button*: INTEGER;
     button*: INTEGER;
     buttons*: SET; (* What mouse buttons are pressed *)
     buttons*: SET; (* What mouse buttons are pressed *)
     down*: BOOLEAN;
     down*: BOOLEAN;
-    s*: ARRAY 32 OF CHAR;
-    ch*: INTEGER(*SHOULD BE 2-byte CHAR*)
+    s*: ARRAY 32 OF CHAR; (* For Text Input and Text Edit Events (SDL2, !TODO) *)
+    ch*: CHAR
   END;
   END;
 
 
   EventQueue* = RECORD
   EventQueue* = RECORD
@@ -278,9 +277,9 @@ PROCEDURE -AAIncludeSDL2h0 '#include "SDL2.h0"';
 (* General *)
 (* General *)
 
 
 PROCEDURE GetError*(VAR s: ARRAY OF CHAR);
 PROCEDURE GetError*(VAR s: ARRAY OF CHAR);
-TYPE P = POINTER TO ARRAY 10240 OF CHAR;
+TYPE P = POINTER TO ARRAY 2048 OF SHORTCHAR;
 VAR p: P;
 VAR p: P;
-BEGIN p := SYSTEM.VAL(P, SDL.GetError()); s := p^$
+BEGIN p := SYSTEM.VAL(P, SDL.GetError()); Utf8.Decode(p^, s)
 END GetError;
 END GetError;
 
 
 PROCEDURE Settings*(w, h: INTEGER; flags: SET);
 PROCEDURE Settings*(w, h: INTEGER; flags: SET);
@@ -563,23 +562,25 @@ END DestroyBitmap;
 
 
 PROCEDURE LoadBitmap*(filename: ARRAY OF CHAR): Bitmap;
 PROCEDURE LoadBitmap*(filename: ARRAY OF CHAR): Bitmap;
 VAR bmp: Bitmap;
 VAR bmp: Bitmap;
-BEGIN NEW(bmp); bmp.surface := SDL.ImgLoad(filename);
+BEGIN NEW(bmp); bmp.surface := SDL.ImgLoad(SHORT(filename));
   IF bmp.surface = NIL THEN bmp := NIL
   IF bmp.surface = NIL THEN bmp := NIL
   ELSE bmp.w := bmp.surface.w; bmp.h := bmp.surface.h END ;
   ELSE bmp.w := bmp.surface.w; bmp.h := bmp.surface.h END ;
 RETURN bmp END LoadBitmap;
 RETURN bmp END LoadBitmap;
 
 
 PROCEDURE SaveBmp*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE SaveBmp*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
 BEGIN
 BEGIN
-  RETURN SDL.SaveBmpRW(bmp.surface, SDL.RWFromFile(filename, 'wb'), 1) = 0
+  RETURN SDL.SaveBmpRW(bmp.surface, SDL.RWFromFile(SHORT(filename), 'wb'), 1) = 0
 END SaveBmp;
 END SaveBmp;
 
 
 PROCEDURE SavePng*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE SavePng*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
-BEGIN
-RETURN SDL.ImgSavePng(bmp.surface, filename) = 0 END SavePng;
+VAR s: ARRAY 1024 OF SHORTCHAR;
+BEGIN Utf8.Encode(filename, s);
+RETURN SDL.ImgSavePng(bmp.surface, s) = 0 END SavePng;
 
 
 PROCEDURE SaveJpg*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
 PROCEDURE SaveJpg*(bmp: Bitmap; filename: ARRAY OF CHAR): BOOLEAN;
-BEGIN
-RETURN SDL.ImgSaveJpg(bmp.surface, filename) = 0 END SaveJpg;
+VAR s: ARRAY 1024 OF SHORTCHAR;
+BEGIN Utf8.Encode(filename, s);
+RETURN SDL.ImgSaveJpg(bmp.surface, s) = 0 END SaveJpg;
 
 
 PROCEDURE Blit*(src, dest: Bitmap; sx, sy, sw, sh, dx, dy: INTEGER);
 PROCEDURE Blit*(src, dest: Bitmap; sx, sy, sw, sh, dx, dy: INTEGER);
 VAR a, b: SDL.Rect;
 VAR a, b: SDL.Rect;
@@ -604,7 +605,7 @@ END StretchBlit;
 
 
 PROCEDURE SetAlpha*(bmp: Bitmap; alpha: INTEGER);
 PROCEDURE SetAlpha*(bmp: Bitmap; alpha: INTEGER);
 BEGIN
 BEGIN
-  IF SDL.SetSurfaceAlphaMod(bmp.surface, CHR(alpha)) = 0 THEN END
+  IF SDL.SetSurfaceAlphaMod(bmp.surface, SYSTEM.VAL(BYTE, alpha)) = 0 THEN END
 END SetAlpha;
 END SetAlpha;
 
 
 PROCEDURE MaskedBlit*(src, dest: Bitmap; sx, sy, dx, dy, w, h: INTEGER);
 PROCEDURE MaskedBlit*(src, dest: Bitmap; sx, sy, dx, dy, w, h: INTEGER);
@@ -671,7 +672,7 @@ BEGIN dstRect.x := x; dstRect.y := y;
 END DrawCharacter;
 END DrawCharacter;
 
 
 PROCEDURE DrawString*(dest: Bitmap; font: Font;
 PROCEDURE DrawString*(dest: Bitmap; font: Font;
-  x, y: INTEGER; s: ARRAY OF CHAR; fg: INTEGER);
+    x, y: INTEGER; s: ARRAY OF CHAR; fg: INTEGER);
 VAR i, cx: INTEGER;
 VAR i, cx: INTEGER;
 BEGIN i := 0; cx := x;
 BEGIN i := 0; cx := x;
   WHILE (s[i] # 0X) & (cx < dest.w) DO
   WHILE (s[i] # 0X) & (cx < dest.w) DO
@@ -710,21 +711,6 @@ BEGIN
   END
   END
 END PumpKeyDown;
 END PumpKeyDown;
 
 
-PROCEDURE DecodeChar(IN s: ARRAY OF CHAR): INTEGER;
-VAR i, x, c: INTEGER;
-BEGIN c := ORD(s[0]);
-  IF c > 80H THEN x := ORD(s[1]) MOD 64; (* Not 1 byte *)
-    IF c DIV 32 = 6 THEN (* 2 bytes *)
-      c := c MOD 32 * 64 + x
-    ELSIF c DIV 16 = 14 THEN (* 3 bytes *)
-      c := (c MOD 16 * 64 + x) * 64 + ORD(s[2]) MOD 64
-    ELSIF c DIV 8 = 30 THEN (* 4 bytes *)
-      c := ((c MOD 8 * 64 + x) * 64 + ORD(s[2]) MOD 64) * 64 + ORD(s[3]) MOD 64
-    ELSE c := 0
-    END
-  END ;
-RETURN c END DecodeChar;
-
 PROCEDURE PumpTextEvent(event: SDL.Event);
 PROCEDURE PumpTextEvent(event: SDL.Event);
 VAR sym: INTEGER;
 VAR sym: INTEGER;
   e: SDL.TextInputEvent;
   e: SDL.TextInputEvent;
@@ -750,8 +736,8 @@ BEGIN
     END;
     END;
     QueueEvent;
     QueueEvent;
     events.buf[events.last].type := textInput;
     events.buf[events.last].type := textInput;
-    events.buf[events.last].s := e.text$;
-    events.buf[events.last].ch := DecodeChar(e.text)
+    Utf8.Decode(e.text, events.buf[events.last].s);
+    events.buf[events.last].ch := events.buf[events.last].s[0]
   END
   END
 END PumpTextEvent;
 END PumpTextEvent;
 
 
@@ -870,7 +856,8 @@ END InitMouseData;
 
 
 (* Misc *)
 (* Misc *)
 PROCEDURE SetWindowTitle*(title: ARRAY OF CHAR);
 PROCEDURE SetWindowTitle*(title: ARRAY OF CHAR);
-BEGIN SDL.SetWindowTitle(window, title)
+VAR s: ARRAY 2048 OF SHORTCHAR;
+BEGIN Utf8.Encode(title, s); SDL.SetWindowTitle(window, s)
 END SetWindowTitle;
 END SetWindowTitle;
 
 
 PROCEDURE SwitchToWindowed*;
 PROCEDURE SwitchToWindowed*;
@@ -1148,7 +1135,7 @@ END Randomize;
 
 
 PROCEDURE Init*(): Bitmap;
 PROCEDURE Init*(): Bitmap;
 VAR flags: SET; success: BOOLEAN; w, h, nw, nh: INTEGER;
 VAR flags: SET; success: BOOLEAN; w, h, nw, nh: INTEGER;
-    s: ARRAY 2000 OF CHAR;
+  s: ARRAY 2000 OF CHAR;
 BEGIN screen := NIL; settings := initSettings;
 BEGIN screen := NIL; settings := initSettings;
   IF SDL.Init({SDL.initVideo}) = 0 THEN
   IF SDL.Init({SDL.initVideo}) = 0 THEN
     flags := {};
     flags := {};

+ 190 - 0
src/In.Mod

@@ -0,0 +1,190 @@
+MODULE In;
+
+IMPORT Platform, SYSTEM, Strings, Out, Utf8;
+
+CONST
+  pending = 0; (* readstate when at start of input or end of line. Implies nextch undefined. *)
+  ready   = 1; (* readstate when nextch is defined and contains next character on current line. *)
+  eof     = 2; (* readstate when at end of file. *)
+
+  (* Codepages, see cp below *)
+  singleByte = 1;
+  utf8       = 2; (*!TODO also add UTF16 = 2 *)
+
+TYPE
+  SHORTINT = SYSTEM.INT16; INTEGER = SYSTEM.INT32; HUGEINT = SYSTEM.INT64;
+  REAL = SYSTEM.REAL32; LONGREAL = SYSTEM.REAL64; CHAR = SYSTEM.CHAR16;
+
+VAR
+  Done-: BOOLEAN;
+  nextch: CHAR; (* Maintains 1 character read ahaead except at end of line. *)
+  readstate: INTEGER;
+  cp: INTEGER; (* Input Code Page *)
+
+PROCEDURE Open*;
+VAR error: Platform.ErrorCode;
+BEGIN
+  error := Platform.Seek(Platform.StdIn, 0, Platform.SeekSet); (* Rewind STDIN to beginning of file. *)
+  cp := singleByte;
+  nextch := 0X;
+  readstate := pending;
+  Done := error = 0
+END Open;
+
+PROCEDURE GetByte(): INTEGER;
+VAR error: Platform.ErrorCode; x, n: INTEGER;
+  m: ARRAY 1 OF BYTE;
+BEGIN
+  error := Platform.ReadBuf(Platform.StdIn, m, n); x := m[0] MOD 256;
+  Out.String('READ ');Out.Int(x, 0);Out.Ln;
+  IF (error = 0) & (n = 1) THEN readstate := ready
+  ELSE readstate := eof; x := 0
+  END;
+  RETURN x
+END GetByte;
+
+PROCEDURE ReadChar;
+VAR x, y: INTEGER;
+BEGIN
+  x := GetByte();
+  IF readstate = ready THEN
+    IF cp = utf8 THEN
+      IF x > 80H THEN y := GetByte() MOD 64; (* Not 1 byte *)
+        IF x DIV 32 = 6 THEN (* 2 bytes *)
+          x := x MOD 32 * 64 + y
+        ELSIF y DIV 16 = 14 THEN (* 3 bytes *)
+          x := (x MOD 16 * 64 + y) * 64 + GetByte() MOD 64
+        ELSIF y DIV 8 = 30 THEN (* 4 bytes *)
+          x := ((x MOD 8 * 64 + y) * 64 + GetByte() MOD 64) * 64 + GetByte() MOD 64
+        ELSE x := 0
+        END
+      END
+    END;
+    nextch := CHR(x)
+  END
+END ReadChar;
+
+PROCEDURE StartRead; (* Ensure either nextch is valid or we're at EOF. *)
+BEGIN Out.Flush; IF readstate = pending THEN ReadChar END
+END StartRead;
+
+PROCEDURE StartAndSkip; (* Like StartRead, but also skip over blanks, CR, LF, tab. *)
+BEGIN StartRead;
+  WHILE (readstate = ready) & (nextch <= ' ') DO ReadChar END
+END StartAndSkip;
+
+PROCEDURE Char*(VAR ch: CHAR);
+BEGIN StartRead;
+  IF readstate = ready THEN Done := TRUE; ch := nextch;
+    IF ch = 0AX THEN readstate := pending ELSE ReadChar END
+  ELSE Done := FALSE; ch := 0X
+  END
+END Char;
+
+PROCEDURE HugeInt*(VAR h: HUGEINT);
+VAR neg, hex, endofnum: BOOLEAN;
+  decacc, hexacc, digit: HUGEINT;
+BEGIN StartAndSkip;
+  Done := FALSE;
+  IF readstate = ready THEN
+    neg := nextch = '-'; IF neg THEN ReadChar END;
+    hex := FALSE;
+    endofnum := FALSE;
+    decacc := 0;
+    hexacc := 0;
+    WHILE (readstate = ready) & ~endofnum DO
+      digit := -1;
+      IF (nextch >= '0') & (nextch <= '9') THEN
+        digit := ORD(nextch) MOD 16
+      ELSIF (nextch >= 'a') & (nextch <= 'f') OR
+            (nextch >= 'A') & (nextch <= 'F') THEN
+        digit := ORD(nextch) MOD 16 + 9; hex := TRUE
+      END;
+      IF digit >= 0 THEN
+        Done := TRUE;
+        decacc := decacc * 10 + digit;
+        hexacc := hexacc * 16 + digit;
+        ReadChar
+      ELSIF nextch = 'H' THEN
+        hex := TRUE; endofnum := TRUE; ReadChar
+      ELSE
+        endofnum := TRUE
+      END
+    END;
+    IF Done THEN
+      IF hex THEN h := hexacc ELSE h := decacc END;
+      IF neg THEN h := -h END
+    ELSE h := 0
+    END
+  END
+END HugeInt;
+
+PROCEDURE Int16*(VAR i: SHORTINT);
+VAR h: HUGEINT;
+BEGIN HugeInt(h); i := SHORT(SHORT(h)) (*!FIXME check range?*)
+END Int16;
+
+PROCEDURE LongInt*(VAR i: INTEGER);
+VAR h: HUGEINT;
+BEGIN HugeInt(h); i := SHORT(h) (*!FIXME check range?*)
+END LongInt;
+
+PROCEDURE Int*(VAR i: INTEGER); (*32-bit INTEGER alias*)
+VAR h: HUGEINT;
+BEGIN HugeInt(h); i := SHORT(h) (*!FIXME check range?*)
+END Int;
+
+PROCEDURE Line*(VAR line: ARRAY OF CHAR);
+VAR i: INTEGER;
+BEGIN StartRead; i := 0; Done := readstate = ready;
+  WHILE (readstate = ready) & (nextch # 0DX) & (nextch # 0AX) &
+        (i < LEN(line) - 1) DO
+    line[i] := nextch; INC(i); ReadChar
+  END;
+  line[i] := 0X;
+  IF (readstate = ready) & (nextch = 0DX) THEN ReadChar END;
+  IF (readstate = ready) & (nextch = 0AX) THEN readstate := pending END
+END Line;
+
+PROCEDURE String*(VAR s: ARRAY OF CHAR);
+VAR i: INTEGER;
+BEGIN StartAndSkip; i := 0;
+  IF (readstate = ready) & (nextch = '"') THEN
+    ReadChar;
+    WHILE (readstate = ready) & (i < LEN(s) - 1) &
+        (nextch >= ' ') & (nextch # '"') DO
+      s[i] := nextch; ReadChar; INC(i)
+    END
+  END;
+  Done := (readstate = ready) & (i < LEN(s) - 1) & (nextch = '"');
+  IF Done THEN ReadChar; s[i] := 0X
+  ELSE s[0] := 0X
+  END
+END String;
+
+PROCEDURE Name*(VAR name: ARRAY OF CHAR); (* Read filename. Presumably using shell semantics. *)
+BEGIN HALT(99) (* Not implemented *)
+END Name;
+
+PROCEDURE Real*(VAR x: REAL);
+VAR s: ARRAY 16 OF CHAR;
+BEGIN
+  StartAndSkip;
+  Line(s);
+  Strings.StrToReal(s, x)
+END Real;
+
+PROCEDURE LongReal*(VAR y: LONGREAL);
+VAR s: ARRAY 16 OF CHAR;
+BEGIN
+  StartAndSkip;
+  Line(s);
+  Strings.StrToLongReal(s, y)
+END LongReal;
+
+BEGIN
+  cp := utf8;
+  nextch := 0X;
+  readstate := pending;
+  Done := TRUE
+END In.

+ 0 - 2
src/Int.Mod

@@ -1,8 +1,6 @@
 MODULE Int;
 MODULE Int;
 IMPORT Strings;
 IMPORT Strings;
 
 
-TYPE CHAR = SHORTCHAR;
-
 PROCEDURE Str*(n: INTEGER; VAR s: ARRAY OF CHAR);
 PROCEDURE Str*(n: INTEGER; VAR s: ARRAY OF CHAR);
 VAR i, j: INTEGER; tmp: CHAR; neg: BOOLEAN;
 VAR i, j: INTEGER; tmp: CHAR; neg: BOOLEAN;
 BEGIN
 BEGIN

+ 9 - 10
src/OV.Mod

@@ -567,8 +567,6 @@ CONST
   hCtrlAltShiftPause*     = 700H + 72;
   hCtrlAltShiftPause*     = 700H + 72;
 
 
 TYPE
 TYPE
-  CHAR = SHORTCHAR;
-
   App* = POINTER TO AppDesc;
   App* = POINTER TO AppDesc;
   Control* = POINTER TO ControlDesc;
   Control* = POINTER TO ControlDesc;
   ControlMethod* = POINTER TO ControlMethodDesc;
   ControlMethod* = POINTER TO ControlMethodDesc;
@@ -602,7 +600,7 @@ TYPE
     click*: PROCEDURE (c: Control);
     click*: PROCEDURE (c: Control);
     dblClick*: PROCEDURE (c: Control);
     dblClick*: PROCEDURE (c: Control);
     keyDown*: PROCEDURE (c: Control; key: G.Key);
     keyDown*: PROCEDURE (c: Control; key: G.Key);
-    textInput*: PROCEDURE (c: Control; ch: INTEGER);
+    textInput*: PROCEDURE (c: Control; ch: CHAR);
     getFocus*: PROCEDURE (c: Control);
     getFocus*: PROCEDURE (c: Control);
     lostFocus*: PROCEDURE (c: Control);
     lostFocus*: PROCEDURE (c: Control);
     mouseMove*: PROCEDURE (c: Control; x, y: INTEGER; buttons: SET);
     mouseMove*: PROCEDURE (c: Control; x, y: INTEGER; buttons: SET);
@@ -798,8 +796,9 @@ BEGIN
   IF ('a' <= ch) & (ch <= 'z') THEN ch := CAP(ch) END ;
   IF ('a' <= ch) & (ch <= 'z') THEN ch := CAP(ch) END ;
 RETURN ch END Cap;
 RETURN ch END Cap;
 
 
-PROCEDURE ToCP866*(n: INTEGER): CHAR;
-BEGIN
+PROCEDURE ToCP866*(ch: CHAR): CHAR;
+VAR n: INTEGER;
+BEGIN n := ORD(ch);
   IF n >= 80H THEN
   IF n >= 80H THEN
     IF (410H(*A*) <= n) & (n <= 42FH(*YA*)) THEN n := n - 410H + 80H
     IF (410H(*A*) <= n) & (n <= 42FH(*YA*)) THEN n := n - 410H + 80H
     ELSIF (430H(*a*) <= n) & (n <= 44FH(*ya*)) THEN
     ELSIF (430H(*a*) <= n) & (n <= 44FH(*ya*)) THEN
@@ -1766,10 +1765,10 @@ BEGIN e := c(Edit); redraw := TRUE;
   IF redraw THEN NeedRedraw(c.app); T.ResetCursorBlink END
   IF redraw THEN NeedRedraw(c.app); T.ResetCursorBlink END
 END EditKeyDown;
 END EditKeyDown;
 
 
-PROCEDURE EditTextInput*(c: Control; ch: INTEGER);
+PROCEDURE EditTextInput*(c: Control; ch: CHAR);
 VAR e: Edit;
 VAR e: Edit;
 BEGIN
 BEGIN
-  IF ch # 0 THEN e := c(Edit);
+  IF ch # 0X THEN e := c(Edit);
     InsertChar(ToCP866(ch), e.pos, e.caption, e.len);
     InsertChar(ToCP866(ch), e.pos, e.caption, e.len);
     IF e.pos < e.len THEN INC(e.pos) END;
     IF e.pos < e.len THEN INC(e.pos) END;
     NeedRedraw(c.app); T.ResetCursorBlink
     NeedRedraw(c.app); T.ResetCursorBlink
@@ -1968,10 +1967,10 @@ BEGIN StrList.First(L, s); n := 0;
   IF L.eol THEN n := -1 END ;
   IF L.eol THEN n := -1 END ;
 RETURN n END FindFirstLetterInList;
 RETURN n END FindFirstLetterInList;
 
 
-PROCEDURE ColumnListTextInput*(c: Control; ch: INTEGER);
+PROCEDURE ColumnListTextInput*(c: Control; ch: CHAR);
 VAR C: ColumnList; n: INTEGER;
 VAR C: ColumnList; n: INTEGER;
 BEGIN C := c(ColumnList);
 BEGIN C := c(ColumnList);
-  IF ch # 0 THEN
+  IF ch # 0X THEN
     n := FindFirstLetterInList(C.items, Cap(ToCP866(ch)));
     n := FindFirstLetterInList(C.items, Cap(ToCP866(ch)));
     IF n # -1 THEN ColumnListSetCur(C, n) END
     IF n # -1 THEN ColumnListSetCur(C, n) END
   END
   END
@@ -2429,7 +2428,7 @@ BEGIN handled := FALSE; p := app.cur;
   END
   END
 END OnKeyDown;
 END OnKeyDown;
 
 
-PROCEDURE OnTextInput(app: App; ch: INTEGER);
+PROCEDURE OnTextInput(app: App; ch: CHAR);
 BEGIN
 BEGIN
   IF (app.cur # NIL) & (app.cur.do.textInput # NIL) THEN
   IF (app.cur # NIL) & (app.cur.do.textInput # NIL) THEN
     app.cur.do.textInput(app.cur, ch)
     app.cur.do.textInput(app.cur, ch)

+ 319 - 0
src/Out.Mod

@@ -0,0 +1,319 @@
+MODULE Out;
+
+IMPORT SYSTEM, Platform, Heap, Utf8;
+
+TYPE
+  SHORTINT = SYSTEM.INT16; INTEGER = SYSTEM.INT32; HUGEINT = SYSTEM.INT64;
+  REAL = SYSTEM.REAL32; LONGREAL = SYSTEM.REAL64; CHAR = SYSTEM.CHAR16;
+
+VAR
+  IsConsole-: BOOLEAN;
+
+  buf: ARRAY 128 OF CHAR;
+  in: INTEGER;
+
+PROCEDURE Flush*;
+VAR error: Platform.ErrorCode;
+  s: ARRAY 1024 OF SHORTCHAR;
+  len: INTEGER;
+BEGIN
+  IF in > 0 THEN
+    Utf8.EncodeEx(buf, in, s, len);
+    error := Platform.Write(Platform.StdOut, SYSTEM.ADR(s), len)
+  END;
+  in := 0
+END Flush;
+
+PROCEDURE Open*;
+BEGIN
+END Open;
+
+PROCEDURE Char*(ch: CHAR);
+BEGIN
+  IF in >= LEN(buf) THEN Flush END;
+  buf[in] := ch; INC(in);
+  IF ch = 0AX THEN Flush END
+END Char;
+
+PROCEDURE Length(IN s: ARRAY OF CHAR): INTEGER;
+VAR n: INTEGER;
+BEGIN n := 0; WHILE (n < LEN(s)) & (s[n] # 0X) DO INC(n) END; RETURN n
+END Length;
+
+PROCEDURE String*(IN str: ARRAY OF CHAR);
+VAR l: INTEGER; error: Platform.ErrorCode;
+  s: ARRAY 1024 OF SHORTCHAR;
+  len: INTEGER;
+BEGIN
+  l := Length(str);
+  IF in + l > LEN(buf) THEN Flush END;
+  IF l >= LEN(buf) THEN
+    (* Doesn't fit buf or no sence. Bypass buffering. *)
+    Utf8.EncodeEx(str, l, s, len);
+    error := Platform.Write(Platform.StdOut, SYSTEM.ADR(s), len)
+  ELSE
+    SYSTEM.MOVE(SYSTEM.ADR(str), SYSTEM.ADR(buf[in]), l * 2); INC(in, l)
+  END
+END String;
+
+PROCEDURE Int*(x, n: HUGEINT);
+  CONST zero = ORD('0');
+  VAR s: ARRAY 22 OF CHAR; i: INTEGER; negative: BOOLEAN;
+BEGIN
+  negative := x < 0;
+  IF x = MIN(HUGEINT) THEN
+    s := "8085774586302733229"; i := 19
+  ELSE
+    IF x < 0 THEN x := - x END;
+    s[0] := SHORT(CHR(zero + (x MOD 10))); x := x DIV 10;
+    i := 1; WHILE x # 0 DO
+      s[i] := SHORT(CHR(zero + (x MOD 10)));
+      x := x DIV 10;
+      INC(i)
+    END
+  END;
+  IF negative THEN s[i] := '-'; INC(i) END;
+  WHILE n > i DO Char(' '); DEC(n) END;
+  WHILE i > 0 DO DEC(i); Char(s[i]) END
+END Int;
+
+PROCEDURE Hex*(x, n: HUGEINT);
+BEGIN
+  IF n < 1 THEN n := 1 ELSIF n > 16 THEN n := 16 END;
+  IF x >= 0 THEN
+    WHILE (n < 16) & (SYSTEM.LSH(x, -4*n) # 0) DO INC(n) END
+  END;
+  x := SYSTEM.ROT(x, 4*(16-n));
+  WHILE n > 0 DO
+    x := SYSTEM.ROT(x,4); DEC(n);
+    IF x MOD 16 < 10 THEN Char(SHORT(CHR((x MOD 16) + ORD('0'))))
+    ELSE Char(SHORT(CHR((x MOD 16) - 10 + ORD('A')))) END
+  END
+END Hex;
+
+PROCEDURE Ln*;
+BEGIN String(Platform.NewLine); Flush
+END Ln;
+
+
+(* Real and Longreal display *)
+
+PROCEDURE digit(n: HUGEINT; VAR s: ARRAY OF CHAR; VAR i: INTEGER);
+BEGIN
+  DEC(i); s[i] := SHORT(CHR(n MOD 10 + 48));
+END digit;
+
+PROCEDURE prepend(IN t: ARRAY OF CHAR; VAR s: ARRAY OF CHAR; VAR i: INTEGER);
+  VAR j: INTEGER; l: INTEGER;
+BEGIN
+  l := Length(t); IF l > i THEN l := i END;
+  DEC(i, SHORT(l)); j := 0;
+  WHILE j < l DO s[i+j] := t[j]; INC(j) END
+END prepend;
+
+
+
+PROCEDURE Ten*(e: INTEGER): LONGREAL;
+VAR r, power: LONGREAL;
+BEGIN r := 1.0E0; power := 1.0E1;
+  WHILE e > 0 DO
+    IF ODD(e) THEN r := r*power END;
+    power := power*power; e := SHORT(e DIV 2)
+  END;
+  RETURN r
+END Ten;
+
+PROCEDURE -Entier64(x: LONGREAL): SYSTEM.INT64 "(INTEGER)(x)";
+
+PROCEDURE RealP(x: LONGREAL; n: INTEGER; long: BOOLEAN);
+
+(* RealP(x, n) writes the long real number x to the end of the output stream using an
+   exponential form. If the textual representation of x requires m characters (including  a
+   three-digit signed exponent), x is right adjusted in a field of Max(n, m) characters padded
+   with blanks at the left end. A plus sign of the mantissa is not written.
+   LONGREAL is 1/sign, 11/exponent, 52/significand *)
+
+VAR
+  e:   SHORTINT;         (* Exponent field *)
+  f:   HUGEINT;          (* Fraction field *)
+  s:   ARRAY 30 OF CHAR; (* Buffer built backwards *)
+  i:   INTEGER;          (* Index into s *)
+  el:  INTEGER;          (* Exponent length *)
+  x0:  LONGREAL;
+  nn:  BOOLEAN;          (* Number negative *)
+  en:  BOOLEAN;          (* Exponent negative *)
+  m:   HUGEINT;          (* Mantissa digits *)
+  d:   INTEGER;          (* Significant digit count to display *)
+  dr:  INTEGER;          (* Number of insignificant digits that can be dropped *)
+
+BEGIN
+  e  := SYSTEM.VAL(SHORTINT, (SYSTEM.VAL(HUGEINT, x) DIV 10000000000000L) MOD 800H);
+  f  := SYSTEM.VAL(HUGEINT, x) MOD 10000000000000L;
+  nn := (SYSTEM.VAL(HUGEINT, x) < 0) & ~((e = 7FFH) & (f # 0)); (* Ignore sign on Nan *)
+  IF nn THEN DEC(n) END;
+
+  i := LEN(s);
+  IF e = 7FFH THEN (* NaN / Infinity *)
+    IF f = 0 THEN prepend("Infinity", s, i) ELSE prepend("NaN", s, i) END
+  ELSE
+    (* Calculate number of significant digits caller has proposed space for, and
+       number of digits to generate. *)
+    IF long THEN
+      el := 3;
+      dr := SHORT(n-6);             (* Leave room for dp and '+D000' *)
+      IF dr > 17 THEN dr := 17 END; (* Limit to max useful significant digits *)
+      d := dr;                      (* Number of digits to generate *)
+      IF d < 15 THEN d := 15 END    (* Generate enough digits to do trailing zero supporession *)
+    ELSE
+      el := 2;
+      dr := SHORT(n-5);             (* Leave room for dp and '+E00' *)
+      IF dr > 9 THEN dr := 9 END;   (* Limit to max useful significant digits *)
+      d := dr;                      (* Number of digits to generate *)
+      IF d < 6 THEN d := 6 END      (* Generate enough digits to do trailing zero supporession *)
+    END;
+
+    IF e = 0 THEN
+      WHILE el > 0 DO DEC(i); s[i] := "0"; DEC(el) END;
+      DEC(i); s[i] := "+";
+      m := 0;
+    ELSE
+      IF nn THEN x := -x END;
+
+      (* Scale e to be an exponent of 10 rather than 2 *)
+      e := SHORT(SHORT(LONG(e - 1023) * 77 DIV 256));
+      IF e >= 0 THEN x := x / Ten(e) ELSE x := Ten(SHORT(-e)) * x END ;
+      IF x >= 10.0E0 THEN x := 0.1E0 * x; INC(e) END;
+
+      (* Generate the exponent digits *)
+      en := e < 0; IF en THEN e := SHORT(-e) END;
+      WHILE el > 0 DO digit(e, s, i); e := SHORT(e DIV 10); DEC(el) END;
+      DEC(i); IF en THEN s[i] := "-" ELSE s[i] := "+" END;
+
+      (* Scale x to enough significant digits to reliably test for trailing
+         zeroes or to the amount of space available, if greater. *)
+      x0 := Ten(SHORT(d-1));
+      x  := x0 * x;
+      x  := x + 0.5E0; (* Do not combine with previous line as doing so
+                          introduces a least significant bit difference
+                          between 32 bit and 64 bit builds. *)
+      IF x >= 10.0E0 * x0 THEN x := 0.1E0 * x; INC(e) END;
+      m := Entier64(x)
+    END;
+
+    DEC(i); IF long THEN s[i] := "D" ELSE s[i] := "E" END;
+
+    (* Drop trailing zeroes where caller proposes to use less space *)
+    IF dr < 2 THEN dr := 2 END;
+    WHILE (d > dr) & (m MOD 10 = 0) DO m := m DIV 10; DEC(d) END;
+
+    (* Render significant digits *)
+    WHILE d > 1 DO digit(m, s, i); m := m DIV 10; DEC(d) END;
+    DEC(i); s[i] := '.';
+    digit(m, s, i);
+  END;
+
+  (* Generate leading padding *)
+  DEC(n, SHORT(LEN(s)-i)); WHILE n > 0 DO Char(" "); DEC(n) END;
+
+  (* Render prepared number from right end of buffer s *)
+  IF nn THEN Char("-") END;
+  WHILE i < LEN(s) DO Char(s[i]); INC(i) END
+END RealP;
+
+
+PROCEDURE Real*(x: REAL; n: INTEGER);
+BEGIN RealP(x, n, FALSE)
+END Real;
+
+PROCEDURE LongReal*(x: LONGREAL; n: INTEGER);
+BEGIN RealP(x, n, TRUE)
+END LongReal;
+
+(* Convert LONGREAL: Write positive integer value of x into array d.
+   The value is stored backwards, i.e. least significant digit
+   first. n digits are written, with trailing zeros fill.
+   On entry x has been scaled to the number of digits required. *)
+PROCEDURE ConvertL*(x: LONGREAL; n: INTEGER; VAR d: ARRAY OF CHAR);
+  VAR i, j, k: HUGEINT;
+BEGIN
+  IF x < 0 THEN x := -x END;
+  k := 0;
+
+  IF (SIZE(INTEGER) < 8) & (n > 9) THEN
+    (* There are more decimal digits than can be held in a single INTEGER *)
+    i := ENTIER(x /      1000000000.0E0);  (* The 10th and higher digits *)
+    j := ENTIER(x - (i * 1000000000.0E0)); (* The low 9 digits *)
+    (* First generate the low 9 digits. *)
+    IF j < 0 THEN j := 0 END;
+    WHILE k < 9 DO
+      d[k] := SHORT(CHR(j MOD 10 + 48)); j := j DIV 10; INC(k)
+    END;
+    (* Fall through to generate the upper digits *)
+  ELSE
+    (* We can generate all the digits in one go. *)
+    i := ENTIER(x);
+  END;
+
+  WHILE k < n DO
+    d[k] := SHORT(CHR(i MOD 10 + 48)); i := i DIV 10; INC(k)
+  END
+END ConvertL;
+
+PROCEDURE Expo*(x: REAL): INTEGER;
+VAR i: SHORTINT;
+BEGIN
+  SYSTEM.GET(SYSTEM.ADR(x) + 2, i);
+  RETURN i DIV 128 MOD 256
+END Expo;
+
+PROCEDURE RealFix*(x: REAL; n, k: INTEGER);
+CONST maxD = 9;
+VAR e, i, minus, p: INTEGER; x0: REAL;
+  d: ARRAY maxD OF CHAR;
+
+  PROCEDURE seq(ch: CHAR; n: INTEGER);
+  BEGIN WHILE n > 0 DO Char(ch); DEC(n) END
+  END seq;
+
+  PROCEDURE dig(n: INTEGER);
+  BEGIN WHILE n > 0 DO DEC(i); Char(d[i]); DEC(n) END
+  END dig;
+
+BEGIN e := Expo(x);
+  IF k < 0 THEN k := 0 END;
+  IF e = 0 THEN seq(' ', SHORT(n-k-2)); Char('0'); seq(' ', SHORT(k + 1))
+  ELSIF e = 255 THEN String(' NaN'); seq(' ', SHORT(n - 4))
+  ELSE e := (e - 127) * 77 DIV 256;
+    IF x < 0 THEN minus := 1; x := -x ELSE minus := 0 END;
+    IF e >= 0 THEN  (*x >= 1.0,  77/256 = log 2*) x := SHORT(x / Ten(e))
+      ELSE (*x < 1.0*) x := SHORT(Ten(SHORT(-e)) * x)
+    END;
+    IF x >= 10.0 THEN x := 0.1 * x; INC(e) END;
+    (* 1 <= x < 10 *)
+    IF k + e >= maxD - 1 THEN k := SHORT(maxD - 1 - e)
+    ELSIF k + e < 0 THEN k := SHORT(-e); x := 0.0
+    END;
+    x0 := SHORT(Ten(SHORT(k + e))); x := x0 * x + 0.5;
+    IF x >= 10.0 * x0 THEN INC(e) END;
+    (*e = no. of digits before decimal point*)
+    INC(e); i := k + e; ConvertL(x, i, d);
+    IF e > 0 THEN
+      IF k > 0 THEN p := 0 ELSE p := 1 END;
+      seq(' ', n - e - k - 1 - minus + p);
+      IF minus # 0 THEN Char('-') END;
+      dig(e);
+      IF k > 0 THEN Char('.'); dig(k) END
+    ELSE
+      IF k + e > 0 THEN p := 0 ELSE p := 1 END;
+      seq(' ', n - k - 2 - minus + p);
+      IF minus # 0 THEN Char('-') END;
+      Char('0');
+      IF k + e > 0 THEN Char('.'); seq('0', -e); dig(k + e) END
+    END
+  END
+END RealFix;
+
+BEGIN
+  IsConsole := Platform.IsConsole(Platform.StdOut);
+  in := 0
+END Out.

+ 1 - 1
src/SDL2.Mod

@@ -507,7 +507,7 @@ PROCEDURE -GetTicks*(): INTEGER "SDL_GetTicks()";
 PROCEDURE -SetWindowFullscreen*(window: Window; flags: SET): INTEGER
 PROCEDURE -SetWindowFullscreen*(window: Window; flags: SET): INTEGER
     "SDL_SetWindowFullscreen((void *)window, flags)";
     "SDL_SetWindowFullscreen((void *)window, flags)";
 
 
-PROCEDURE -SetSurfaceAlphaMod*(surface: Surface; alpha: CHAR): INTEGER
+PROCEDURE -SetSurfaceAlphaMod*(surface: Surface; alpha: BYTE): INTEGER
     "SDL_SetSurfaceAlphaMod(surface, alpha)";
     "SDL_SetSurfaceAlphaMod(surface, alpha)";
 
 
 END SDL2.
 END SDL2.

+ 1 - 1
src/StrList.Mod

@@ -41,7 +41,7 @@ PROCEDURE GetCur(L: List; VAR s: ARRAY OF CHAR);
 VAR i: INTEGER; p: Item;
 VAR i: INTEGER; p: Item;
 BEGIN
 BEGIN
   IF L.cur # NIL THEN L.eol := FALSE; p := L.cur; i := 0;
   IF L.cur # NIL THEN L.eol := FALSE; p := L.cur; i := 0;
-    WHILE (i < LEN(s) - 1) & (p.s[i] # 0X) DO s[i] := p.s[i]; INC(i) END;
+    WHILE (i < LEN(s) - 1) & (p.s[i] # CHR(0)(*!FIXME*)) DO s[i] := p.s[i]; INC(i) END;
     s[i] := 0X
     s[i] := 0X
   ELSE s[0] := 0X; L.eol := TRUE
   ELSE s[0] := 0X; L.eol := TRUE
   END
   END

+ 228 - 0
src/Strings.Mod

@@ -0,0 +1,228 @@
+(*-------------------------------------------------------------
+Strings provides a set of operations on strings (i.e., on string constants and character
+arrays, both of which contain the character 0X as a terminator). All positions in
+strings start at 0.
+Strings.Length(s)
+  returns the number of characters in s up to and excluding the first 0X.
+Strings.Insert(src, pos, dst)
+  inserts the string src into the string dst at position pos (0 <= pos <= Length(dst)).
+  If pos >= Length(dst), src is appended to dst. If the size of dst is not large enough
+  to hold the result of the operation, the result is truncated so that dst is always
+  terminated with a 0X.
+Strings.Append(s, dst)
+  has the same effect as Insert(s, Length(s), dst).
+Strings.Delete(s, pos, n)
+  deletes n characters from s starting at position pos (0 <= pos < Length(s)).
+  If n > Length(s) - pos, the new length of s is pos.
+Strings.Replace(src, pos, dst)
+  has the same effect as Delete(dst, pos, Length(src)) followed by an Insert(src, pos, dst).
+Strings.Extract(src, pos, n, dst)
+  extracts a substring dst with n characters from position pos (0 <= pos < Length(src)) in src.
+  If n > Length(src) - pos, dst is only the part of src from pos to Length(src) - 1. If the size of
+  dst is not large enough to hold the result of the operation, the result is truncated so that
+  dst is always terminated with a 0X.
+Strings.Pos(pat, s, pos)
+  returns the position of the first occurrence of pat in s after position pos (inclusive).
+  If pat is not found, -1 is returned.
+Strings.Cap(s)
+  replaces each lower case letter in s by its upper case equivalent.
+-------------------------------------------------------------*)
+
+MODULE Strings;
+IMPORT Reals, SYSTEM;
+
+TYPE
+  REAL = SYSTEM.REAL32;
+  LONGREAL = SYSTEM.REAL64;
+
+PROCEDURE Length*(IN s: ARRAY OF CHAR): INTEGER;
+VAR i: INTEGER;
+BEGIN i := 0; WHILE (i < LEN(s)) & (s[i] # 0X) DO INC(i) END ;
+RETURN i END Length;
+
+PROCEDURE Append*(IN extra: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
+VAR n1, n2, i: INTEGER;
+BEGIN
+  n1 := Length(dest); n2 := Length(extra); i := 0;
+  WHILE (i < n2) & (i + n1 < LEN(dest)) DO dest[i + n1] := extra[i]; INC(i) END;
+  IF i + n1 < LEN(dest) THEN dest[i + n1] := 0X ELSE dest[LEN(dest) - 1] := 0X END
+END Append;
+
+PROCEDURE Insert*(IN source: ARRAY OF CHAR; pos: INTEGER; VAR dest: ARRAY OF CHAR);
+VAR n1, n2, len, i, j: INTEGER;
+BEGIN
+  n1 := Length(dest); n2 := Length(source); len := LEN(dest);
+  IF pos < 0 THEN pos := 0 END;
+  IF pos > n1 THEN Append(source, dest); RETURN END;
+  (*--- make room for source*)
+  IF pos + n2 < len THEN
+    i := n1; j := i + n2; (*move also 0X if it is there*)
+    WHILE i >= pos DO
+      IF j < len THEN dest[j] := dest[i] END;
+      DEC(i); DEC(j)
+    END
+  END;
+  (*--- copy source to dest*)
+  i := 0; j := pos;
+  WHILE (i < n2) & (j < len) DO
+    dest[j] := source[i];
+    INC(i); INC(j)
+  END;
+  IF j >= len THEN dest[len - 1] := 0X END
+END Insert;
+
+PROCEDURE Delete*(VAR s: ARRAY OF CHAR; pos, n: INTEGER);
+VAR len, i: INTEGER;
+BEGIN
+  len := Length(s);
+  IF pos < 0 THEN pos := 0 ELSIF pos >= len THEN RETURN END;
+  IF pos + n < len THEN
+    i := pos + n; WHILE i < len DO s[i - n] := s[i]; INC(i) END;
+    IF i - n < LEN(s) THEN s[i - n] := 0X END
+  ELSE s[pos] := 0X
+  END
+END Delete;
+
+PROCEDURE Replace*(IN source: ARRAY OF CHAR; pos: INTEGER; VAR dest: ARRAY OF CHAR);
+BEGIN
+  Delete(dest, pos, pos + Length(source));
+  Insert(source, pos, dest)
+END Replace;
+
+PROCEDURE Extract*(IN source: ARRAY OF CHAR; pos, n: INTEGER; VAR dest: ARRAY OF CHAR);
+VAR len, destLen, i: INTEGER;
+BEGIN
+  len := Length(source); destLen := LEN(dest) - 1;
+  IF pos < 0 THEN pos := 0 END;
+  IF pos >= len THEN dest[0] := 0X; RETURN END;
+  i := 0;
+  WHILE (pos + i <= LEN(source)) & (source[pos + i] # 0X) & (i < n) DO
+    IF i < destLen THEN dest[i] := source[pos + i] END;
+    INC(i)
+  END;
+  dest[i] := 0X
+END Extract;
+
+PROCEDURE Pos*(IN pattern, s: ARRAY OF CHAR; pos: INTEGER): INTEGER;
+VAR n1, n2, i, j: INTEGER;
+BEGIN
+  n1 := Length(s); n2 := Length(pattern);
+  IF n2 = 0 THEN RETURN 0 END;
+  i := pos;
+  WHILE i <= n1 - n2 DO
+    IF s[i] = pattern[0] THEN
+      j := 1; WHILE (j < n2) & (s[i + j] = pattern[j]) DO INC(j) END;
+      IF j = n2 THEN RETURN i END
+    END;
+    INC(i)
+  END;
+  RETURN -1
+END Pos;
+
+PROCEDURE Cap*(VAR s: ARRAY OF CHAR);
+VAR i: INTEGER;
+BEGIN
+  i := 0;
+  WHILE s[i] # 0X DO
+    IF ('a' <= s[i]) & (s[i] <= 'z') THEN s[i] := CAP(s[i]) END;
+    INC(i)
+  END
+END Cap;
+
+PROCEDURE Match*(IN string, pattern: ARRAY OF CHAR): BOOLEAN;
+
+  PROCEDURE M (IN name, mask: ARRAY OF CHAR; n, m: INTEGER): BOOLEAN;
+  BEGIN
+    WHILE (n >= 0) & (m >= 0) & (mask[m] # '*') DO
+      IF name[n] # mask[m] THEN RETURN FALSE END;
+      DEC(n); DEC(m)
+    END;
+    (* ----- name empty | mask empty | mask ends with '*' *)
+    IF m < 0 THEN RETURN n < 0 END;
+    (* ----- name empty | mask ends with '*' *)
+    WHILE (m >= 0) & (mask[m] = '*') DO DEC(m) END;
+    IF m < 0 THEN RETURN TRUE END;
+    (* ----- name empty | mask still to be matched *)
+    WHILE n >= 0 DO
+      IF M(name, mask, n, m) THEN RETURN TRUE END;
+      DEC(n)
+    END;
+    RETURN FALSE
+  END M;
+
+BEGIN
+  RETURN M(string, pattern, Length(string) - 1, Length(pattern) - 1)
+END Match;
+
+PROCEDURE StrToReal*(IN s: ARRAY OF CHAR; VAR r: REAL);
+VAR p, e: SHORTINT; y, g: REAL; neg, negE: BOOLEAN;
+BEGIN
+  p := 0;
+  WHILE (s[p] = ' ') OR (s[p] = '0') DO INC(p) END;
+  IF s[p] = '-' THEN neg := TRUE; INC(p) ELSE neg := FALSE END;
+  WHILE (s[p] = ' ') OR (s[p] = '0') DO INC(p) END;
+  
+  y := 0;
+  WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+    y := y * 10 + (ORD(s[p]) - 30H);
+    INC(p)
+  END;
+  IF s[p] = '.' THEN
+    INC(p); g := 1; 
+    WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+      g := g / 10; y := y + g * (ORD(s[p]) - 30H);
+      INC(p)
+    END
+  END;
+  IF (s[p] = 'D') OR (s[p] = 'E') THEN
+    INC(p); e := 0;
+    IF s[p] = '-' THEN negE := TRUE; INC(p) ELSE negE := FALSE END;
+    WHILE (s[p] = '0') DO INC(p) END;
+    WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+      e := SHORT(e * 10 + (ORD(s[p]) - 30H));
+      INC(p)
+    END;
+    IF negE THEN y := y / Reals.Ten(e)
+    ELSE y := y * Reals.Ten(e)
+    END
+  END;
+  IF neg THEN y := -y END;
+  r := y
+END StrToReal;
+
+PROCEDURE StrToLongReal*(IN s: ARRAY OF CHAR; VAR r: LONGREAL);
+VAR p, e: INTEGER; y, g: LONGREAL; neg, negE: BOOLEAN;
+BEGIN
+  p := 0;
+  WHILE (s[p] = ' ') OR (s[p] = '0') DO INC(p) END;
+  IF s[p] = '-' THEN neg := TRUE; INC(p) ELSE neg := FALSE END;
+  WHILE (s[p] = ' ') OR (s[p] = '0') DO INC(p) END;
+  y := 0;
+  WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+    y := y * 10 + (ORD(s[p]) - 30H);
+    INC(p)
+  END;
+  IF s[p] = '.' THEN
+    INC(p); g := 1; 
+    WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+      g := g / 10; y := y + g * (ORD(s[p]) - 30H);
+      INC(p)
+    END
+  END;
+  IF (s[p] = 'D') OR (s[p] = 'E') THEN
+    INC(p); e := 0;
+    IF s[p] = '-' THEN negE := TRUE; INC(p) ELSE negE := FALSE END;
+    WHILE (s[p] = '0') DO INC(p) END;
+    WHILE ('0' <= s[p]) & (s[p] <= '9') DO
+      e := e * 10 + ORD(s[p]) - 30H;
+      INC(p)
+    END;
+    IF negE THEN y := y / LONG(Reals.Ten(SHORT(e)))
+    ELSE y := y * LONG(Reals.Ten(SHORT(e)))
+    END
+  END;
+  IF neg THEN y := -y END;
+  r := y
+END StrToLongReal;
+
+END Strings.

+ 2 - 3
src/Terminal.Mod

@@ -22,7 +22,6 @@ CONST
   cursorTicks* = 10; (* Ticks before cursor flashes *)
   cursorTicks* = 10; (* Ticks before cursor flashes *)
 
 
 TYPE
 TYPE
-  CHAR = SHORTCHAR;
   ScreenChar* = RECORD
   ScreenChar* = RECORD
     ch*: CHAR;
     ch*: CHAR;
     fg*, bg*: INTEGER; (* Цвет текста и цвет фона*)
     fg*, bg*: INTEGER; (* Цвет текста и цвет фона*)
@@ -316,7 +315,7 @@ END WriteString;
 
 
 PROCEDURE LoadMedia(): BOOLEAN;
 PROCEDURE LoadMedia(): BOOLEAN;
 CONST fontFile = 'data/images/font.bmp';
 CONST fontFile = 'data/images/font.bmp';
-BEGIN font := G.LoadFont(fontFile, charW, charH);
+BEGIN font := G.LoadFont(LONG(fontFile), charW, charH);
   IF font = NIL THEN Out.String('Could not load font file "');
   IF font = NIL THEN Out.String('Could not load font file "');
     Out.String(fontFile); Out.String('".'); Out.Ln
     Out.String(fontFile); Out.String('".'); Out.Ln
   END ;
   END ;
@@ -336,7 +335,7 @@ BEGIN success := FALSE; isFullscreen := fullscreen;
   G.SetSizeStep(charW, charH);
   G.SetSizeStep(charW, charH);
   screen := G.Init();
   screen := G.Init();
   IF screen # NIL THEN
   IF screen # NIL THEN
-    G.SetWindowTitle('Free Oberon');
+    G.SetWindowTitle(LONG('Free Oberon'));
     G.ShowMouse(FALSE);
     G.ShowMouse(FALSE);
     charsX := screen.w DIV charW;
     charsX := screen.w DIV charW;
     charsY := screen.h DIV charH;
     charsY := screen.h DIV charH;

+ 73 - 0
src/Utf8.Mod

@@ -0,0 +1,73 @@
+MODULE Utf8;
+
+VAR
+  Done-: BOOLEAN;
+
+PROCEDURE Decode*(IN in: ARRAY OF SHORTCHAR; OUT out: ARRAY OF CHAR);
+VAR i, j, val, lim: INTEGER; c: SHORTCHAR;
+BEGIN Done := TRUE; c := in[0]; i := 1; j := 0; lim := LEN(out) - 1;
+  WHILE Done & (c # 0X) & (j < lim) DO
+    IF c < 80X THEN out[j] := c; INC(j)
+    ELSIF (c < 0E0X) & (i < LEN(in) - 2) THEN val := ORD(c) - 192;
+      IF val < 0 THEN out := in$; Done := FALSE END;
+      c := in[i]; INC(i); val := val * 64 + ORD(c) - 128;
+      IF (c < 80X) OR (c >= 0E0X) THEN out := in$; Done := FALSE END;
+      out[j] := CHR(val); INC(j)
+    ELSIF (c < 0F0X) & (i < LEN(in) - 3) THEN val := ORD(c) - 224;
+      c := in[i]; INC(i); val := val * 64 + ORD(c) - 128;
+      IF (c < 80X) OR (c >= 0E0X) THEN out := in$; Done := FALSE END;
+      c := in[i]; INC(i); val := val * 64 + ORD(c) - 128;
+      IF (c < 80X) OR (c >= 0E0X) THEN out := in$; Done := FALSE END;
+      out[j] := CHR(val); INC(j)
+    ELSE out := in$; Done := FALSE
+    END;
+    c := in[i]; INC(i)
+  END;
+  out[j] := 0X;
+  IF c # 0X THEN Done := FALSE END
+END Decode;
+
+PROCEDURE EncodeEx*(IN in: ARRAY OF CHAR; inLen: INTEGER;
+    OUT out: ARRAY OF SHORTCHAR; OUT outLen: INTEGER);
+VAR i, j, val, lim: INTEGER;
+BEGIN Done := TRUE; i := 0; j := 0; lim := LEN(out) - 1;
+  IF inLen < 0 THEN inLen := LEN(in) END;
+  WHILE Done & (i < inLen) & (in[i] # 0X) & (j < lim) DO
+    val := ORD(in[i]); INC(i);
+    IF val < 128 THEN
+      out[j] := SHORT(CHR(val)); INC(j)
+    ELSIF (val < 2048) & (j < lim - 1) THEN
+      out[j] := SHORT(CHR(val DIV 64 + 192)); INC(j);
+      out[j] := SHORT(CHR(val MOD 64 + 128)); INC(j)
+    ELSIF j < lim - 2 THEN
+      out[j] := SHORT(CHR(val DIV 4096 + 224)); INC(j); 
+      out[j] := SHORT(CHR(val DIV 64 MOD 64 + 128)); INC(j);
+      out[j] := SHORT(CHR(val MOD 64 + 128)); INC(j)
+    ELSE Done := FALSE
+    END
+  END;
+  out[j] := 0X; outLen := j;
+  IF in[i] # 0X THEN Done := FALSE END
+END EncodeEx;
+
+PROCEDURE Encode*(IN in: ARRAY OF CHAR; OUT out: ARRAY OF SHORTCHAR);
+VAR n: INTEGER;
+BEGIN EncodeEx(in, -1, out, n)
+END Encode;
+
+PROCEDURE DecodeChar*(IN s: ARRAY OF CHAR): CHAR;
+VAR i, x, c: INTEGER;
+BEGIN c := ORD(s[0]);
+  IF c > 80H THEN x := ORD(s[1]) MOD 64; (* Not 1 byte *)
+    IF c DIV 32 = 6 THEN (* 2 bytes *)
+      c := c MOD 32 * 64 + x
+    ELSIF c DIV 16 = 14 THEN (* 3 bytes *)
+      c := (c MOD 16 * 64 + x) * 64 + ORD(s[2]) MOD 64
+    ELSIF c DIV 8 = 30 THEN (* 4 bytes *)
+      c := ((c MOD 8 * 64 + x) * 64 + ORD(s[2]) MOD 64) * 64 + ORD(s[3]) MOD 64
+    ELSE c := 0
+    END
+  END ;
+RETURN CHR(c) END DecodeChar;
+
+END Utf8.

+ 16 - 4
src/make.sh

@@ -14,11 +14,19 @@ CCFULL="$CC -g3 -O0 -fno-exceptions -I $OFRDIR/../../Mod/Lib -I $OFRDIR/Lib/Obj"
 
 
 $OFR -C Config_linux.Mod
 $OFR -C Config_linux.Mod
 
 
+$OFR -C Utf8.Mod
+
+$OFR -C In.Mod
+
+$OFR -C Out.Mod
+
+$OFR -C Strings.Mod
+
 $OFR -C Int.Mod
 $OFR -C Int.Mod
 
 
-$OFR -7 StrList.Mod
+$OFR -7w StrList.Mod
 
 
-$OFR -7 Dir.Mod
+$OFR -7w Dir.Mod
 
 
 $OFR -C -i SDL2.Mod
 $OFR -C -i SDL2.Mod
 
 
@@ -38,15 +46,19 @@ $OFR -C -m FreeOberon.Mod
 
 
 
 
 
 
+$CCFULL -c Strings.c
+$CCFULL -c Utf8.c
 $CCFULL -c Int.c
 $CCFULL -c Int.c
+$CCFULL -c In.c
+$CCFULL -c Out.c
 $CCFULL -c StrList.c
 $CCFULL -c StrList.c
 $CCFULL -c Dir.c
 $CCFULL -c Dir.c
 $CCFULL -c SDL2.c
 $CCFULL -c SDL2.c
 $CCFULL -c Graph.c
 $CCFULL -c Graph.c
-$AR -crs ../data/bin/libFreeOberon.a Int.o StrList.o Dir.o SDL2.o Graph.o
+$AR -crs ../data/bin/libFreeOberon.a Strings.o Utf8.o Int.o In.o Out.o StrList.o Dir.o SDL2.o Graph.o
 
 
 $CCFULL Config.c term/term_linux.c \
 $CCFULL Config.c term/term_linux.c \
-  Int.o StrList.o Dir.o SDL2.o Graph.o \
+  Strings.o Utf8.o Int.o In.o Out.o StrList.o Dir.o SDL2.o Graph.o \
   Term.c Terminal.c OV.c EditorText.c Editor.c \
   Term.c Terminal.c OV.c EditorText.c Editor.c \
   $PROG.c -o ../$PROG \
   $PROG.c -o ../$PROG \
   $OFRDIR/Lib/libOfront.a \
   $OFRDIR/Lib/libOfront.a \