Make module from Trianus system added.

S3 system filen renamed with prefix eth.
This commit is contained in:
Norayr Chilingarian 2013-10-31 22:37:05 +04:00
parent 671fa80f50
commit 2aeddb9975
15 changed files with 441 additions and 38 deletions

View file

@ -203,16 +203,19 @@ stage6:
$(VOCSTATIC) -sP MersenneTwister.Mod
#s3 libs
$(VOCSTATIC) -sP BTrees.Mod
$(VOCSTATIC) -sP MD5.Mod
$(VOCSTATIC) -sP Zlib.Mod
$(VOCSTATIC) -sP ZlibBuffers.Mod
$(VOCSTATIC) -sP ZlibInflate.Mod
$(VOCSTATIC) -sP ZlibDeflate.Mod
$(VOCSTATIC) -sP ZlibReaders.Mod
$(VOCSTATIC) -sP ZlibWriters.Mod
$(VOCSTATIC) -sP Zip.Mod
$(VOCSTATIC) -sP RandomNumbers.Mod
$(VOCSTATIC) -sP ethBTrees.Mod
$(VOCSTATIC) -sP ethMD5.Mod
$(VOCSTATIC) -sP ethSets.Mod
$(VOCSTATIC) -sP ethZlib.Mod
$(VOCSTATIC) -sP ethZlibBuffers.Mod
$(VOCSTATIC) -sP ethZlibInflate.Mod
$(VOCSTATIC) -sP ethZlibDeflate.Mod
$(VOCSTATIC) -sP ethZlibReaders.Mod
$(VOCSTATIC) -sP ethZlibWriters.Mod
$(VOCSTATIC) -sP ethZip.Mod
$(VOCSTATIC) -sP ethRandomNumbers.Mod
$(VOCSTATIC) -sP ethGZReaders.Mod
$(VOCSTATIC) -sP ethGZWriters.Mod
stage7:

View file

@ -1,8 +1,8 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE BTrees; (** portable *) (* ejz, *)
IMPORT Files;
MODULE ethBTrees; (** portable *) (* ejz, *)
IMPORT Files := OakFiles;
(** BTrees is a utility module that manages b-trees with string (64 characters) or longint keys. Each key is linked to
a longint value (org) which normaly is an offset to where the data for that key is stored. *)
@ -1131,4 +1131,4 @@ MODULE BTrees; (** portable *) (* ejz, *)
BEGIN
Init()
END BTrees.
END ethBTrees.

169
src/lib/s3/ethGZReaders.Mod Normal file
View file

