home *** CD-ROM | disk | FTP | other *** search
- ; Asm.Rswitch: Icon "context switch", for co-expression.
- ;
- ; coswitch(old_cs, new_cs, first) saves the C stack and frame pointers
- ; in the old_cs array, and then sets up new stack and frame pointers
- ; from the new_cs array. If first == 0, then coswitch() actually
- ; creates, using malloc(), the new stack.
-
- ; From checking the Icon source code, it appears that if first == -1
- ; on entry, then this is the last time that coswitch() will be called
- ; for this co-expression. It is therefore possible to free the
- ; co-expression's stack at this point. Note that this use of first is
- ; not actually documented, and so may be subject to change in new
- ; versions of Icon. It is, however, the only "clean" way to free the
- ; co-expression stack, so I have used it here.
-
- ; Register definitions
-
- a1 RN 0
- a2 RN 1
- a3 RN 2
- a4 RN 3
- v1 RN 4
- v2 RN 5
- v3 RN 6
- v4 RN 7
- v5 RN 8
- v6 RN 9
- sl RN 10
- fp RN 11
- ip RN 12
- sp RN 13
- lr RN 14
- pc RN 15
-
- f0 FN 0
- f1 FN 1
- f2 FN 2
- f3 FN 3
- f4 FN 4
- f5 FN 5
- f6 FN 6
- f7 FN 7
-
- ; Work registers (parameters)
-
- old_cs RN 0
- new_cs RN 1
- first RN 2
-
- ; Constants
-
- MinStack * &800 ; 2K (should be an exact no of words)
- SL_Off * 560
-
- ; External symbols
-
- IMPORT |x$stack_overflow|
- IMPORT interp
- IMPORT syserr
- IMPORT malloc
- IMPORT free
- IMPORT stksize
- EXPORT coswitch
-
- ; coswitch() procedure
-
- AREA |C$$code|, CODE, READONLY
-
- start = "coswitch",0
- ALIGN
- & &FF000000+{PC}-start
-
- ; int coswitch (word *old_cs, word *new_cs, int first)
-
- coswitch
- MOV ip, sp
- STMFD sp!, {v1,fp,ip,lr,pc}
- SUB fp, ip, #4
- CMPS sp, sl
- BLLT |x$stack_overflow|
-
- ; Save vn, fp, sp, sl, and lr, and the floating point registers
- ; (ie, the C state) in old_cs.
-
- STMIA old_cs, {v1-v6,sl,fp,sp,lr}
- STFE f4, [old_cs, #40]
- STFE f5, [old_cs, #52]
- STFE f6, [old_cs, #64]
- STFE f7, [old_cs, #76]
-
- ; If first is zero, we must set up a new stack frame (at label First).
- ; Otherwise we can simply reload the C state from new_cs.
-
- CMP first, #0
- BEQ First
-
- ; If first is not -1, we can now return. Otherwise, we must free
- ; the co-expression stack before we return, as we are now finished
- ; with this co-expression.
-
- CMN first, #1
- BEQ Free
-
- ; *------------------------------------------------------------*
- ; * Simple case: switch context and return *
- ; *------------------------------------------------------------*
-
- ; Restore vn, fp, sp, sl, and lr, and the floating point registers
-
- LDMIA new_cs, {v1-v6,sl,fp,sp,lr}
- LDFE f4, [new_cs, #40]
- LDFE f5, [new_cs, #52]
- LDFE f6, [new_cs, #64]
- LDFE f7, [new_cs, #76]
-
- ; Return, restoring fp, sp, and pc (all other registers are as at the
- ; point of call).
-
- LDMDB fp, {fp,sp,pc}^
-
- ; *------------------------------------------------------------*
- ; * First call: Create a stack, switch to it, and call interp *
- ; *------------------------------------------------------------*
-
- First
-
- ; Icon has offered us a stack pointer in new_cs[0]. Unfortunately,
- ; this is the top of a double-ended stack, with the bottom containing
- ; the Icon run-time stack. So we cannot use it (as this would not
- ; allow the C run-time stack checks to function). So we get our own
- ; stack, using malloc(). We will assume that the value in the external
- ; variable stksize is a suitable stack size, but we require a minimum
- ; of 2K, which seems a reasonable size (the initial C stack is 16K)
-
- LDR a1, StkSize
- LDR a1, [a1]
- ADD a1, a1, #3 ; Round to a word boundary
- BIC a1, a1, #3
- CMP a1, #MinStack ; Make sure we have at least the min
- MOVLT a1, #MinStack
- MOV v1, a1 ; Save the size in v1 for later
- BL malloc
-
- ; Check if we ran out of memory
-
- CMP a1, #0
- BNE MemOk
-
- ; If we did, do syserr("ran out of memory in coswitch")
-
- ADR a1, nomem
- LDMDB fp, {v1,fp,sp,lr}
- B syserr
-
- MemOk
-
- ; Copy the old stack chunk structure (5 words, plus 7 Acorn-reserved
- ; words) into the new stack chunk. This seems the best way of making
- ; sure that everything is as it should be.
-
- SUB sl, sl, #SL_Off
- MOV ip, a1
-
- MOV a3, #12 ; 5 basic words, plus 7 Acorn-reserved
- 1 LDR a2, [sl], #4
- STR a2, [ip], #4
- SUBS a3, a3, #1
- BNE %B1
-
- ; Now overwrite the stuff specific to our stack
-
- MOV ip, #0
- STR ip, [a1, #4] ; Stack chunk: sc_next is NULL
- STR ip, [a1, #8] ; Stack chunk: sc_prev is NULL
- STR v1, [a1, #12] ; Stack chunk: sc_size is what we set
- LDR ip, FreeAddr
- STR ip, [a1, #16] ; Stack chunk: sc_deallocate is free()
-
- ; Set up the new stack pointers
-
- MOV sl, a1 ; Start at the base
- ADD sp, sl, v1 ; SP is at the high end
- ADD sl, sl, #SL_Off ; SL points 560 bytes in
- MOV fp, #0 ; FP starts at 0
-
- ; interp(0,0)
-
- MOV a2, #0
- MOV a1, #0
- BL interp
-
- ; syserr("interp() returned in coswitch")
-
- ADR a1, errmsg
- LDMDB fp, {v1,fp,sp,lr}
- B syserr
-
- ; *------------------------------------------------------------*
- ; * Last call: Free the stack, switch context, and return *
- ; *------------------------------------------------------------*
-
- Free
-
- ; OK, so we have to free the current C stack. We do this by getting
- ; the address of the latest chunk from sl, and following the chain back,
- ; freeing stuff as it goes.
-
- LDR v1, [old_cs, #24] ; Depends on sl < {fp,sp,lr} !!!
- SUB v1, v1, #SL_Off ; Get to the chunk head
-
- ; We're about to trash our stack. Before we do, we should switch to
- ; the new stack, so that the deallocation functions don't get in a
- ; mess trying to deallocate their own stack..... We also save the value
- ; of new_cs in a scratch register for use later.
-
- ADD v2, new_cs, #24 ; Skip v1-v6
- LDMIA v2, {sl,fp,sp} ; Get the new stack frame
- MOV v6, new_cs
-
- ; First, get the address of the next chunk to free and the address of
- ; the deallocation function (sc_prev and sc_deallocate). Note that we
- ; jump back to this point repeatedly, for each stack chunk. The only
- ; entry condition is that v1 points to the stack chunk header.
-
- 1 LDR v2, [v1, #8] ; v2 = v1->sc_prev
- LDR v3, [v1, #16] ; v3 = v1->sc_deallocate
-
- ; Now, deallocate this chunk. That is, call the function whose address
- ; is in v3 with a single parameter, v1.
-
- MOV a1, v1
- ADR lr, %F2 ; Set up the return address
- MOV pc, v3
-
- ; Now, check whether there is another chunk to free. If v2 is NULL, we
- ; have reached the end of the stack chain and we can stop.
-
- 2 CMP v2, #0
- BEQ %F3
-
- ; Otherwise, we should move the address of the next chunk into v1,
- ; and start over.
-
- MOV v1, v2
- B %B1
-
- ; Now, we can finally restore the whole machine state from new_cs.
- ; Note, however, that new_cs is a volatile register (not guaranteed
- ; across function calls). That's why we saved it in v6, and it's v6
- ; we will use now.
-
- 3 MOV new_cs, v6
- LDMIA new_cs, {v1-v6,sl,fp,sp,lr}
- LDFE f4, [new_cs, #40]
- LDFE f5, [new_cs, #52]
- LDFE f6, [new_cs, #64]
- LDFE f7, [new_cs, #76]
-
- ; Return, restoring fp, sp, and pc (all other registers are as at the
- ; point of call).
-
- LDMDB fp, {fp,sp,pc}^
-
- ; *------------------------------------------------------------*
- ; * Extra junk (variables, strings) *
- ; *------------------------------------------------------------*
-
- ; Indirected addresses for C variables
-
- StkSize & stksize
- FreeAddr & free
-
- ; Message texts for syserr()
-
- errmsg = "interp() returned in coswitch",0
- nomem = "ran out of memory in coswitch",0
- ALIGN
-
- ; That's all, folks...
-
- END
-