-
Notifications
You must be signed in to change notification settings - Fork 2
/
ORL.Mod
283 lines (272 loc) · 13.2 KB
/
ORL.Mod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
MODULE ORL; (*Oberon boot linker/loader for RISC / AP 1.5.23*)
IMPORT SYSTEM, Kernel, Files, Modules, Texts, Oberon;
CONST versionkey = 1X; versionkey0 = 0X; MT = 12; DescSize = 80; MnLength = 32; BootSec = 2; BootSize = 64; FPrint = 12345678H;
noerr* = 0; nofile* = 1; badversion* = 2; badkey* = 3; badfile* = 4; nospace* = 5;
DestAdr = 8; MemAdr = 12; AllocAdr = 16; RootAdr = 20; StackAdr = 24; FPrintAdr = 28; MTAdr = 32; ModAdr = 256;
C4 = 10H; C6 = 40H; C8 = 100H; C10 = 400H; C12 = 1000H; C14 = 4000H; C16 = 10000H;
C18 = 40000H; C20 = 100000H; C22 = 400000H; C24 = 1000000H; BCT = 0E7000000H; BLT = 0F7000000H;
TYPE (*copied from Modules for use as cross linker/loader*)
Module* = POINTER TO ModDesc;
Command* = PROCEDURE;
ModuleName* = ARRAY MnLength OF CHAR;
ModDesc* = RECORD
name*: ModuleName;
next*: Module;
key*, num*, size*, refcnt*: INTEGER;
data*, code*, imp*, cmd*, ent*, ptr*, unused: INTEGER (*addresses*)
END ;
VAR root: Module;
MTOrg, AllocPtr, Start, limit, res*: INTEGER;
importing*, imported*: ModuleName;
W: Texts.Writer;
PROCEDURE EndLine;
BEGIN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
END EndLine;
PROCEDURE MakeFileName(VAR FName: ARRAY OF CHAR; name, ext: ARRAY OF CHAR);
VAR i, j: INTEGER;
BEGIN i := 0; j := 0; (*assume name suffix less than 4 characters*)
WHILE (i < MnLength-5) & (name[i] > 0X) DO FName[i] := name[i]; INC(i) END ;
REPEAT FName[i]:= ext[j]; INC(i); INC(j) UNTIL ext[j] = 0X;
FName[i] := 0X
END MakeFileName;
PROCEDURE ThisFile(name: ARRAY OF CHAR): Files.File;
VAR F: Files.File;
filename: ModuleName;
BEGIN MakeFileName(filename, name, ".rsc"); F := Files.Old(filename); RETURN F
END ThisFile;
PROCEDURE error(n: INTEGER; name: ARRAY OF CHAR);
BEGIN res := n; importing := name
END error;
PROCEDURE check(s: ARRAY OF CHAR; VAR slen: INTEGER); (*slen includes trailing 0X*)
VAR i: INTEGER; ch: CHAR;
BEGIN ch := s[0]; res := 1; i := 0; slen := 0;
IF (ch >= "A") & (ch <= "Z") OR (ch >= "a") & (ch <= "z") THEN
REPEAT INC(i); ch := s[i]
UNTIL ~((ch >= "0") & (ch <= "9") OR (ch >= "A") & (ch <= "Z")
OR (ch >= "a") & (ch <= "z") OR (ch = ".")) OR (i >= MnLength);
IF ch = 0X THEN res := 0; slen := i+1 END
END
END check;
PROCEDURE LinkOne(name: ARRAY OF CHAR; VAR newmod: Module);
(*search module in list; if not found, link module.
res = noerr: already present or linked;
res = nofile: file not available;
res = badversion: bad file version;
res = badkey: key conflict;
res = badfile: corrupted file;
res = nospace: insufficient space*)
VAR mod, impmod: Module;
i, n, key, impkey, mno, nofimps, size: INTEGER;
p, u, v, w: INTEGER; (*addresses*)
ch: CHAR;
body: Command;
fixorgP, fixorgD, fixorgT: INTEGER;
disp, adr, inst, pno, vno, dest, offset: INTEGER;
name1, impname: ModuleName;
F: Files.File; R: Files.Rider;
import: ARRAY 16 OF Module;
BEGIN mod := root; error(noerr, name); nofimps := 0;
WHILE (mod # NIL) & (name # mod.name) DO mod := mod.next END ;
IF mod = NIL THEN (*link*)
check(name, n);
IF res = noerr THEN F := ThisFile(name) ELSE F := NIL END ;
IF F # NIL THEN
Files.Set(R, F, 0); Files.ReadString(R, name1); Files.ReadInt(R, key); Files.Read(R, ch);
Files.ReadInt(R, size); importing := name1;
IF (ch = versionkey) (*regular module*) OR (ch = versionkey0) (*standalone program*) THEN
Files.ReadString(R, impname); (*imports*)
WHILE (impname[0] # 0X) & (res = noerr) DO
Files.ReadInt(R, impkey);
LinkOne(impname, impmod); import[nofimps] := impmod; importing := name1;
IF res = noerr THEN
IF impmod.key = impkey THEN INC(impmod.refcnt); INC(nofimps)
ELSE error(badkey, name1); imported := impname
END
END ;
Files.ReadString(R, impname)
END
ELSE error(badversion, name1)
END
ELSE error(nofile, name)
END ;
IF res = noerr THEN
INC(size, DescSize);
IF AllocPtr + size < limit THEN (*allocate*)
p := AllocPtr; mod := SYSTEM.VAL(Module, p);
AllocPtr := (p + size + 3) DIV 4 * 4; mod.size := AllocPtr - p;
IF root = NIL THEN mod.num := 1 ELSE mod.num := root.num + 1 END ;
mod.next := root; root := mod
ELSE error(nospace, name1)
END
END ;
IF res = noerr THEN (*read file*)
INC(p, DescSize); (*allocate descriptor*)
mod.name := name; mod.key := key; mod.refcnt := 0; mod.unused := 0; i := n;
WHILE i < MnLength DO mod.name[i] := 0X; INC(i) END ;
mod.data := p; (*data*)
SYSTEM.PUT(mod.num * 4 + MTOrg, p); (*module table entry*)
Files.ReadInt(R, n);
WHILE n > 0 DO Files.ReadInt(R, w); SYSTEM.PUT(p, w); INC(p, 4); DEC(n, 4) END ; (*type descriptors*)
Files.ReadInt(R, n);
WHILE n > 0 DO SYSTEM.PUT(p, 0); INC(p, 4); DEC(n, 4) END ; (*variable space*)
Files.ReadInt(R, n);
WHILE n > 0 DO Files.Read(R, ch); SYSTEM.PUT(p, ch); INC(p); DEC(n) END ; (*strings*)
mod.code := p; (*program*)
Files.ReadInt(R, n);
WHILE n > 0 DO Files.ReadInt(R, w); SYSTEM.PUT(p, w); INC(p, 4); DEC(n) END ; (*program code*)
mod.imp := p; (*copy imports*)
i := 0;
WHILE i < nofimps DO
SYSTEM.PUT(p, import[i]); INC(p, 4); INC(i)
END ;
mod.cmd := p; (*commands*) Files.Read(R, ch);
WHILE ch # 0X DO
REPEAT SYSTEM.PUT(p, ch); INC(p); Files.Read(R, ch) UNTIL ch = 0X;
REPEAT SYSTEM.PUT(p, 0X); INC(p) UNTIL p MOD 4 = 0;
Files.ReadInt(R, n); SYSTEM.PUT(p, n); INC(p, 4); Files.Read(R, ch)
END ;
REPEAT SYSTEM.PUT(p, 0X); INC(p) UNTIL p MOD 4 = 0;
mod.ent := p; (*entries*)
Files.ReadInt(R, n);
WHILE n > 0 DO Files.ReadInt(R, w); SYSTEM.PUT(p, w); INC(p, 4); DEC(n) END ;
mod.ptr := p; (*pointer references*)
Files.ReadInt(R, w);
WHILE w >= 0 DO SYSTEM.PUT(p, mod.data + w - Start); INC(p, 4); Files.ReadInt(R, w) END ;
SYSTEM.PUT(p, 0); INC(p, 4);
Files.ReadInt(R, fixorgP); Files.ReadInt(R, fixorgD); Files.ReadInt(R, fixorgT);
Files.ReadInt(R, w); body := SYSTEM.VAL(Command, mod.code + w - Start);
Files.Read(R, ch);
IF ch # "O" THEN mod := NIL; error(badfile, name) END
END ;
IF res = noerr THEN (*fixup of BL*)
adr := mod.code + fixorgP*4;
WHILE adr # mod.code DO
SYSTEM.GET(adr, inst);
mno := inst DIV C20 MOD C4;
pno := inst DIV C12 MOD C8;
disp := inst MOD C12;
SYSTEM.GET(mod.imp + (mno-1)*4, impmod);
SYSTEM.GET(impmod.ent + pno*4, dest); dest := dest + impmod.code;
offset := (dest - adr - 4) DIV 4;
SYSTEM.PUT(adr, (offset MOD C24) + BLT);
adr := adr - disp*4
END ;
(*fixup of LDR/STR/ADD*)
adr := mod.code + fixorgD*4;
WHILE adr # mod.code DO
SYSTEM.GET(adr, inst);
mno := inst DIV C20 MOD C4;
disp := inst MOD C12;
IF mno = 0 THEN (*global*)
SYSTEM.PUT(adr, (inst DIV C24 * C4 + MT) * C20 + mod.num * 4)
ELSE (*import*)
SYSTEM.GET(mod.imp + (mno-1)*4, impmod); v := impmod.num;
SYSTEM.PUT(adr, (inst DIV C24 * C4 + MT) * C20 + v*4);
SYSTEM.GET(adr+4, inst); vno := inst MOD C8;
SYSTEM.GET(impmod.ent + vno*4, offset);
IF ODD(inst DIV C8) THEN offset := offset + impmod.code - impmod.data END ;
SYSTEM.PUT(adr+4, inst DIV C16 * C16 + offset)
END ;
adr := adr - disp*4
END ;
(*fixup of type descriptors*)
adr := mod.data + fixorgT*4;
WHILE adr # mod.data DO
SYSTEM.GET(adr, inst);
mno := inst DIV C24 MOD C4;
vno := inst DIV C12 MOD C12;
disp := inst MOD C12;
IF mno = 0 THEN (*global*) inst := mod.data - Start + vno
ELSE (*import*)
SYSTEM.GET(mod.imp + (mno-1)*4, impmod);
SYSTEM.GET(impmod.ent + vno*4, offset);
inst := impmod.data - Start + offset
END ;
SYSTEM.PUT(adr, inst); adr := adr - disp*4
END ;
SYSTEM.PUT(Start, body) (*module initialization body*)
ELSIF res >= badkey THEN importing := name;
WHILE nofimps > 0 DO DEC(nofimps); DEC(import[nofimps].refcnt) END
END
END ;
newmod := mod
END LinkOne;
PROCEDURE Link*; (*link multiple object files together and create a single boot file M.bin from them*)
VAR i, x: INTEGER;
F: Files.File; R: Files.Rider;
S: Texts.Scanner;
M, p: Module;
name: ModuleName;
BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); res := -1;
IF S.class = Texts.Name THEN
root := NIL; Start := Modules.AllocPtr; MTOrg := Start + MTAdr; AllocPtr := Start + ModAdr; i := Start;
WHILE i < AllocPtr DO SYSTEM.PUT(i, 0); INC(i, 4) END ;
REPEAT LinkOne(S.s, M); Texts.Scan(S) UNTIL (S.class # Texts.Name) OR (res # noerr);
IF res = noerr THEN p := root; Texts.WriteString(W, " linking");
WHILE p # NIL DO (*fixup*) Texts.Write(W, " "); Texts.WriteString(W, p.name);
M := p; p := p.next;
IF p # NIL THEN M.next := SYSTEM.VAL(Module, SYSTEM.VAL(INTEGER, p) - Start) END ;
M.data := M.data - Start;
SYSTEM.PUT(M.num * 4 + MTOrg, M.data); (*module table entry*)
M.code := M.code - Start;
i := M.imp;
WHILE i < M.cmd DO SYSTEM.GET(i, x); SYSTEM.PUT(i, x - Start); INC(i, 4) END ;
M.imp := M.imp - Start;
M.cmd := M.cmd - Start;
M.ent := M.ent - Start;
M.ptr := M.ptr - Start
END ;
SYSTEM.GET(Start, x); (*address of initialization body of the top module relative to Start*)
SYSTEM.PUT(Start, BCT + (x DIV 4) - 1); (*branch instruction to the initialization body of the top module*)
SYSTEM.PUT(Start + DestAdr, 0); (*destination address of the prelinked, executable binary*)
SYSTEM.PUT(Start + MemAdr, 0); (*limit of available memory, typically overwritten by the boot loader*)
SYSTEM.PUT(Start + AllocAdr, AllocPtr - Start); (*address of the end of the module space loaded*)
SYSTEM.PUT(Start + RootAdr, SYSTEM.VAL(INTEGER, root) - Start); (*current root of the links of loaded modules*)
SYSTEM.PUT(Start + StackAdr, 0); (*current limit of the module area, typically overwritten by the boot loader*)
SYSTEM.PUT(Start + FPrintAdr, FPrint); (*fingerprint*)
MakeFileName(name, S.s, ".bin"); F := Files.New(name); Files.Set(R, F, 0); i := Start;
WHILE i < AllocPtr DO SYSTEM.GET(i, x); Files.WriteInt(R, x); INC(i, 4) END ;
Texts.WriteInt(W, AllocPtr - Start, 7); Files.Register(F)
ELSE
Texts.WriteString(W, "Link error: "); Texts.WriteString(W, importing);
IF res = nofile THEN Texts.WriteString(W, " module not found")
ELSIF res = badversion THEN Texts.WriteString(W, " bad version")
ELSIF res = badkey THEN Texts.WriteString(W, " imports ");
Texts.WriteString(W, imported); Texts.WriteString(W, " with bad key")
ELSIF res = badfile THEN Texts.WriteString(W, " corrupted obj file")
ELSIF res = nospace THEN Texts.WriteString(W, " insufficient space")
END
END
ELSE Texts.WriteString(W, "Usage: ORL.Link [module...] topmodule")
END ;
EndLine; root := NIL
END Link;
PROCEDURE Load*; (*load prelinked boot file M.bin onto the boot area of the local disk*)
VAR i, secno: LONGINT; b: BYTE;
F: Files.File; R: Files.Rider;
S: Texts.Scanner;
buf: ARRAY Kernel.SectorLength OF BYTE;
BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); res := -1;
IF S.class = Texts.Name THEN
Texts.WriteString(W, " loading "); Texts.WriteString(W, S.s); F := Files.Old(S.s);
IF F # NIL THEN Texts.WriteString(W, " onto boot area"); Texts.WriteInt(W, Files.Length(F), 7);
secno := BootSec; i := 0; Files.Set(R, F, 0); Files.ReadByte(R, b); res := noerr;
WHILE ~R.eof DO buf[i] := b; INC(i);
IF i = Kernel.SectorLength THEN Kernel.PutSector(secno*29, buf); INC(secno); i := 0 END ;
Files.ReadByte(R, b)
END ;
IF i > 0 THEN
WHILE i < Kernel.SectorLength DO buf[i] := 0; INC(i) END ;
Kernel.PutSector(secno*29, buf); INC(secno)
END ;
FOR i := 0 TO Kernel.SectorLength-1 DO buf[i] := 0 END ;
WHILE secno < BootSize DO Kernel.PutSector(secno*29, buf); INC(secno) END
ELSE Texts.WriteString(W, " not found")
END
ELSE Texts.WriteString(W, "Usage: ORL.Load M.bin")
END ;
EndLine
END Load;
BEGIN Texts.OpenWriter(W); Texts.WriteString(W, "OR Boot linker/loader AP 1.5.23"); EndLine;
limit := Kernel.stackOrg - Kernel.stackSize
END ORL.