@ -0,0 +1,169 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ethGZReaders; (** Stefan Walthert **)
(**
Reading from .gz files
**)
IMPORT
Files := OakFiles, ZlibReaders := ethZlibReaders;
CONST
(** result codes **)
Ok* = ZlibReaders.Ok; StreamEnd* = ZlibReaders.StreamEnd;
StreamError* = ZlibReaders.StreamError; DataError* = ZlibReaders.DataError; BufError* = ZlibReaders.BufError; FileError* = ZlibReaders.FileError;
BufSize = 4000H;
DeflateMethod = 8;
TYPE
(** structure for reading from a .gz file **)
Reader* = RECORD
file-: Files.File; (** underlying Oberon file **)
res-: LONGINT; (** current stream state **)
transparent: BOOLEAN; (* set if not a .gz file *)
pos: LONGINT; (* logical position in decompressed output stream *)
zr: ZlibReaders.Reader;
END;
(* check .gz header; input buffer must be empty or just have been refilled (in case magic id is missing) *)
PROCEDURE CheckHeader (VAR r: Reader; VAR fr: Files.Rider);
CONST
headCRC = 2; extraField = 4; origName = 8; comment = 10H; reserved = 20H;
VAR
ch, method, flags: CHAR; len: INTEGER;
BEGIN
Files.Read(fr, ch);
IF fr.eof THEN
r.res := StreamEnd
ELSIF ch # 1FX THEN
r.transparent := TRUE; r.res := Ok
ELSE (* first byte of magic id ok *)
Files.Read(fr, ch);
IF fr.eof OR (ch # 8BX)THEN
r.transparent := TRUE; r.res := Ok
ELSE (* second byte of magic id ok *)
Files.Read(fr, method); Files.Read(fr, flags);
IF fr.eof OR (ORD(method) # DeflateMethod) OR (ORD(flags) >= reserved) THEN
r.res := DataError
ELSE
FOR len := 1 TO 6 DO Files.Read(fr, ch) END; (* skip time, xflags and OS code *)
IF ODD(ORD(flags) DIV extraField) THEN (* skip extra field *)
Files.Read(fr, ch); len := ORD(ch);
Files.Read(fr, ch); len := len + 100H*ORD(ch);
WHILE ~fr.eof & (len # 0) DO
Files.Read(fr, ch); DEC(len)
END
END;
IF ODD(ORD(flags) DIV origName) THEN (* skip original file name *)
REPEAT Files.Read(fr, ch) UNTIL fr.eof OR (ch = 0X)
END;
IF ODD(ORD(flags) DIV comment) THEN (* skip the .gz file comment *)
REPEAT Files.Read(fr, ch) UNTIL fr.eof OR (ch = 0X)
END;
IF ODD(ORD(flags) DIV headCRC) THEN (* skip header crc *)
Files.Read(fr, ch); Files.Read(fr, ch)
END;
IF fr.eof THEN r.res := DataError
ELSE r.res := Ok
END
END
END
END
END CheckHeader;
(** open reader on existing file for input **)
PROCEDURE Open* (VAR r: Reader; file: Files.File);
VAR
fr: Files.Rider;
BEGIN
r.transparent := FALSE;
IF file # NIL THEN
r.file := file; Files.Set(fr, file, 0);
CheckHeader(r, fr);
ZlibReaders.Open(r.zr, FALSE, fr);
r.pos := 0
ELSE
r.res := StreamError
END
END Open;
(** close reader **)
PROCEDURE Close* (VAR r: Reader);
VAR
fr: Files.Rider;
crc32: LONGINT;
BEGIN
IF r.transparent THEN
r.res := Ok
ELSE
ZlibReaders.Close(r.zr);
IF r.zr.res = ZlibReaders.Ok THEN
Files.Set(fr, r.file, Files.Length(r.file) - 8);
Files.ReadLInt(fr, crc32);
IF crc32 # r.zr.crc32 THEN
r.res := DataError
ELSE
r.res := Ok
END
ELSE
r.res := r.zr.res
END
END
END Close;
(** read specified number of bytes into buffer and return number of bytes actually read **)
PROCEDURE ReadBytes* (VAR r: Reader; VAR buf: ARRAY OF CHAR; offset, len: LONGINT; VAR read: LONGINT);
VAR i: LONGINT; fr: Files.Rider; bufp: POINTER TO ARRAY OF CHAR;
BEGIN
IF r.file = NIL THEN
r.res := StreamError; read := 0
ELSIF r.res < Ok THEN
read := 0
ELSIF r.res = StreamEnd THEN
read := 0
ELSIF r.transparent THEN (* uncompressed input *)
Files.Set(fr, r.file, r.pos);
IF offset = 0 THEN
Files.ReadBytes(fr, buf, len)
ELSE
NEW(bufp, len);
Files.ReadBytes(fr, bufp^, len);
FOR i := 0 TO len - 1 DO
buf[offset + i] := bufp[i]
END
END;
read := len - fr.res
ELSE
ZlibReaders.ReadBytes(r.zr, buf, offset, len, read)
END;
INC(r.pos, read)
END ReadBytes;
(** read decompressed byte **)
PROCEDURE Read* (VAR r: Reader; VAR ch: CHAR);
BEGIN
ZlibReaders.Read(r.zr, ch)
END Read;
(** get position of reader within uncompressed output stream **)
PROCEDURE Pos* (VAR r: Reader): LONGINT;
VAR pos: LONGINT;
BEGIN
IF r.file = NIL THEN
r.res := StreamError; pos := 0
ELSIF r.res < Ok THEN
pos := 0
ELSE
pos := r.pos
END;
RETURN pos
END Pos;
END ethGZReaders.

113
src/lib/s3/ethGZWriters.Mod Normal file
View file

@ -0,0 +1,113 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ethGZWriters; (** Stefan Walthert **)
IMPORT
Files := OakFiles, ZlibWriters := ethZlibWriters;
CONST
(** result codes **)
Ok* = ZlibWriters.Ok; StreamEnd* = ZlibWriters.StreamEnd;
FileError* = -1; StreamError* = ZlibWriters.StreamError; DataError* = ZlibWriters.DataError; BufError* = ZlibWriters.BufError;
(** compression levels **)
DefaultCompression* = ZlibWriters.DefaultCompression; NoCompression* = ZlibWriters.NoCompression;
BestSpeed* = ZlibWriters.BestSpeed; BestCompression* = ZlibWriters.BestCompression;
(** compression strategies **)
DefaultStrategy* = ZlibWriters.DefaultStrategy; Filtered* = ZlibWriters.Filtered; HuffmanOnly* = ZlibWriters.HuffmanOnly;
DeflateMethod = 8;
TYPE
(** structure for writing to a .gz file **)
Writer* = RECORD
file-: Files.File; (** underlying Oberon file **)
res-: LONGINT; (** current stream state **)
start: LONGINT; (* start of compressed data in file (after header) *)
pos: LONGINT; (* logical position in uncompressed input stream *)
zw: ZlibWriters.Writer;
END;
PROCEDURE WriteHeader(VAR w: Writer; VAR r: Files.Rider);
VAR
i: INTEGER;
BEGIN
Files.Write(r, 1FX); INC(w.start); (* ID1 *)
Files.Write(r, 8BX); INC(w.start); (* ID2 *)
Files.Write(r, CHR(DeflateMethod)); (* CM (Compression Method) *)
FOR i := 0 TO 6 DO Files.Write(r, 0X); INC(w.start) END;
END WriteHeader;
(** change deflate parameters within the writer **)
PROCEDURE SetParams*(VAR w: Writer; level, strategy: SHORTINT);
BEGIN
ZlibWriters.SetParams(w.zw, level, strategy, ZlibWriters.NoFlush);
w.res := w.zw.res;
END SetParams;
(** open writer on .gz-file **)
PROCEDURE Open*(VAR w: Writer; level, strategy: SHORTINT; file: Files.File);
VAR
r: Files.Rider;
BEGIN
w.start := 0;
IF file# NIL THEN
w.file := file; Files.Set(r, w.file, 0);
WriteHeader(w, r);
ZlibWriters.Open(w.zw, level, strategy, ZlibWriters.NoFlush, FALSE, r);
w.res := w.zw.res
ELSE
w.res := FileError
END
END Open;
(** write specified number of bytes from buffer into .gz-file and return number of bytes actually written **)
PROCEDURE WriteBytes*(VAR w: Writer; VAR buf: ARRAY OF CHAR; offset, len: LONGINT; VAR written: LONGINT);
BEGIN
ZlibWriters.WriteBytes(w.zw, buf, offset, len, written);
INC(w.pos, written);
w.res := w.zw.res
END WriteBytes;
(** write byte **)
PROCEDURE Write*(VAR w: Writer; ch: CHAR);
BEGIN
ZlibWriters.Write(w.zw, ch);
w.res := w.zw.res
END Write;
(** close writer **)
PROCEDURE Close*(VAR w: Writer);
VAR
r: Files.Rider;
BEGIN
ZlibWriters.Close(w.zw);
w.res := w.zw.res;
IF w.res = ZlibWriters.Ok THEN
Files.Close(w.file);
Files.Set(r, w.file, Files.Length(w.file));
Files.WriteLInt(r, w.zw.crc32); (* CRC32 *)
Files.WriteLInt(r, w.pos); (* ISIZE: Input Size *)
Files.Close(w.file)
END
END Close;
(** get position of reader within uncompressed output stream **)
PROCEDURE Pos* (VAR w: Writer): LONGINT;
VAR pos: LONGINT;
BEGIN
IF (w.file = NIL) THEN
w.res := StreamError; pos := 0
ELSIF w.res < Ok THEN
pos := 0
ELSE
pos := w.pos
END;
RETURN pos
END Pos;
END ethGZWriters.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE MD5; (** portable *) (* ejz *)
MODULE ethMD5; (** portable *) (* ejz *)
IMPORT SYSTEM;
(** The MD5 Message-Digest Algorithm (RFC1321)
@ -292,4 +292,4 @@ under a public-key cryptosystem such as RSA. *)
str[32] := 0X
END ToString;
END MD5.
END ethMD5.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE RandomNumbers; (** portable *)
MODULE ethRandomNumbers; (** portable *)
(* Random Number Generator, page 12 *)
IMPORT Math := oocOakMath, Oberon := Kernel, SYSTEM;
@ -37,4 +37,4 @@ END InitSeed;
BEGIN
Oberon.GetClock(t, d);
Z := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, t) / SYSTEM.VAL(SET, d))
END RandomNumbers. (* Copyright M. Reiser, 1992 *)
END ethRandomNumbers. (* Copyright M. Reiser, 1992 *)

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE Sets; (** portable *)
MODULE ethSets; (** portable *)
IMPORT Texts := CmdlnTexts;
@ -138,4 +138,4 @@ BEGIN
END Print;
END Sets.
END ethSets.

View file

@ -1,10 +1,10 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE Zip; (** Stefan Walthert **)
MODULE ethZip; (** Stefan Walthert **)
IMPORT
Files := OakFiles, Zlib, ZlibReaders, ZlibWriters;
Files := OakFiles, Zlib := ethZlib, ZlibReaders := ethZlibReaders, ZlibWriters := ethZlibWriters;
CONST
@ -742,4 +742,4 @@ BEGIN
END
END Close;
END Zip.
END ethZip.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE Zlib; (** Stefan Walthert **)
MODULE ethZlib; (** Stefan Walthert **)
IMPORT
SYSTEM;
@ -157,4 +157,4 @@ END CRC32;
BEGIN
InitCRCTable();
END Zlib.
END ethZlib.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ZlibBuffers; (** Stefan Walthert **)
MODULE ethZlibBuffers; (** Stefan Walthert **)
IMPORT
SYSTEM;
@ -113,4 +113,4 @@ BEGIN
INC(buf.avail, size); DEC(buf.next, size);
END Drain;
END ZlibBuffers.
END ethZlibBuffers.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ZlibDeflate; (** Stefan Walthert **)
MODULE ethZlibDeflate; (** Stefan Walthert **)
(**
Compression of byte streams with deflate algorithm
@ -13,7 +13,7 @@ MODULE ZlibDeflate; (** Stefan Walthert **)
*)
IMPORT
SYSTEM, Zlib, ZlibBuffers;
SYSTEM, Zlib := ethZlib, ZlibBuffers := ethZlibBuffers;
CONST
(** Result codes for compression/decompression functions **)
@ -1489,4 +1489,4 @@ BEGIN
ConfigTable[8].MaxChain := 1024; ConfigTable[8].Compress := CompressSlow;
ConfigTable[9].GoodLen := 32; ConfigTable[9].MaxLazy := 128; ConfigTable[9].NiceLen := 258;
ConfigTable[9].MaxChain := 4096; ConfigTable[9].Compress := CompressSlow; (* maximum compression *)
END ZlibDeflate.
END ethZlibDeflate.

View file

@ -1,7 +1,7 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ZlibInflate; (** eos **)
MODULE ethZlibInflate; (** eos **)
(**
Decompression of deflated byte streams
@ -18,7 +18,7 @@ MODULE ZlibInflate; (** eos **)
*)
IMPORT
SYSTEM, Zlib, ZlibBuffers;
SYSTEM, Zlib := ethZlib, ZlibBuffers := ethZlibBuffers;
CONST
(** result codes **)
@ -1227,4 +1227,4 @@ BEGIN
Order[0] := 16; Order[1] := 17; Order[2] := 18; Order[3] := 0; Order[4] := 8; Order[5] := 7; Order[6] := 9;
Order[7] := 6; Order[8] := 10; Order[9] := 5; Order[10] := 11; Order[11] := 4; Order[12] := 12; Order[13] := 3;
Order[14] := 13; Order[15] := 2; Order[16] := 14; Order[17] := 1; Order[18] := 15
END ZlibInflate.
END ethZlibInflate.

View file

@ -1,10 +1,10 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ZlibReaders; (** Stefan Walthert **)
MODULE ethZlibReaders; (** Stefan Walthert **)
IMPORT
Files := OakFiles, Zlib, ZlibBuffers, ZlibInflate;
Files := OakFiles, Zlib := ethZlib, ZlibBuffers := ethZlibBuffers, ZlibInflate := ethZlibInflate;
CONST
(** result codes **)
@ -110,4 +110,4 @@ BEGIN
END Uncompress;
END ZlibReaders.
END ethZlibReaders.

View file

@ -1,10 +1,10 @@
(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
MODULE ZlibWriters; (** Stefan Walthert **)
MODULE ethZlibWriters; (** Stefan Walthert **)
IMPORT
Files := OakFiles, Zlib, ZlibBuffers, ZlibDeflate;
Files := OakFiles, Zlib := ethZlib, ZlibBuffers := ethZlibBuffers, ZlibDeflate := ethZlibDeflate;
CONST
(** result codes **)
@ -158,4 +158,4 @@ BEGIN
END Compress;
END ZlibWriters.
END ethZlibWriters.

118
src/tools/Make/Make.Mod Normal file
View file

@ -0,0 +1,118 @@
(*-----------------------------------------------------------------------
Make topologically sorts a set of Oberon source file names according to their import relationships
so that they are in a correct compilation order. The generated list can be prepended with the
command Compiler.Compile and can be compiled.
Make.Order {filename} ~
reads a list of file names describing Oberon modules. The import relationships of these
modules are inspected and the modules are sorted accordingly. The sorted list of file names
is written to the standard output.
-----------------------------------------------------------------------*)
MODULE Make; (*HM 94-06-22 / *)
IMPORT Texts, In, Out;
TYPE
ModuleName = ARRAY 32 OF CHAR;
Import = POINTER TO ImportDesc;
Module = POINTER TO ModuleDesc;
ModuleDesc = RECORD
name: ModuleName;
imports: Import;
ref: INTEGER;
next: Module
END ;
ImportDesc = RECORD
mod: Module;
next: Import;
END ;
PROCEDURE Append (VAR name: ModuleName; ext: ARRAY OF CHAR);
VAR i, j: INTEGER;
BEGIN
i := 0; WHILE name[i] # 0X DO INC(i) END ;
j := 0; WHILE ext[j] # 0X DO name[i] := ext[j]; INC(i); INC(j) END ;
name[i] := 0X
END Append;
PROCEDURE ReadModuleList (VAR list: Module);
VAR m: Module; name: ModuleName;
BEGIN
In.Open; list := NIL; In.Name(name);
WHILE In.Done DO
NEW(m); m.name := name; m.imports := NIL; m.ref := 0; m.next := list; list := m;
In.Name(name)
END
END ReadModuleList;
PROCEDURE Find (list: Module; name: ModuleName; VAR m: Module);
BEGIN
m := list;
WHILE (m # NIL) & (m.name # name) DO m := m.next END
END Find;
PROCEDURE FindTop (list: Module; VAR m, prev: Module);
BEGIN
m := list; prev := NIL;
WHILE (m # NIL) & (m.ref # 0) DO prev := m; m := m.next END
END FindTop;
PROCEDURE FindImports (m, list: Module);
VAR t: Texts.Text; s: Texts.Scanner; p: Module; imp: Import; name: ModuleName;
BEGIN
NEW(t); Texts.Open(t, m.name);
IF t.len > 0 THEN
Texts.OpenScanner(s, t, 0);
REPEAT Texts.Scan(s) UNTIL s.eot OR (s.class = Texts.Name) & (s.s = "IMPORT");
IF ~ s.eot THEN
REPEAT
Texts.Scan(s);
IF s.class = Texts.Name THEN
COPY(s.s, name); Texts.Scan(s);
IF (s.class = Texts.Char) & (s.c = ":") THEN Texts.Scan(s); Texts.Scan(s); COPY(s.s, name) END ;
Append(name, ".Mod");
Find(list, name, p);
IF p = NIL THEN NEW(p); p.name := name; p.imports := NIL; p.ref := 0 END ;
INC(p.ref);
NEW(imp); imp.mod := p;
imp.next := m.imports; m.imports := imp
END
UNTIL (s.class = Texts.Char) & (s.c = ";")
END
END
END FindImports;
PROCEDURE Print (VAR list: Module);
VAR m, prev: Module; imp: Import;
BEGIN
IF list # NIL THEN
FindTop(list, m, prev);
IF prev = NIL THEN list := m.next ELSE prev.next := m.next END ;
imp := m.imports; WHILE imp # NIL DO DEC(imp.mod.ref); imp := imp.next END ;
Print(list);
Out.String(m.name); Out.String("/s"); Out.Ln
END
END Print;
PROCEDURE Order*;
VAR list, imports, p, m, last: Module;
BEGIN
ReadModuleList(list);
m := list;
WHILE m # NIL DO FindImports(m, list); m := m.next END ;
Print(list)
END Order;
END Make.
Make.Order
POPB.Mod
POPC.Mod
POPdump.Mod
POPL.Mod
POPM.Mod
POPP.Mod
POPS.Mod
POPT.Mod
POPV.Mod
~