Initial revision
[ssr] / StraySrc / Libraries / Sapphire / xfer / s / xsave
1 ;
2 ; xfer.xsave.s
3 ;
4 ; Simplified saving with coroutines (MDW)
5 ;
6 ; © 1994 Straylight
7 ;
8
9 ;----- Standard header ------------------------------------------------------
10
11 GET libs:header
12 GET libs:swis
13
14 ;----- External dependencies ------------------------------------------------
15
16 GET sapphire:alloc
17 GET sapphire:coRoutine
18 GET sapphire:fastMove
19 GET sapphire:sapphire
20
21 ;----- How it all works -----------------------------------------------------
22 ;
23 ; xsave attempts to unify the two saving routines you have to give (the saver
24 ; and the sender) and provide the same interface to both. For data sending
25 ; through RAM transfer, this has to be done using coroutines, but this is
26 ; fairly invisible to you.
27 ;
28 ; There are two main routines, xsave_save and xsave_send which start save
29 ; jobs. They take a pointer to a save routine with R10 and R12 pointers.
30 ; xsave_save also takes a pointer to a filename. The actual save routine
31 ; should be the same for both.
32 ;
33 ; Saving of data is done with the call xsave_block. This just writes out
34 ; a block of data somehow. All the other routines are special interfaces to
35 ; xsave_block.
36
37 ;----- How the error handling works -----------------------------------------
38 ;
39 ; The really tricky bit is passing of errors during a RAM transfer.
40 ; Errors can be returned at two places -- in the user code (e.g. running out
41 ; of memory) and by save's cunning message handling if the receiver dies.
42 ; All errors must be handled by user code, to release claimed memory etc.
43 ;
44 ; The error gets passed along a path like this:
45 ;
46 ; Error created by user code
47 ;
48 ; user code --> xsave__startCo --> xsave_failed
49 ;
50 ; Error created by remote receiver
51 ;
52 ; xsave_failed --> xsave__write --> user code --> xsave__startCo
53
54 ;----- Main code ------------------------------------------------------------
55
56 AREA |Sapphire$$Code|,CODE,READONLY
57
58 ; --- xsave_save ---
59 ;
60 ; On entry: R0 == pointer to saver routine
61 ; R1 == R10 value to pass to saver
62 ; R2 == R12 value to pass to saver
63 ; R3 == pointer to filename to save to
64 ; R4 == filetype of file to save
65 ;
66 ; On exit: May return an error
67 ;
68 ; Use: Calls a generalised saver routine to write data to a file.
69
70 EXPORT xsave_save
71 xsave_save ROUT
72
73 STMFD R13!,{R0-R5,R10,R12,R14} ;Save lots of registers
74 WSPACE xsave__wSpace ;Find my workspace address
75
76 ; --- First, try to allocate a buffer ---
77
78 BL xsave__setBuff ;Allocate the buffer nicely
79 BVS %99xsave_save ;If we couldn't, tidy up
80
81 ; --- Set up the file for writing ---
82
83 MOV R1,R3 ;Point at the file name
84 MOV R0,#&8F ;Lots of errors, no path
85 SWI XOS_Find ;Try to open the file
86 BVS %98xsave_save ;Tidy up if it wouldn't open
87 STR R0,xsave__file ;Save the file handle away
88
89 ; --- Set up flags and let rip ---
90
91 LDR R14,xsave__flags ;Load my current flags word
92 AND R14,R14,#xsFlag__inited ;Only leave inited flag
93 STR R14,xsave__flags ;Save the flags back again
94
95 LDMIA R13,{R0,R10,R12} ;Load the arguments out
96 ADDS R0,R0,#0 ;Clear overflow and carry
97 MOV R14,PC ;Set up the return address
98 MOV PC,R0 ;And call the saver routine
99 WSPACE xsave__wSpace ;Restore my workspace ptr
100 BVS %97xsave_save ;Tidy up if it failed
101
102 ; --- Write the remainder of the file ---
103
104 ADR R14,xsave__buffer ;Point to the buffer info
105 LDMIA R14,{R0,R1} ;Load buffer addr and size
106 BL xsave__write ;Write it out to the file
107 BVS %97xsave_save ;Tidy up if it failed
108
109 ; --- Close the file and set the filetype ---
110
111 MOV R0,#0 ;Close an open file
112 LDR R1,xsave__file ;Get the file handle ready
113 SWI OS_Find ;Close it
114
115 MOV R0,#18 ;Set file type
116 MOV R1,R3 ;Point to the filename
117 MOV R2,R4 ;And get the filetype
118 SWI OS_File ;And set the filetype
119
120 ; --- Get rid of the buffer and return ---
121
122 BL xsave__loseBuff ;Free up the buffer space
123 LDMFD R13!,{R0-R5,R10,R12,R14} ;Unstack all the registers
124 BICS PC,R14,#V_flag ;And return to caller
125
126 ; --- Tidy up after various catastrophes ---
127
128 97xsave_save MOV R10,R0 ;Keep the error pointer
129 MOV R0,#0 ;Close an open file
130 LDR R1,xsave__file ;Get the file handle ready
131 SWI OS_Find ;Close it
132 MOV R0,#6 ;Delete the file now
133 MOV R1,R3 ;Point to the filename
134 SWI XOS_File ;Delete it as much as we can
135 MOV R0,R10 ;Restore the error pointer
136
137 98xsave_save BL xsave__loseBuff ;Free up the buffer space
138
139 99xsave_save ADD R13,R13,#4 ;Don't restore R0 on exit
140 LDMFD R13!,{R1-R5,R10,R12,R14} ;Unstack all the registers
141 ORRS PC,R14,#V_flag ;And return to caller
142
143 ; --- xsave_send ---
144 ;
145 ; On entry: R0 == pointer to saver routine
146 ; R1 == R10 value to pass to saver
147 ; R2 == R12 value to pass to saver
148 ;
149 ; On exit: R0 == pointer to block to send
150 ; R1 == size of block
151 ; CS if this is the last block, else CC
152 ; May return an error
153 ;
154 ; Use: Calls a generalised saver routine to write data to another
155 ; application, using RAM transfer. Note that you must call
156 ; this routine from your send entry point throughout the
157 ; save operation.
158
159 EXPORT xsave_send
160 xsave_send ROUT
161
162 STMFD R13!,{R0-R4,R12,R14} ;Save some registers away
163 WSPACE xsave__wSpace ;Find my workspace address
164
165 ; --- Find out if we need to set anything up ---
166
167 LDR R14,xsave__flags ;Load my flags word
168 TST R14,#xsFlag__ramTran ;Are we doing RAM transfer?
169 BNE %50xsave_send ;Yes -- continue te job then
170
171 ; --- Clear all the flags for now ---
172
173 AND R4,R14,#xsFlag__inited ;Leave only `initialised'
174 STR R4,xsave__flags ;And save these flags back
175
176 ; --- Set things up for RAM transfer ---
177
178 BL xsave__setBuff ;Set up my buffer nicely
179 BVS %99xsave_send ;If it failed, tidy up
180
181 ADR R0,xsave__startCo ;Point to my coroutine
182 MOV R1,R13 ;Point R10 at the stack
183 MOV R2,R12 ;Pass workspace in R12
184 MOV R3,#0 ;Use a default stack
185 BL coRout_create ;Create a coroutine
186 BVS %98xsave_send ;If it failed, tidy up
187 STR R0,xsave__coRout ;Save the coroutine handle
188
189 ; --- Set up the flags properly now ---
190
191 ORR R4,R4,#xsFlag__ramTran ;Say we're RAM transfering
192 STR R4,xsave__flags ;And save these flags back
193
194 ; --- Get some more data to send ---
195
196 50xsave_send LDR R0,xsave__coRout ;Get the coroutine handle
197 BL coRout_switch ;Switch to it for a bit
198
199 LDR R14,xsave__flags ;Get the newly updated flags
200 TST R14,#xsFlag__error ;Was there an error?
201 LDRNE R0,xsave__error ;Yes -- load the error ptr
202 BNE %99xsave_send ;And tidy everything up
203
204 ; --- Get the returned block ---
205
206 ADR R0,xsave__start ;Point to the block info
207 LDMIA R0,{R0,R1} ;Load the start and length
208 TST R14,#xsFlag__finish ;Has the transfer finished?
209 ADD R13,R13,#8 ;Don't restore R0,R1
210 LDMFD R13!,{R2-R4,R12,R14} ;Unstack lots of registers
211 ORRNES PC,R14,#C_flag ;If finished, set C flag
212 BICEQS PC,R14,#C_flag ;Otherwise say more to come
213
214 ; --- Handle errors from various things ---
215
216 98xsave_send BL xsave__loseBuff ;Close the buffer
217
218 99xsave_send ADD R13,R13,#4 ;Don't restore R0 on exit
219 LDMFD R13!,{R1-R4,R12,R14} ;Unstack lots of registers
220 ORRS PC,R14,#V_flag ;And return the error
221
222 LTORG
223
224 ; --- xsave__startCo ---
225 ;
226 ; On entry: R0 == my coroutine handle
227 ; R10 == pointer to block containing (routine,R10,R12)
228 ; R12 == my workspace
229 ;
230 ; On exit: Returns through coRout_end
231 ;
232 ; Use: Starts the coroutine for sending data by RAM transfer. It
233 ; handles errors reported by it, and terminates the transfer
234 ; properly.
235
236 xsave__startCo ROUT
237
238 LDMIA R10,{R0,R10,R12} ;Load the caller's registers
239 MOV R14,PC ;Set up a return address
240 MOV PC,R0 ;And call the saver routine
241
242 ; --- Handle its return values ---
243
244 WSPACE xsave__wSpace ;Locate my workspace address
245 LDR R14,xsave__flags ;Load the flags word
246 BVS %90xsave__startCo ;If error, handle it nicely
247
248 ; --- Just return the current buffer state ---
249
250 ORR R14,R14,#xsFlag__finish ;Set the finished flag
251 STR R14,xsave__flags ;Save the flags back again
252 ADR R14,xsave__buffer ;Point to the buffer info
253 LDMIA R14,{R0,R1} ;Load the start and size info
254 ADR R14,xsave__start ;Point to return information
255 STMIA R14,{R0,R1} ;Save this to be sent next
256 B coRout_end ;And terminate the coroutine
257
258 90 ORR R14,R14,#xsFlag__error ;Set the main error flag
259 STR R14,xsave__flags ;Save the flags back again
260 STR R0,xsave__error ;And save the error pointer
261 B coRout_end ;And finish the coroutine
262
263 LTORG
264
265 ; --- xsave_done ---
266 ;
267 ; On entry: --
268 ;
269 ; On exit: --
270 ;
271 ; Use: Tidies up after a successful save job.
272
273 EXPORT xsave_done
274 xsave_done ROUT
275
276 STMFD R13!,{R12,R14} ;Save some registers
277 WSPACE xsave__wSpace ;Find my workspace
278 LDR R14,xsave__flags ;Load my flags word
279 TST R14,#xsFlag__ramTran ;Are we using RAM transfer?
280 BLNE xsave__loseBuff ;Yes -- destroy the buffer
281 LDMFD R13!,{R12,PC}^ ;Return to caller
282
283 LTORG
284
285 ; --- xsave_failed ---
286 ;
287 ; On entry: R0 == pointer to error block
288 ;
289 ; On exit: --
290 ;
291 ; Use: Tidies up a RAM transfer after an error.
292
293 EXPORT xsave_failed
294 xsave_failed ROUT
295
296 STMFD R13!,{R12,R14} ;Save some registers
297 WSPACE xsave__wSpace ;Find my workspace
298 LDR R14,xsave__flags ;Load my flags word
299 TST R14,#xsFlag__ramTran ;Are we using RAM transfer?
300 LDMEQFD R13!,{R12,PC}^ ;No -- return right now then
301
302 ; --- Find out if the user has seen the error yet ---
303 ;
304 ; We also don't want to inform the user if the save is
305 ; complete -- his coroutine will already be destroyed.
306
307 STMFD R13!,{R0} ;Save some more registers
308 TST R14,#xsFlag__finish :OR: xsFlag__error
309 BNE %30xsave_failed ;Yes -- then don't switch
310
311 ; --- Get xsave__write to return an error ---
312
313 ORR R14,R14,#xsFlag__error ;Set the error flag
314 STR R0,xsave__error ;And save the error pointer
315 LDR R0,xsave__coRout ;Load the coroutine handle
316 BL coRout_switch ;And switch to it
317
318 ; --- Now tidy everything up ---
319 ;
320 ; The coroutine should have killed itself by now
321
322 30xsave_failed BL xsave__loseBuff ;Kill off the data buffer
323 LDMFD R13!,{R0,R12,PC}^ ;Return to caller
324
325 LTORG
326
327 ; --- xsave__setBuff ---
328 ;
329 ; On entry: --
330 ;
331 ; On exit: May return an error
332 ;
333 ; Use: Sets up the buffer for a data transfer.
334
335 xsave__setBuff ROUT
336
337 STMFD R13!,{R0-R2,R14} ;Save some registers
338
339 ; --- Allocate the buffer memory ---
340
341 MOV R0,#xsave__buffSize ;Get the buffer size
342 BL alloc ;Try to allocate the buffer
343 BLCS alloc_error ;Get an error if it failed
344 BCS %90xsave__setBuff ;And tidy things up
345
346 ; --- Set up los of variables ---
347
348 ADR R14,xsave__buffer ;Point at the buffer info
349 MOV R1,#0 ;No buffer used yet
350 MOV R2,#0 ;No data sent yet either
351 STMIA R14,{R0-R2} ;Save these values away
352 LDMFD R13!,{R0-R2,R14} ;Restore all the registers
353 BICS PC,R14,#V_flag ;And return with V clear
354
355 ; --- Report an error ---
356
357 90 ADD R13,R13,#4 ;Don't restore R0 on exit
358 LDMFD R13!,{R1,R2,R14} ;Restore all the registers
359 ORRS PC,R14,#V_flag ;And return with V set
360
361 LTORG
362
363 ; --- xsave__loseBuff ---
364 ;
365 ; On entry: --
366 ;
367 ; On exit: --
368 ;
369 ; Use: Kills off the buffer.
370
371 xsave__loseBuff ROUT
372
373 STMFD R13!,{R0,R14} ;Save some registers
374 LDR R0,xsave__buffer ;Load the buffer address
375 BL free ;Free the buffer
376 LDR R14,xsave__flags ;Load my current flags word
377 AND R14,R14,#xsFlag__inited ;Only leave inited flag
378 STR R14,xsave__flags ;Save the flags back again
379 LDMFD R13!,{R0,PC}^ ;And return to caller
380
381 LTORG
382
383 ; --- xsave_byte ---
384 ;
385 ; On entry: R0 == byte to write in lowest 8 bits
386 ;
387 ; On exit: May return an error
388 ;
389 ; Use: Writes a single byte to the current output.
390
391 EXPORT xsave_byte
392 xsave_byte ROUT
393
394 STMFD R13!,{R0,R1,R14} ;Save some registers
395 MOV R0,R13 ;Point to saved byte
396 MOV R1,#1 ;Just write one byte
397 BL xsave_block ;Send it to the output
398 STRVS R0,[R13,#0] ;Save any error pointers
399 LDMFD R13!,{R0,R1,PC} ;And return to caller
400
401 LTORG
402
403 ; --- xsave_word ---
404 ;
405 ; On entry: R0 == word to write
406 ;
407 ; On exit: May return an error
408 ;
409 ; Use: Writes a single word to the current output.
410
411 EXPORT xsave_word
412 xsave_word ROUT
413
414 STMFD R13!,{R0,R1,R14} ;Save some registers
415 MOV R0,R13 ;Point to saved word
416 MOV R1,#4 ;Just write one word
417 BL xsave_block ;Send it to the output
418 STRVS R0,[R13,#0] ;Save any error pointers
419 LDMFD R13!,{R0,R1,PC} ;And return to caller
420
421 LTORG
422
423 ; --- xsave_string ---
424 ;
425 ; On entry: R0 == pointer to a control-terminated string
426 ;
427 ; On exit: May return an error
428 ;
429 ; Use: Writes a control-terminated string to the current output.
430 ; The string is null terminated in the output file.
431
432 EXPORT xsave_string
433 xsave_string ROUT
434
435 STMFD R13!,{R0,R14} ;Save some registers
436 00xsave_string LDRB R14,[R0],#1 ;Load a byte from the string
437 CMP R14,#32 ;Is it a control character?
438 MOVLO R14,#0 ;Yes -- make it a zero then
439 BL xsave_byte ;Write it out
440 BVS %90xsave_string ;If it failed, return error
441 BHS %00xsave_string ;Otherwise go round again
442 LDMFD R13!,{R0,R14} ;Restore the registers
443 BICS PC,R14,#V_flag ;And don't return an error
444
445 90xsave_string ADD R13,R13,#4 ;Don't restore R0 on exit
446 LDMFD R13!,{R14} ;Restore the link register
447 ORRS PC,R14,#V_flag ;And return an error
448
449 LTORG
450
451 ; --- xsave_block ---
452 ;
453 ; On entry: R0 == pointer to buffer to write
454 ; R1 == size of buffer to write
455 ;
456 ; On exit: May return an error
457 ;
458 ; Use: Writes out a block of data. Data is buffered, so this is
459 ; fairly quick for reading small objects. Large data blocks
460 ; are sent directly to avoid buffering overhead.
461
462 EXPORT xsave_block
463 xsave_block ROUT
464
465 CMP R1,#0 ;Is there anything there?
466 BICEQS PC,R14,#V_flag ;No -- return with no error
467
468 STMFD R13!,{R0-R4,R12,R14} ;Save a load of registers
469 WSPACE xsave__wSpace ;Load my workspace address
470
471 ; --- Fix up the total data sent ---
472
473 LDR R14,xsave__total ;Load the total sent so far
474 ADD R14,R14,R1 ;Add on the size of this one
475 STR R14,xsave__total ;And save the total back
476
477 ; --- Work out what we have to do ---
478
479 CMP R1,#xsave__buffSize ;Is the block really big?
480 BGE %50xsave_block ;Yes -- then deal with that
481
482 ; --- Write as much as possible to the buffer ---
483
484 ADR R14,xsave__buffer ;Point to buffer data
485 LDMIA R14,{R2,R3} ;Load address and size
486 RSB R4,R3,#xsave__buffSize ;How much is free in there?
487 CMP R1,R4 ;Is there enough space?
488 BLT %30xsave_block ;Yes -- just add some more
489
490 ; --- Fill the buffer right up now ---
491
492 MOV R1,R0 ;Point to the new block
493 ADD R0,R2,R3 ;Point to free part of buffer
494 MOV R2,R4 ;And get the size to copy
495 BL fastMove ;Copy it all over then
496 LDR R0,xsave__buffer ;Point to the buffer
497 MOV R1,#xsave__buffSize ;And get its full size
498 BL xsave__write ;Write a bufferfull out
499 BVS %99xsave_block ;Handle an error from this
500
501 ; --- Fix up all the pointers ---
502
503 LDMIA R13,{R0,R1} ;Reload the buffer addresses
504 ADD R0,R0,R2 ;Move on the block pointer
505 SUB R1,R1,R2 ;And chop off correctly
506 LDR R2,xsave__buffer ;Point to the buffer start
507 MOV R3,#0 ;And now it's empty
508
509 ; --- Just top up the buffer then ---
510
511 30xsave_block MOV R14,R0 ;Point to client's buffer
512 ADD R0,R2,R3 ;Point to free part of buff
513 MOVS R2,R1 ;Get the block size to copy
514 MOV R1,R14 ;Set up source pointer now
515 BLNE fastMove ;Do the copy job now
516 ADD R3,R3,R2 ;Add on the block size
517 STR R3,xsave__buffUsed ;Save the new buffer size
518 B %90xsave_block ;And finish it all up
519
520 ; --- Send a really big block ---
521
522 50xsave_block ADR R14,xsave__buffer ;Point to buffer info
523 LDMIA R14,{R0,R1} ;Load the buffer stuff
524 BL xsave__write ;Write it all out
525 BVS %99xsave_block ;Handle an error from this
526 MOV R14,#0 ;Nothing left there now
527 STR R14,xsave__buffUsed ;So clear the buffer size
528 LDMIA R13,{R0,R1} ;Get caller's block size
529 BL xsave__write ;Write it out nicely
530 BVS %99xsave_block ;Handle an error from this
531
532 90xsave_block LDMFD R13!,{R0-R4,R12,R14} ;Restore all the registers
533 BICS PC,R14,#V_flag ;And return no errors
534
535 99xsave_block ADD R13,R13,#4 ;Don't restore R0 on exit
536 LDMFD R13!,{R1-R4,R12,R14} ;Restore all the registers
537 ORRS PC,R14,#V_flag ;And return the error
538
539 LTORG
540
541 ; --- xsave__write ---
542 ;
543 ; On entry: R0 == pointer to buffer to write
544 ; R1 == size of buffer to write
545 ;
546 ; On exit: May return an error
547 ;
548 ; Use: Writes a block of data to the file or application. This is
549 ; one of the few bits of save/send dependent code in the
550 ; module.
551
552 xsave__write ROUT
553
554 CMP R1,#0 ;Is there anything there?
555 BICEQS PC,R14,#V_flag ;No -- return with no error
556
557 STMFD R13!,{R14} ;Save some registers
558 LDR R14,xsave__flags ;Find my flags word
559 TST R14,#xsFlag__ramTran ;Am I using RAM transfer?
560 BNE %50xsave__write ;Yes -- do that then
561
562 ; --- Use OS_HeebieJeebie to send the data ---
563
564 STMFD R13!,{R0-R4} ;Save some more registers
565 MOV R2,R0 ;Point to the user's buffer
566 MOV R3,R1 ;And get the buffer size
567 LDR R1,xsave__file ;Get the file's handle
568 MOV R0,#2 ;Write at current position
569 SWI XOS_GBPB ;Write the data out nicely
570 STRVS R0,[R13,#0] ;If error, save error pointer
571 LDMFD R13!,{R0-R4,R14} ;Unstack the registers
572 ORRVSS PC,R14,#V_flag ;Return V set as required
573 BICVCS PC,R14,#V_flag
574
575 ; --- Get the main coroutine to send the buffer ---
576
577 50xsave__write STMFD R13!,{R0} ;Save another register
578 ADR R14,xsave__start ;Point to the buffer info
579 STMIA R14,{R0,R1} ;Save for main coroutine
580 MOV R0,#0 ;Get magic coroutine handle
581 BL coRout_switch ;And switch to main code
582 LDR R14,xsave__flags ;Reload the flags
583 TST R14,#xsFlag__error ;Was there a problem?
584 LDMFD R13!,{R0,R14} ;Restore the registers
585 BICEQS PC,R14,#V_flag ;No -- just clear V then
586 LDRNE R0,xsave__error ;Otherwise load error pointer
587 ORRNES PC,R14,#V_flag ;And return with V set
588
589 LTORG
590
591 xsave__wSpace DCD 0
592
593 ;----- Constants ------------------------------------------------------------
594
595 xsave__buffSize EQU 1024 ;1K buffer should be enough
596
597 ;----- Workspace ------------------------------------------------------------
598
599 ^ 0,R12
600 xsave__wStart # 0
601
602 xsave__flags # 4 ;Various run-time flags
603 xsave__coRout # 0 ;The send coroutine handle
604 xsave__file # 4 ;The save file handle
605 xsave__buffer # 4 ;Pointer to my 1K buffer
606 xsave__buffUsed # 4 ;Amount of data in buffer
607 xsave__total # 4 ;How much data sent so far
608 xsave__error # 0 ;Error pointer from corout
609 xsave__start # 4 ;Start of buffer to send
610 xsave__length # 4 ;Size of next buffer to send
611
612 xsave__wSize EQU {VAR}-xsave__wStart
613
614 xsFlag__inited EQU (1<<0) ;Are we initialised already?
615 xsFlag__ramTran EQU (1<<1) ;Are we doing RAM transfer?
616 xsFlag__finish EQU (1<<2) ;This is the very last block
617 xsFlag__error EQU (1<<3) ;Should return an error
618
619 AREA |Sapphire$$LibData|,CODE,READONLY
620
621 DCD xsave__wSize
622 DCD xsave__wSpace
623 DCD 0
624 DCD 0
625
626 ;----- That's all, folks ----------------------------------------------------
627
628 END