MODULE DisplayGTF;
IMPORT SYSTEM, MathL, Strings, Commands;
CONST
GTFLockVF = 1;
GTFLockHF = 2;
GTFLockPF = 3;
TYPE
GTFConstants = RECORD
margin: LONGREAL;
cellGran: LONGREAL;
minPorch: LONGREAL;
vSyncRqd: LONGREAL;
hSync: LONGREAL;
minVSyncBP: LONGREAL;
m: LONGREAL;
c: LONGREAL;
k: LONGREAL;
j: LONGREAL
END;
GTFHCRTC = RECORD
hTotal: LONGINT;
hDisp: LONGINT;
hSyncStart: LONGINT;
hSyncEnd: LONGINT;
END;
GTFVCRTC = RECORD
vTotal: LONGINT;
vDisp: LONGINT;
vSyncStart: LONGINT;
vSyncEnd: LONGINT;
END;
GTFTimings = RECORD
h: GTFHCRTC;
v: GTFVCRTC;
hSyncPol: CHAR;
vSyncPol: CHAR;
interlace: CHAR;
vFreq: LONGREAL;
hFreq: LONGREAL;
END;
VAR
conf*: ARRAY 512 OF CHAR;
hex: ARRAY 17 OF CHAR;
PROCEDURE pow(x: LONGREAL; n: LONGINT): LONGREAL;
VAR s: LONGREAL;
BEGIN
s := 1;
WHILE n > 0 DO s := s * x; DEC(n) END;
RETURN s
END pow;
PROCEDURE Round(v: LONGREAL): LONGREAL;
BEGIN
RETURN ENTIER(v + 0.5)
END Round;
PROCEDURE GetInternalConstants(VAR c: GTFConstants);
VAR GC: GTFConstants;
BEGIN
GC.margin := 1.8; GC.cellGran := 8; GC.minPorch := 1; GC.vSyncRqd := 3;
GC.hSync := 8; GC.minVSyncBP := 550; GC.m := 600; GC.c := 40; GC.k := 128; GC.j := 20;
c.margin := GC.margin; c.cellGran := Round(GC.cellGran);
c.minPorch := Round(GC.minPorch); c.vSyncRqd := Round(GC.vSyncRqd);
c.hSync := GC.hSync; c.minVSyncBP := GC.minVSyncBP;
IF GC.k = 0 THEN c.k := 0.001 ELSE c.k := GC.k END;
c.m := (c.k / 256) * GC.m; c.c := (GC.c - GC.j) * (c.k / 256) + GC.j;
c.j := GC.j
END GetInternalConstants;
PROCEDURE GTFCalcTimings(hPixels, vLines, freq: LONGREAL; type: LONGINT; wantMargins, wantInterlace: BOOLEAN; VAR t: GTFTimings);
VAR
interlace,vFieldRate,hPeriod: LONGREAL;
topMarginLines,botMarginLines: LONGREAL;
leftMarginPixels,rightMarginPixels: LONGREAL;
hPeriodEst,vSyncBP,vBackPorch: LONGREAL;
vTotalLines,vFieldRateEst: LONGREAL;
hTotalPixels,hTotalActivePixels,hBlankPixels: LONGREAL;
idealDutyCycle,hSyncWidth,hSyncBP,hBackPorch: LONGREAL;
idealHPeriod: LONGREAL;
vFreq,hFreq,dotClock: LONGREAL;
c: GTFConstants;
BEGIN
GetInternalConstants(c);
vFreq := freq; hFreq := freq; dotClock := freq;
hPixels := Round(hPixels / c.cellGran) * c.cellGran;
IF wantInterlace THEN
vLines := Round(vLines / 2);
vFieldRate := vFreq * 2;
dotClock := dotClock * 2;
interlace := 0.5;
ELSE vFieldRate := vFreq; interlace := 0
END;
IF wantMargins THEN
topMarginLines := Round(c.margin / 100 * vLines);
botMarginLines := Round(c.margin / 100 * vLines)
ELSE topMarginLines := 0; botMarginLines := 0
END;
IF type # GTFLockPF THEN
IF type = GTFLockVF THEN
hPeriodEst := ((1/vFieldRate)-(c.minVSyncBP/1000000))/
(vLines+(2*topMarginLines)+c.minPorch+interlace)*1000000;
vSyncBP := Round(c.minVSyncBP / hPeriodEst);
ELSIF type = GTFLockHF THEN
vSyncBP := Round((c.minVSyncBP * hFreq) / 1000);
END;
vBackPorch := vSyncBP - c.vSyncRqd;
vTotalLines := vLines + topMarginLines + botMarginLines + vSyncBP
+ interlace + c.minPorch;
IF type = GTFLockVF THEN
vFieldRateEst := 1000000 / (hPeriodEst * vTotalLines);
hPeriod := (hPeriodEst * vFieldRateEst) / vFieldRate;
vFieldRate := 1000000 / (hPeriod * vTotalLines);
ELSIF type = GTFLockHF THEN
vFieldRate := (hFreq / vTotalLines) * 1000;
END
END;
IF wantMargins THEN
leftMarginPixels := Round(hPixels * c.margin) / (100 * c.cellGran);
rightMarginPixels := Round(hPixels * c.margin) / (100 * c.cellGran);
ELSE leftMarginPixels := 0; rightMarginPixels := 0
END;
hTotalActivePixels := hPixels + leftMarginPixels + rightMarginPixels;
IF type = GTFLockVF THEN
idealDutyCycle := c.c - ((c.m * hPeriod) / 1000)
ELSIF type = GTFLockHF THEN
idealDutyCycle := c.c - (c.m / hFreq);
ELSIF type = GTFLockPF THEN
idealHPeriod := (((c.c - 100) + (MathL.sqrt((pow(100-c.c,2)) +
(0.4 * c.m * (hTotalActivePixels + rightMarginPixels +
leftMarginPixels) / dotClock)))) / (2 * c.m)) * 1000;
idealDutyCycle := c.c - ((c.m * idealHPeriod) / 1000);
END;
hBlankPixels := Round((hTotalActivePixels * idealDutyCycle) /
((100 - idealDutyCycle) * c.cellGran)) * c.cellGran;
hTotalPixels := hTotalActivePixels + hBlankPixels;
hBackPorch := Round((hBlankPixels / 2) / c.cellGran) * c.cellGran;
hSyncWidth := Round(((c.hSync/100) * hTotalPixels) / c.cellGran) * c.cellGran;
hSyncBP := hBackPorch + hSyncWidth;
IF type = GTFLockPF THEN
hFreq := (dotClock / hTotalPixels) * 1000;
vSyncBP := Round((c.minVSyncBP * hFreq) / 1000);
vBackPorch := vSyncBP - c.vSyncRqd;
vTotalLines := vLines + topMarginLines + botMarginLines + vSyncBP
+ interlace + c.minPorch;
vFieldRate := (hFreq / vTotalLines) * 1000;
ELSE
IF type = GTFLockVF THEN
hFreq := 1000 / hPeriod;
ELSIF type = GTFLockHF THEN
hPeriod := 1000 / hFreq;
END;
dotClock := hTotalPixels / hPeriod;
END;
IF wantInterlace THEN vFreq := vFieldRate / 2; dotClock := dotClock / 2;
ELSE vFreq := vFieldRate
END;
t.vFreq := vFreq;
t.hFreq := hFreq;
t.h.hTotal := ENTIER(hTotalPixels);
t.h.hDisp := ENTIER(hTotalActivePixels);
t.h.hSyncStart := ENTIER(t.h.hTotal - hSyncBP);
t.h.hSyncEnd := ENTIER(t.h.hTotal - hBackPorch);
t.v.vTotal := ENTIER(vTotalLines);
t.v.vDisp := ENTIER(vLines);
t.v.vSyncStart := ENTIER(t.v.vTotal - vSyncBP);
t.v.vSyncEnd := ENTIER(t.v.vTotal - vBackPorch);
IF wantInterlace THEN t.interlace := 'I' ELSE t.interlace := 'N' END;
t.hSyncPol := '-';
t.vSyncPol := '+'
END GTFCalcTimings;
PROCEDURE VesaConf(mode, width, height, depth, hz: LONGINT; VAR conf: ARRAY OF CHAR);
VAR mode1: LONGINT; flags: SET; t: GTFTimings; valstr : ARRAY 10 OF CHAR; cr : ARRAY 2 OF CHAR;
PROCEDURE HexByte(x: LONGINT);
VAR s: ARRAY 3 OF CHAR;
BEGIN
s[0] := hex[x DIV 10H];
s[1] := hex[x MOD 10H];
s[2] := CHR(0);
Strings.Append(conf, s);
END HexByte;
PROCEDURE HexWord(x: LONGINT);
BEGIN
HexByte(x MOD 100H); HexByte(x DIV 100H)
END HexWord;
PROCEDURE HexDWord(x: LONGINT);
BEGIN
HexWord(x MOD 10000H); HexWord(x DIV 10000H)
END HexDWord;
BEGIN
cr[0] := 0DX; cr[1] := 0X;
Strings.Append(conf, cr);
Strings.Append(conf, 'Init="');
IF hz > 0 THEN
mode1 := mode + 4800H;
GTFCalcTimings(width, height, hz, GTFLockVF, FALSE, FALSE, t);
Strings.Append(conf, "b80b4fbb0000ba"); HexWord(mode1);
Strings.Append(conf, "66b9"); HexDWord(hz * t.h.hTotal * t.v.vTotal);
Strings.Append(conf, "cd1026c705"); HexWord(t.h.hTotal);
Strings.Append(conf, "26c74502"); HexWord(t.h.hSyncStart);
Strings.Append(conf, "26c74504"); HexWord(t.h.hSyncEnd);
Strings.Append(conf, "26c74506"); HexWord(t.v.vTotal);
Strings.Append(conf, "26c74508"); HexWord(t.v.vSyncStart);
Strings.Append(conf, "26c7450a"); HexWord(t.v.vSyncEnd);
flags := {};
IF t.interlace = "I" THEN INCL(flags, 1) END;
IF t.hSyncPol = "-" THEN INCL(flags, 2) END;
IF t.vSyncPol = "-" THEN INCL(flags, 3) END;
Strings.Append(conf, "26c6450c"); HexByte(SYSTEM.VAL(LONGINT, flags));
Strings.Append(conf, "2666894d0d26c74511"); HexWord(hz*100);
Strings.Append(conf, cr);
ELSE
mode1 := mode + 4000H
END;
Strings.Append(conf, "b8024fbb"); HexWord(mode1);
Strings.Append(conf, "cd10b8014fb9"); HexWord(mode);
Strings.Append(conf, "cd10268b4d28268b552a");
Strings.Append(conf, '"');
Strings.Append(conf, cr);
Strings.Append(conf, 'DWidth="');
Strings.IntToStr(width, valstr);
Strings.Append(conf, valstr);
Strings.Append(conf, '"');
Strings.Append(conf, cr);
Strings.Append(conf, 'DHeight="');
Strings.IntToStr(height, valstr);
Strings.Append(conf, valstr);
Strings.Append(conf, '"');
Strings.Append(conf, cr);
Strings.Append(conf, 'DDepth="');
Strings.IntToStr(depth, valstr);
Strings.Append(conf, valstr);
Strings.Append(conf, '"');
Strings.Append(conf, cr);
END VesaConf;
PROCEDURE Mode*(context : Commands.Context);
VAR display: ARRAY 512 OF CHAR;
m, w, h, d, f: LONGINT;
BEGIN
context.arg.SkipWhitespace; context.arg.Int(m, FALSE); context.out.Int(m, 5);
context.arg.SkipWhitespace; context.arg.Int(w, FALSE); context.out.Int(w, 5);
context.arg.SkipWhitespace; context.arg.Int(h, FALSE); context.out.Int(h, 5);
context.arg.SkipWhitespace; context.arg.Int(d, FALSE); context.out.Int(d, 5);
context.arg.SkipWhitespace; context.arg.Int(f, FALSE); context.out.Int(f, 5);
display[0] := 0X;
VesaConf(m, w, h, d, f, display);
context.out.String(display);
END Mode;
BEGIN
hex := "0123456789abcdef"
END DisplayGTF.
-------------------------
Result on EIZO Flexscan T57S -> -> -> fV= Hz fH= kHz
Aos.Call DisplayGTF.Mode 279 1024 768 16 60 ~ 279 is 117H 59.7 47.8
Aos.Call DisplayGTF.Mode 279 1024 768 16 70 ~ 70.4 56.7
Aos.Call DisplayGTF.Mode 279 1024 768 16 80 ~ 81.3 65.5
Aos.Call DisplayGTF.Mode 279 1024 768 16 100 ~ 104.4 84.1
Aos.Call DisplayGTF.Mode 283 1280 1024 16 80 ~ 279 is 11BH
Default 60 Hz operation example:
Aos.Call DisplayGTF.Mode 279 1024 768 16 0 ~
The values obtained using a VBE setting with Init=117H results in fV= 60.5Hz fH= 48.4kHz
-------------------------
System.Free DisplayGTF ~
Execute Partitions.GetConfig dev#part
then Aos.Call DisplayGTF.Mode parameter-list
Get the Init="..." string with the corresponding resolution values from the Kernel.Log
Example: Aos.Call DisplayGTF.Mode 279 1024 768 16 80 ~
Result in Kernel.Log:
Init="b80b4fbb0000ba174966b9009e3e05cd1026c705580526c74502380426c74504a80426c74506240326c74508010326c7450a040326c6450c042666894d0d26c74511401fb8024fbb1749cd10b8014fb91701cd10268b4d28268b552a"
DWidth="1024"
DHeight="768"
DDepth="16"
IMPORTANT: Make sure to remove any spurious carrier return inserted by the Oberon editor.
Use the [Grow] Button to verify!!!!!
Execute Partitions.SetConfig appearing in the configuration text.
That's all.