; Copyright 1996 Acorn Computers Ltd ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; > asm.ModuleWrap ; ; This provides the wrapping for turning a C application into a module. ; ; Changes: PJC: 15-Aug-91: Fixed Application_Code so that if an error occurs when ; Filer_Action executes the wimpslot command, R0 isn't corrupted ; Also added "GET Hdr:Variables" ; ADH: 28-Apr-2000: UsePathForHelpMessages switch added - makes RAM modules work; ; exports private word pointer as 'module_private_word_ptr'. GET s.AppName ; defines the name of this module GET Hdr:ListOpts GET Hdr:Macros GET Hdr:System GET Hdr:ModHand GET Hdr:EnvNumbers GET Hdr:Proc GET Hdr:FSNumbers GET Hdr:NewErrors GET Hdr:Services GET Hdr:ResourceFS GET Hdr:MsgTrans GET Hdr:Variables GET Hdr:UpCall IMPORT |_Lib$Reloc$Off| IMPORT |_clib_initialisemodule| IMPORT |_clib_entermodule| IMPORT |_clib_finalisemodule| ^ 0 old_exit_handler # 4 old_exit_workspace # 4 module_to_operate_on # 4 enter_and_kill_strsize * :INDEX: @ ;sl RN 10 ;fp RN 11 ; This structure is used during application start and end. ; The information here enables the emulation of a module ; initialisation and finalisation for the purposes of the ; C run-times. The stack bit of the structure is used both ; for initialisation and finalisation and stops the ; embarasing situation of not being able to finalise due ; to not being able to allocate some memory for a short ; term stack. The disadvantage is that this gives an extra ; 300 byte overhead per invocation of a module. ^ 0 af_old_exit_handler # 4 af_old_exit_workspace # 4 af_private_word_address # 4 af_allow_finalisation # 4 application_finalise_strsize * :INDEX: @ AREA |!!!Module$$Header|,CODE,READONLY Module_BaseAddr DCD ModuleWrap_Start - Module_BaseAddr DCD ModuleWrap_Init - Module_BaseAddr ; for ResourceFS stuff DCD ModuleWrap_Die - Module_BaseAddr DCD ModuleWrap_Service - Module_BaseAddr DCD ModuleWrap_TitleString - Module_BaseAddr DCD ModuleWrap_HelpString - Module_BaseAddr DCD ModuleWrap_CommandTable - Module_BaseAddr DCD 0 DCD 0 DCD 0 DCD 0 [ International_Help <> 0 DCD Message_FileName - Module_BaseAddr | DCD 0 ] ModuleWrap_CommandTable [ :DEF:FilerAct DCB "Filer_Action", 0 | DCB "Desktop_", ApplicationName, 0 ] ALIGN DCD Application_Code - Module_BaseAddr DCD &00ff0000 :OR: International_Help DCD Application_Syntax - Module_BaseAddr DCD Application_Help - Module_BaseAddr ; Table termination DCD 0 [ :DEF:FilerAct [ International_Help <> 0 Message_FileName DCB "Resources:Resources.FilerAct.Messages",0 Application_Syntax DCB "SFACFAC",0 Application_Help DCB "HFACFAC",0 | Application_Syntax DCB "Syntax:", 9, "*Filer_Action", 13, 0 Application_Help DCB "The Filer_Action module runs the background " DCB "Filer operations", 0 ] | [ International_Help <> 0 [ :DEF: UsePathForHelpMessages Message_FileName DCB ApplicationName,":Messages",0 | Message_FileName DCB "Resources:Resources.",ApplicationName,".Messages",0 ] Application_Syntax DCB ApplicationName,"Syntax",0 Application_Help DCB ApplicationName,"Help",0 | Application_Syntax DCB "Syntax:",9,"*Desktop_", ApplicationName, 13, 0 Application_Help DCB "The !", ApplicationName, " module runs the " DCB ApplicationName, " desktop application", 13, 0 ] ] ModuleWrap_HelpString [ :DEF:FilerAct DCB "Filer_Action", 9 | DCB "!", ApplicationName, 9 [ :LEN: ApplicationName < 7 DCB 9 ] ] DCB ApplicationVersion, 0 [ :DEF:FilerAct ModuleWrap_TitleString DCB "Filer_Action", 0 | ModuleWrap_TitleString DCB "!", ApplicationName, 0 ] ALIGN space * 32 strcpy_advance Entry "r1,r2" 10 LDRB r2, [r1], #1 STRB r2, [r0], #1 TEQ r2, #0 BNE %BT10 EXIT ; Command processor Application_Code Entry "r0" [ :DEF:FilerAct ; Drop slot size ADR r0, SlotSizeCommand SWI XOS_CLI STRVS r0, [sp] EXIT VS ] ; Is base active? MOV r0, #ModHandReason_LookupName ADRL r1, ModulePercentBase SWI XOS_Module BVS %FT99 ; !<App>%Base doesn't exist - bad noozz TEQ r4, #0 BNE %FT05 ; !<App>%Base is inactive - enter that MOV r0, #ModHandReason_Enter ADRL r1, ModulePercentBase LDR r2, [sp] ; Pointer to parameters SWI XOS_Module B %FT99 ; VS or VC its bad noozz - we shouldn't have returned 05 ; Base is active ; Find first/create a numbered module ; Copy the skeleton !<App>%Wnnnnnn onto the stack so we can poo on it SUB sp, sp, #ModulePercentNumbered_Size MOV r0, sp ADRL r1, ModulePercentNumbered BL strcpy_advance MOV r6, #0 10 MOV r0, r6 ADD r1, sp, #ModulePercentNumbered_NumberOffset MOV r2, #ModulePercentNumbered_Size - ModulePercentNumbered_NumberOffset SWI XOS_ConvertCardinal4 ADDVS sp, sp, #ModulePercentNumbered_Size BVS %FT99 MOV r0, #ModHandReason_LookupName MOV r1, sp SWI XOS_Module BVC %FT20 LDR r1, [r0] LDR r14, =ErrorNumber_IncarnationNotFound TEQ r1, r14 ADDNE sp, sp, #ModulePercentNumbered_Size BNE %FT99 ; Exit if not an incarnation not found error ; Create the incarnation MOV r0, #ModHandReason_NewIncarnation MOV r1, sp SWI XOS_Module BVC %FT30 ADD sp, sp, #ModulePercentNumbered_Size B %FT99 20 ; If this module is active, move onto the next TEQ r4, #0 ADDNE r6, r6, #1 BNE %BT10 ; Drop through into the 'do a numbered incarnation' code. ; This case (numbered incarnation being inactive) shouldn't ; happen, but if it does this is the right thing to do. 30 ; Enter and kill it afterwards MOV r0, #ModHandReason_Claim MOV r3, #ModulePercentNumbered_Size SWI XOS_Module ADDVS sp, sp, #ModulePercentNumbered_Size BVS %FT99 ; Copy the name onto the bit of RMA we've just got MOV r0, r2 MOV r1, sp BL strcpy_advance ; Drop the string from the stack now we no longer need it ADD sp, sp, #ModulePercentNumbered_Size ; Move the pointer to somewhere out of the way MOV r4, r2 ; Get the struct MOV r0, #ModHandReason_Claim MOV r3, #enter_and_kill_strsize SWI XOS_Module BVS %FT98 ; Fill in the string pointer STR r4, [r2, #module_to_operate_on] MOV r4, r2 ; Dummy a new application starting before changing the exit handler MOV r0, #UpCall_NewApplication ADRL r2, Module_BaseAddr SWI XOS_UpCall BVS %FT97 MOV r2, r4 ; Swap to our exit handler MOV r0, #ExitHandler ADRL r1, exit_and_kill_afterwards_handler ; r2 already contains wsp SWI XOS_ChangeEnvironment BVS %FT97 STR r1, [r4, #old_exit_handler] STR r2, [r4, #old_exit_workspace] ; Enter the module MOV r0, #ModHandReason_Enter LDR r1, [r4, #module_to_operate_on] LDR r2, [sp] SWI XOS_Module 96 ; If we commence execution here, then the enter failed, in which ; case we must unhitch our exit handler and free the rest of the ; junk we've allocated STR r0, [sp] MOV r0, #ExitHandler LDR r1, [r4, #old_exit_handler] LDR r2, [r4, #old_exit_workspace] SWI XOS_ChangeEnvironment LDR r0, [sp] 97 ; Error exit - free struct pointed to by r4 MOV r2, r4 LDR r4, [r2, #module_to_operate_on] STR r0, [sp] MOV r0, #ModHandReason_Free SWI XOS_Module LDR r0, [sp] 98 ; Error exit - free thing pointed to by r4 STR r0, [sp] MOV r0, #ModHandReason_Free MOV r2, r4 SWI XOS_Module LDR r0, [sp] SETV 99 ; Error finish off STR r0, [sp] EXIT LTORG ModulePercentBase [ :DEF:FilerAct DCB "Filer_Action" | DCB "!", ApplicationName ] PercentBase DCB "%" Base DCB "Base", 0 ALIGN ; Make sure this end of this string is aligned ModulePercentNumbered [ :DEF:FilerAct DCB "Filer_Action", "%W" | DCB "!", ApplicationName, "%W" ] ModulePercentNumbered_NumberOffset * .-ModulePercentNumbered DCB "nnnnnnnnn", 0 ALIGN ; This ensures the hole is a whole number of word big ModulePercentNumbered_Size * .-ModulePercentNumbered ALIGN [ :DEF:FilerAct SlotSizeCommand DCB "*WimpSlot -min 40k -max 40k", 0 ALIGN ] exit_and_kill_afterwards_handler ; Restore the exit handler specified in the workspace MOV r0, #ExitHandler LDR r1, [r12, #old_exit_handler] LDR r2, [r12, #old_exit_workspace] SWI OS_ChangeEnvironment ; Set CAO to 4 - this module is getting stuffed whether it ; likes it or not MOV r0, #CAOPointer MOV r1, #4 MOV r2, #0 MOV r3, #0 SWI OS_ChangeEnvironment ; Perform the operation on the module MOV r0, #ModHandReason_Delete LDR r1, [r12, #module_to_operate_on] SWI OS_Module ; Free our workspace: string, then workspace MOV r0, #ModHandReason_Free LDR r2, [r12, #module_to_operate_on] SWI OS_Module MOV r2, r12 SWI OS_Module ; Exit MOV r0, #0 MOV r1, #0 MOV r2, #0 SWI OS_Exit ; Enter module InitialisationStackSize * 1024 sl_offset DCD |_Lib$Reloc$Off| ModuleWrap_Start ROUT ; Pop up into SVC mode to get some stack SWI XOS_EnterOS BVS %FT99 ; Store command string somewhere handy MOV r9, r0 ; Install the finalise exit handler MOV r0, #ModHandReason_Claim MOV r3, #application_finalise_strsize SWI XOS_Module BVS %FT99 ; Evaluate the stack limit MOV sl, sp, LSR #20 MOV sl, sl, ASL #20 ; Store finalise RMA piece address in r4 MOV r4, r2 STR r12, [r4, #af_private_word_address] MOV lr, #0 STR lr, [r4, #af_allow_finalisation] ; Switch exit handler to ours MOV r0, #ExitHandler ADRL r1, application_finalisation SWI XOS_ChangeEnvironment BVS %FT97 ; Store the old exit handler in the RMA piece STR r1, [r4, #af_old_exit_handler] STR r2, [r4, #af_old_exit_workspace] ; Store the command line and private word address on the stack Push "r9,r12" ; Store the magic stack limit things LDMIA sl, {r1, r2} Push "r1,r2,r4,sl" ; Initialise the application MOV r0, #1 ; Initialise re-entrantly BL |_clib_initialisemodule| ; Restore the magic stack limit things Pull "r1,r2,r4,sl" STMIA sl, {r1,r2} ; If error rebalance stack and generate it ADDVS sp, sp, #8 SWIVS OS_GenerateError MOV lr, #1 STR lr, [r4, #af_allow_finalisation] ; Get the command line and private word from the stack Pull "r0,r1" ; Drop back into user mode MOV r12, #3 MRS r12, CPSR TST r12, #2_11100 TEQEQP pc, #Z_bit ; keeps EQ MSRNE CPSR_c, #USR32_mode ; acts as NOP for TEQP :) MOV r12, r1 ; put a copy of the private word into the data for the ; app to get hold of if necessary LDR r0, [r12] ; r0 -> workspace LDR lr, module_private_word_ptr_adcon LDR r0, [r0, #8] ; r0 = client static offset STR r12, [lr, r0] ; call the application BL |_clib_entermodule| ; Exit if the application returned, otherwise ; the application will have exited anyway. MOV r0, #0 MOV r1, #0 MOV r2, #0 SWI OS_Exit 97 ; Exit in error - junk the finalisation heap piece MOV r5, r0 MOV r0, #ModHandReason_Free MOV r2, r4 SWI XOS_Module MOV r0, r5 99 ; Exit in error - we didn't manage to get any stack SWI OS_GenerateError module_private_word_ptr_adcon DCD module_private_word_ptr ; This is the application finalisation routine. It should free up ; the static workspace. application_finalisation ROUT ; Switch to the original exit handler to avoid deadly cycles MOV r0, #ExitHandler LDR r1, [r12, #af_old_exit_handler] LDR r2, [r12, #af_old_exit_workspace] SWI OS_ChangeEnvironment ; Switch to SVC mode to get some stack ; As we're going to bomb out to the OS anyway, who cares about ; which mode we bomb out in? SWI OS_EnterOS LDR r4, [r12, #af_allow_finalisation] ; Construct the stack limit MOV sl, sp, LSR #20 MOV sl, sl, ASL #20 ; Store r12 to free the af structure Push "r12" ; Save the magic stack limit things LDMIA sl, {r1,r2} Push "r1,r2" LDR r12, [r12, #af_private_word_address] MOV r0, r12 LDR r12, [r12] LDMIB r12, {fp, r12} STMIA sl, {fp, r12} LDR r14, sl_offset ADD sl, sl, r14 MOV fp, #0 TEQ r4, #0 BLNE |_clib_finalisemodule| ; BL<cond> 32-bit OK ; Restore the magic stack limit things Pull "r1,r2" STMIA sl, {r1,r2} ; Now we've cleaned up the C bits, lets clean up ourselves Pull "r2" ; Zero the private word LDR r0, [r2, #af_private_word_address] MOV r1, #0 STR r1, [r0] ; Junk the fh structure MOV r0, #ModHandReason_Free SWI OS_Module MOV r0, #0 MOV r1, #0 MOV r2, #0 SWI OS_Exit [ :LNOT::DEF:REMOVE_RAMLOAD_CHECK module_linkaddr DCD Module_BaseAddr module_ramload_error DCD &800e0a DCB "RAMLoadC", 0 ALIGN ] ModuleWrap_Error Push "r7, lr" MOV r1, #0 MOV r2, #0 ADRL r4, ModuleWrap_TitleString MOV r5, #0 MOV r6, #0 MOV r7, #0 SWI XMessageTrans_ErrorLookup Pull "r7, pc" ; Initialise module ModuleWrap_Init [ :LNOT::DEF:REMOVE_RAMLOAD_CHECK ADRL r0, Module_BaseAddr LDR r1, module_linkaddr CMP r0, r1 ADRNE r0, module_ramload_error BNE ModuleWrap_Error ] Push "lr" [ :DEF:FilerAct ; initialise FilerAct$Path if not already done ADR R0, FilerActPath MOV R2, #-1 MOV R3, #0 MOV R4, #VarType_Expanded SWI XOS_ReadVarVal ; returns R2=0 if doesn't exist CMP R2, #0 ; clears V as well! ADREQ R0, FilerActPath ADREQ R1, PathDefault MOVEQ R2, #?PathDefault MOVEQ R3, #0 MOVEQ R4, #VarType_String SWIEQ XOS_SetVarVal CLRV ] ; If there are NO instantiations yet, this must be the first [ :LNOT::DEF:FilerAct BL countinstantiations ADRLT r0, resourcefsfiles SWILT XResourceFS_RegisterFiles ; ignore errors CLRV ; (ResourceFS may not yet be present) ] Pull "pc" [ :DEF:FilerAct FilerActPath DCB "FilerAct$Path", 0 PathDefault DCB "Resources:$.Resources.FilerAct.", 0 ALIGN ] ; Finalise module ModuleWrap_Die ; Don't allow ourselves to die if there is workspace hanging around. ; This means the module is running as an application. LDR r0, [r12] TEQ r0, #0 ADRNE r0, ErrorBlock_MustQuitApplicationFirst BNE ModuleWrap_Error ; If this is the only instantiation, call ResourceFS_DeregisterFiles ; Ignore errors, as ResourceFS may no longer be around Push "lr" [ :LNOT::DEF:FilerAct BL countinstantiations ; LT => we're the only one ADRLT r0, resourcefsfiles SWILT XResourceFS_DeregisterFiles ] CLRV Pull "pc" ErrorBlock_MustQuitApplicationFirst DCD 0 DCB "AppQuit", 0 ALIGN ; Count how many instantiations there are of this module ; Then compare with 1 (ie. LT => 0 instantiations, EQ => 1 etc.) countinstantiations Push "r0-r7, lr" MOV r0, #ModHandReason_LookupName ADRL r1, ModuleWrap_TitleString ; looks up instantiation 0 SWI XOS_Module ; error => no instantiations MOV r6, r1 ; remember module number MOVVS r7, #0 MOVVC r7, #1 ; r7 = module count count1 MOVVC r0, #ModHandReason_GetNames SWIVC XOS_Module ; updates r1, r2 ADDVS r1, r6, #1 ; no more if error CMP r1, r6 ADDEQ r7, r7, #1 BEQ count1 CMP r7, #1 ; LT => 0, EQ => 1 Pull "r0-r7, pc" ; Service call handler ; ;Ursula format ; ASSERT Service_Memory < Service_Reset ASSERT Service_Reset < Service_ResourceFSStarting ; ModuleWrap_ServTab DCD 0 ;flags DCD ModuleWrap_UService - Module_BaseAddr DCD Service_Memory DCD Service_Reset [ :LNOT::DEF:FilerAct DCD Service_ResourceFSStarting ] DCD 0 ;terminator DCD ModuleWrap_ServTab - Module_BaseAddr ;anchor ModuleWrap_Service MOV r0, r0 ;magic instruction ModuleWrap_UService [ :LNOT::DEF:FilerAct CMP r1, #Service_ResourceFSStarting BEQ svc_resourcefsstarting ] CMP r1, #Service_Reset BEQ svc_reset CMP r1, #Service_Memory MOVNE pc, lr ; Claim the service_memory if its our memory it's stealing STMFD sp!, {lr} ADRL r14, Module_BaseAddr CMP r2, r14 MOVEQ r1, #0 LDMFD sp!, {pc} ; Free workspace of all instantiations and kill all non base instantiations ; Note. Assumes Base is last module to get service call. Don't see how I ; can get around this. svc_reset Push "r0-r5, lr" LDR r2, [r12] CMP r2, #0 ; Speed optimisation Pull "r0-r5, pc",EQ MOV r0, #ModHandReason_Free SWI XOS_Module ; Free workspace and flag pw STR r12, [r12] ; with its own address MOV r0, #ModHandReason_LookupName ADRL r1, ModulePercentBase SWI XOS_Module ; Find module no. and workspace ptr Pull "r0-r5, pc",VS CMP r12, r4 ; = flagged workspace ptr? Pull "r0-r5, pc",NE ; No, return. MOV r2, #0 ; Set base pw to 0 so we don't STR r2, [r12] ; kill base svc_reset1 MOV r0, #ModHandReason_GetNames SWI XOS_Module ; Get next instantiation Pull "r0-r5, pc",VS CMP r4, #0 ; Base BEQ svc_reset4 ; => don't kill it MOV r2, #0 ; Reset instantion no. as kill will STR r2, [r4] ; invalidate it. Clear pw. MOV r4, r1 SUB sp, sp, #ModulePercentNumbered_Size MOV r0, sp ; Copy <App>%<Base> to stack ADRL r1, ModuleWrap_TitleString svc_reset2 LDRB lr, [r1], #1 CMP lr, #&20 MOVCC lr, #'%' STRB lr, [r0], #1 BCS svc_reset2 svc_reset3 LDRB lr, [r5], #1 STRB lr, [r0], #1 CMP lr, #&20 BCS svc_reset3 MOV r0, #ModHandReason_Delete MOV r1, sp SWI XOS_Module ; Die ADD sp, sp, #ModulePercentNumbered_Size MOV r1, r4 B svc_reset1 svc_reset4 CMP r2, #0 ; End of instantions? BNE svc_reset1 Pull "r0-r5, pc" GBLS GetRoundObjAsm [ :LNOT::DEF:FilerAct svc_resourcefsstarting Push "r0, lr" ADR r0, resourcefsfiles MOV lr, pc ; lr -> return address MOV pc, r2 ; r2 -> code to call Pull "r0, pc" ; (r3 = workspace pointer) resourcefsfiles GetRoundObjAsm SETS " GET s.ResFiles" | GetRoundObjAsm SETS " " ] $GetRoundObjAsm [ :LNOT::DEF:FilerAct DCD 0 ; provide terminator automatically ] AREA ModuleWrapData,DATA EXPORT module_private_word_ptr module_private_word_ptr DCD 0 END