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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
|
; AUTHORS: L.Smith, J. Sutton
;
; DATE: Last edit 16-Mar-90 (ECN)
;
; VERSION: 0.07
;
; DESCRIPTION: Overlay Manager
;
; CHANGES: 28-Feb-90 LDS
; Fixed a bug in check_for_invalidated_returns whereby a return
; handler was allocated on return rather than just on call.
;
; 16-Mar-90 ECN
; Use OS_GetEnv to read the overlay directory instead of Obey$Dir
; which doesn't work if the image is not executed from an obey file.
;
; Tidied up the behaviour of "Disk not present errors". The previous
; version just splatted a message all over the desktop. The new
; version only prompt for disk insertion if executing outside the
; desktop (inside the desktop the wimp will prompt the user).
;
; Tidied up the generation of errors and exit from application.
; Previously either gave a trap or stiffed the machine.
;
;;; Copyright (C) Advanced RISC Machines Ltd., 1991
EXPORT |Image$$overlay_init|
EXPORT |Image$$load_seg|
; pointers to start and end of workspace area supplied by the linker
IMPORT |Overlay$$Data$$Base|
IMPORT |Overlay$$Data$$Limit|
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT MemCopy
IMPORT LoadOverlaySegment
IMPORT SevereErrorHandler, WEAK
ZeroInitCodeOffset EQU 64
; Why does the linker need to generate this zero init area, why can't the
; Overlay Manager define it itself??? ECN.
;
; Layout of workspace allocated by the linker pointed to by Overlay$$Data
; This area is automatically zero-initialised AFTER overlay_init is called
; offsets are prefixed with Work_
^ 0
Work_HStack # 4 ; top of stack of allocated handlers
Work_HFree # 4 ; head of free-list
Work_RSave # 9*4 ; for R0-R8
Work_LRSave # 4 ; saved lr
Work_PCSave # 4 ; saved PC
Work_PSRSave # 4 ; saved CPSR
Work_ReturnHandlersArea EQU @ ; rest of this memory is treated as heap
; space for the return handlers
Work_MinSize EQU @ + 32 * RHandl_Size
; Return handler. 1 is allocated per inter-segment procedure call
; allocated and free lists of handlers are pointed to from HStack and HFree
; offsets are prefixed with RHandl_
^ 0
RHandl_Branch # 4 ; BL load_seg_and_ret
RHandl_RealLR # 4 ; space for the real return address
RHandl_Segment # 4 ; -> PCIT section of segment to load
RHandl_Link # 4 ; -> next in stack order
RHandl_Size EQU @
; set up by check_for_invalidated_returns.
; PCITSection. 1 per segment stored in root segment, allocated by linker
; offsets are prefixed with PCITSect_
^ 0
PCITSect_Vecsize # 4 ; .-4-EntryV ; size of entry vector
PCITSect_Base # 4 ; used by load_segment; not initialised
PCITSect_Limit # 4 ; used by load_segment; not initialised
PCITSect_Name # 11 ; <11 bytes> ; 10-char segment name + NUL in 11 bytes
PCITSect_Flags # 1 ; ...and a flag byte
PCITSect_ClashSz # 4 ; PCITEnd-.-4 ; size of table following
PCITSect_Clashes # 4 ; >table of pointers to clashing segments
; Stack structure (all offsets are negative)
; defined in procedure call standard
; offsets are prefixed with Stack_
^ 0
Stack_SaveMask # -4
Stack_LRReturn # -4
Stack_SPReturn # -4
Stack_FPReturn # -4
; the code and private workspace area
AREA OverLayMgrArea, PIC, CODE , READONLY
STRLR STR lr, [pc, #-8] ; a word that is to be matched in PCITs
; Store 2 words which are the addresses of the start and end of the workspace
WorkSpace DCD |Overlay$$Data$$Base|
WorkSpaceEnd DCD |Overlay$$Data$$Limit|
InitFlag DCD InitDoneFlag
|Image$$overlay_init| ROUT
; Initialise overlay manager.
; In the AIF format this is is called from offset 8 in header. This routine has
; to pass control to the zero initialisation code.
; In Non AIF formats this routine has to be called explicitly e.g. from
; a customised rtstand.s .
;
; In the AIF example the entire root segment is copied to the load address.
;
; In the non AIF example only the RW part of the root is copied.
;
LDR ip,WorkSpace
IF :DEF:AIF_EXAMPLE
STR lr,[ip,#Work_LRSave]
ADD ip,ip,#Work_RSave
STMIA ip,{r0-r3}
;
; Look at the AIF header and add compute the total size of the AIF header
;
SUB r1,lr,#0xC
LDR r0,[r1,#0x28]
LDR r2,[r1,#0x14]
LDR r3,[r1,#0x18]
ADD r2,r2,r3
LDR r3,[r1,#0x1C]
ADD r2,r2,r3
;
; A system specific Memory Copy call. r0 is the destination area.
; r1 is the source area.
; r2 is the block size in bytes.
;
BL MemCopy
LDR ip,WorkSpace
LDR lr,[ip,#Work_LRSave]
ADD ip,ip,#Work_RSave
SUB lr,lr,#12
LDR lr,[lr,#0x28]
ADD lr,lr,#12
LDMIA ip,{r0-r3}
;
; Return to the Zero Initialisation Code in the AIF header.
;
ADD pc,lr,#ZeroInitCodeOffset-12
ELSE
LDR ip,WorkSpace
ADD ip,ip,#Work_RSave
STMIA ip,{r0-r2,lr}
LDR r0,=|Image$$RW$$Base|
LDR r1,=|Image$$RO$$Limit|
LDR r2,=|Image$$ZI$$Base|
SUB r2,r2,r0
BL MemCopy
LDR ip,WorkSpace
ADD ip,ip,#Work_RSave
LDMIA ip,{r0-r2,PC}
ENDIF
DCD 0 ; Not needed for a real system but needed when using ARMSD
; is used to simulate a ROMN based system.
; entry point
|Image$$load_seg| ROUT
; called when segment has been called but is not loaded
; presume ip is corruptible by this
LDR ip, WorkSpace
ADD ip, ip, #Work_RSave
STMIA ip, {r0-r8} ; save working registers
MRS r4, CPSR ; Save status register -it'll get stored in the
; workspace later.
; (save in my workspace because stack is untouchable during procedure call)
LDR r0, InitFlag
LDRB r1, [r0]
CMP r1, #0
BNE InitDone
;Initialise Return Handlers on first call to this routine
MOV r1, #1
STRB r1, [r0] ; set InitDone flag
LDR r0, WorkSpace
; r0 points to workspace
; corrupts r0-r3,lr
; create and initialise return handler linked list
MOV r2, #0
STR r2, [r0, #Work_HStack] ; initialise start of handler list with NULL
ADD r1, r0, #Work_ReturnHandlersArea ; Start of heap space
STR r1, [r0, #Work_HFree] ; Start of list of free handlers point to heap space
LDR r0, WorkSpaceEnd ; for test in loop to make sure..
SUBS r0, r0, #RHandl_Size ; ..I dont overrun in init
01 ADD r3, r1, #RHandl_Size ; next handler
; set up link to point to next handler (in fact consecutive locations)
STR r3, [r1, #RHandl_Link]
MOV r1, r3 ; next handler
CMP r1, r0 ; test for end of workspace
BLT %BT01
SUB r1, r1, #RHandl_Size ; previous handler
STR r2, [r1, #RHandl_Link] ; NULL-terminate list
InitDone
LDR r3, WorkSpace
STR r4, [r3, #Work_PSRSave] ; CPSR read into R4 before the InitDone
; test.
MOV r8,lr ;
LDR r0, [r8, #-8] ; saved r14... (is end of PCIT)
STR r0, [r3, #Work_LRSave] ; ...save it here ready for retry
LDR r0, STRLR ; look for this...
SUB r1, r8, #8 ; ... starting at last overwrite
01 LDR r2, [r1, #-4]!
CMP r2, r0 ; must stop on guard word...
BNE %B01
ADD r1, r1, #4 ; gone one too far...
STR r1, [r3, #Work_PCSave] ; where to resume at
load_segment
; ip -> the register save area; r8 -> the PCIT section of the segment to load.
; First re-initialise the PCIT section (if any) which clashes with this one...
ADD r1, r8, #PCITSect_Clashes
LDR r0, [r8, #PCITSect_ClashSz]
01 SUBS r0, r0, #4
BLT Done_Reinit ; nothing left to do
LDR r7, [r1], #4 ; a clashing segment...
LDRB r2, [r7, #PCITSect_Flags] ; its flags (0 if unloaded)
CMPS r2, #0 ; is it loaded?
BEQ %B01 ; no, so look again
; clashing segment is loaded (clearly, there can only be 1 such segment)
; mark it as unloaded and reinitialise its PCIT
; r7 -> PCITSection of clashing loaded segment
MOV r0, #0
STRB r0, [r7, #PCITSect_Flags] ; mark as unloaded
LDR r0, [r7, #PCITSect_Vecsize]
SUB r1, r7, #4 ; end of vector
LDR r2, STRLR ; init value to store in the vector...
02 STR r2, [r1, #-4]! ;>
SUBS r0, r0, #4 ;> loop to initialise the PCIT segment
BGT %B02 ;>
; Now we check the chain of call frames on the stack for return addresses
; which have been invalidated by loading this segment and install handlers
; for each invalidated return.
; Note: r8 identifies the segment being loaded; r7 the segment being unloaded.
BL check_for_invalidated_returns
Done_Reinit
; All segment clashes have now been dealt with, as have the re-setting
; of the segment-loaded flags and the intercepting of invalidated returns.
; So, now load the required segment.
Retry
;
; Call a routine to load the overlay segment.
; First parameter is the length of the segment name.
; The second parameter is the address of the segment name
; The third parameter is the base address of the segement.
; The routine returns the segment length in r0.
;
MOV r0,#12
ADD r1, r8, #PCITSect_Name
LDR r2, [ r8, #PCITSect_Base]
BL LoadOverlaySegment
TEQ r0,#0
MOVEQ r0,#2
BEQ SevereErrorHandler
LDR ip,WorkSpace
ADD ip,ip,#Work_RSave
;
; Mark the segment as loaded.
;
MOV r1,#1
STRB r1, [r8, #PCITSect_Flags]
LDR r2, [r8, #PCITSect_Base]
ADD r0, r2, r0 ; start + length = end of file
; The segment's entry vector is at the end of the segment...
; ...copy it to the PCIT section identified by r8.
LDR r1, [r8, #PCITSect_Vecsize]
SUB r3, r8, #8 ; end of entry vector...
MOV r4, #0 ; for data initialisation
01 LDR r2, [r0, #-4]! ;>loop to copy
STR r4, [r0] ; (zero-init possible data section)
STR r2, [r3], #-4 ;>the segment's PCIT
SUBS r1, r1, #4 ;>section into the
BGT %B01 ;>global PCIT
; Finally, continue, unabashed...
LDR r3, WorkSpace
LDR r3, [r3,#Work_PSRSave]
MSR CPSR,r3
LDMIA ip, {r0-r8, lr, pc}
load_seg_and_ret
; presume ip is corruptible by this
LDR ip, WorkSpace
ADD ip, ip, #Work_RSave
STMIA ip, {r0-r8} ; save working registers
; (save in my workspace because stack is untouchable during procedure call)
LDR r3, WorkSpace
MRS r8, CPSR
STR r8, [r3, #Work_PSRSave]
; lr points to the return handler
MOV r8, lr
; load return handler fields RealLR, Segment, Link
LDMIA r8, {r0, r1, r2}
SUB r8, r8, #4 ; point to true start of return handler before BL
STR r0, [r3, #Work_LRSave]
STR r0, [r3, #Work_PCSave]
; Now unchain the handler and return it to the free pool
; HStack points to this handler
LDR r0, [r3, #Work_HStack]
CMPS r0, r8
MOVNE r0, #1
BNE SevereErrorHandler
STR r2, [r3, #Work_HStack] ; new top of handler stack
LDR r2, [r3, #Work_HFree]
STR r2, [r8, #RHandl_Link] ; Link -> old HFree
STR r8, [r3, #Work_HFree] ; new free list
MOV r8, r1 ; segment to load
B load_segment
check_for_invalidated_returns
; Note: r8 identifies the segment being loaded; r7 the segment being unloaded.
; Note: check for returns invalidated by a call NOT for returns invalidated by
; a return! In the 2nd case, the saved LR and saved PC are identical.
LDR r5, WorkSpace
ADD r6, r5, #Work_LRSave ; 1st location to check
LDMIA r6, {r0, r1} ; saved LR & PC
CMPS r0, r1
MOVEQ pc, lr ; identical => returning...
MOV r0, fp ; temporary FP...
01 LDR r1, [r6] ; the saved return address...
LDR r2, [r5, #Work_HStack] ; top of handler stack
CMPS r1, r2 ; found the most recent handler, so
MOVEQ pc, lr ; abort the search
LDR r2, [r7, #PCITSect_Base]
CMPS r1, r2 ; see if >= base...
BLT %F02
LDR r2, [r7, #PCITSect_Limit]
CMPS r1, r2 ; ...and < limit ?
BLT FoundClash
02 CMPS r0, #0 ; bottom of stack?
MOVEQ pc, lr ; yes => return
ADD r6, r0, #Stack_LRReturn
LDR r0, [r0, #Stack_FPReturn] ; previous FP
B %B01
FoundClash
LDR r0, [r5, #Work_HFree] ; head of chain of free handlers
CMPS r0, #0
MOVEQ r0, #2
BEQ SevereErrorHandler
; Transfer the next free handler to head of the handler stack.
LDR r1, [r0, #RHandl_Link] ; next free handler
STR r1, [r5, #Work_HFree]
LDR r1, [r5, #Work_HStack] ; the active handler stack
STR r1, [r0, #RHandl_Link]
STR r0, [r5, #Work_HStack] ; now with the latest handler linked in
; Initialise the handler with a BL load_seg_and_ret, RealLR and Segment.
ADR r1, load_seg_and_ret
SUB r1, r1, r0 ; byte offset for BL in handler
SUB r1, r1, #8 ; correct for PC off by 8
MOV r1, r1, ASR #2 ; word offset
BIC r1, r1, #&FF000000
ORR r1, r1, #&EB000000 ; code for BL
STR r1, [r0, #RHandl_Branch]
LDR r1, [r6] ; LRReturn on stack
STR r1, [r0, #RHandl_RealLR] ; RealLR
STR r0, [r6] ; patch stack to return to handler
STR r7, [r0, #RHandl_Segment] ; segment to re-load on return
MOV pc, lr ; and return
AREA OverlayInit, DATA
InitDoneFlag DCD 0
END
|