; ; dynamite.s ; ; Memory management in a dynamic area ; ; © 1994-1998 Straylight ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Dynamite ; ; Dynamite is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Dynamite is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Dynamite. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET sh.dynAnchor GET sh.dynArea GET sh.dynHeap GET sh.dynTask GET sh.wSpace GET sh.messages IMPORT version IMPORT |Dynamite$$Commands$$Base| ;----- Module header -------------------------------------------------------- AREA |!!!Module$$Header|,CODE,READONLY dyn__base DCD dt_run ;Application code DCD dyn__init ;Initialisation DCD dyn__quit ;Finalisation DCD dt_service ;Service call handling DCD dyn__name ;Module title string DCD version ;Module help string DCD |Dynamite$$Commands$$Base| ;Command table DCD &4A3C0 ;SWI chunk number DCD dyn__swis ;SWI handler code DCD dyn__swiNames ;SWI name table DCD 0 ;SWI name-number code dyn__name DCB "Dynamite",0 ;----- Main module code ----------------------------------------------------- AREA |Dynamite$$Code|,CODE,READONLY ; --- dyn_base --- ; ; On entry: -- ; ; On exit: R14 == base address of module ; ; Use: Finds the base address of the module. EXPORT dyn_base dyn_base ROUT STMFD R13!,{R14} ADR R14,dyn__base LDMFD R13!,{PC}^ LTORG ; --- dyn__init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Claims some memory, and intercepts OS_ChangeDynamicArea. dyn__init ROUT STMFD R13!,{R7,R8,R14} ;Save some registers ; --- Allocate module workspace --- MOV R0,#6 ;Claim RMA workspace LDR R3,=dyn_wSize ;Get my workspace size SWI XOS_Module ;Allocate the workspace LDMVSFD R13!,{R7,R8,PC} ;If it failed, return error STR R2,[R12,#0] ;Save workspace address MOV R12,R2 ;Point to workspace nicely STR R12,dyn__wsAddr ;Store for my patch code MOV R0,#0 ;Initialise some workspace STR R0,dyn_areaSize ;Dynamic area not created yet STR R0,dyn_heapSize ;No data in the heap STR R0,dyn_lockCount ;And clear the lock counter STR R0,dyn_launch ;Make RMRun do clever stuff STR R0,dyn_taskHandle ;And make the task handle 0 STR R0,dyn_ancTable ;Clear the free anchor table STR R0,dyn_ancList ;And the anchor block list MOV R0,#hpFlag_tidy ;We are compacted now STR R0,dyn_hpFlags ;So make a note of this ADR R14,dyn_stack ;Point to reloc stack base STR R14,dyn_stackPtr ;Save as the stack pointer ; --- Claim OS_ChangeDynamicArea --- ; ; We trap this in the kernel branch table, rather than at ; the SWI vector, because it's so much easier. SWI XOS_ReadMemMapInfo ;Read nice things STR R0,dyn_pageSize ;Save the page size SUB R1,R1,#1 ;Chop one off for da_findPage STR R1,dyn_pageCount ;And the number of them MOV R14,#0 ;Log 2 of page size 00dyn__init TST R0,#1 ;Is bottom bit set yet? MOVEQ R0,R0,LSR#1 ;No -- shift along a bit ADDEQ R14,R14,#1 ;Add to the log BEQ %00dyn__init ;And keep on going STR R14,dyn_log2PageSize ;And store this away MOV R0,#129 ;The OS_Byte number MOV R1,#0 ;Lovely... MOV R2,#255 ;...jubbly! SWI XOS_Byte ;Get OS Version number STR R1,dyn_machine ;Store this away CMP R1,#&A5 ;Is this a RISC PC? BGE %50dyn__init ;Yes -- jump ahead then CMP R1,#&A3 ;Is it RISC OS 2? LDRLT R0,=&ABC ;Yes --- this is sprarea size LDRGE R0,=&ACC ;No -- this is then STR R0,dyn_sprSize ;Save in the workspace MOV R6,PC ;Get the current status TST R6,#IRQ_disable ;Are IRQs enabled? TEQEQP R6,#IRQ_disable ;Yes -- disable them then LDR R0,=&01F033FC ;Find the kernel dispatcher LDR R14,[R0,#OS_ChangeDynamicArea*4] STR R14,dyn_oldChnArea ;Save this in my workspace ADR R14,dyn__patch ;Point to my patch routine STR R14,[R0,#OS_ChangeDynamicArea*4] LDR R14,[R0,#OS_ReadDynamicArea*4] STR R14,dyn_oldReadArea ;Save this in my workspace ADR R14,dyn__readPatch ;Point to my patch routine STR R14,[R0,#OS_ReadDynamicArea*4] LDR R14,[R0,#OS_ValidateAddress*4] STR R14,dyn_oldValidate ;Save this in my workspace ADR R14,dyn__valPatch ;Point to my patch routine STR R14,[R0,#OS_ValidateAddress*4] TEQP R6,#0 ;Restore interrupt status MOV R0,R0 ;Shut the assembler up ; --- Find the TaskManager and WindowManager addresses --- ADR R6,dyn_switchBase ;Point to bit of workspace MOV R0,#18 ;Look up modules by name ADR R1,dyn__switcher ;Point to the Switcher's name SWI XOS_Module ;Try and find its address MOVVS R3,#0 ;If not there, dummy address MOVVS R4,#0 ;For that and the end LDRVC R4,[R3,#-4] ;Otherwise load module length STMIA R6!,{R3,R4} ;Save them in workspace ADR R1,dyn__wimp ;Point to the Wimp's name SWI XOS_Module ;Try and find its address MOVVS R3,#0 ;If not there, dummy address MOVVS R4,#0 ;For that and the end LDRVC R4,[R3,#-4] ;Otherwise load module length STMIA R6!,{R3,R4} ;Save them in workspace LDMFD R13!,{R7,R8,PC}^ ;Return to caller ; --- The machine is a RISC PC -- lucky sod! --- 50dyn__init MOV R0,#0 ;Create dynamic area MOV R1,#-1 ;No particular number MOV R2,#0 ;Initial size of area MOV R3,#-1 ;No particular base address MOV R4,#&80 ;The area flags MOV R5,#-1 ;No maximum size please MOV R6,#0 ;No handler needed MOV R7,#0 ;Workspace to pass to handler ADR R8,dyn__areaName ;Dynamic area name SWI XOS_DynamicArea ;Create the area STRVC R1,dyn_areaHandle ;Store the handle STRVC R3,dyn_areaBase ;And the base address LDMFD R13!,{R7,R8,PC} ;Return to caller dyn__switcher DCB "TaskManager",0 dyn__wimp DCB "WindowManager",0 dyn__areaName DCB "Dynamite",0 LTORG dyn__wsAddr DCD 0 ;Yuk -- address of workspace ; --- dyn__quit --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Tries to close Dynamite down. dyn__quit ROUT STMFD R13!,{R12,R14} ;Save a register LDR R12,[R12] ;Find my workspace address ; --- Make sure we're not needed --- BL dh_compact ;Compact the heap first LDR R14,dyn_areaSize ;How much space are we using? CMP R14,#0 ;Is it 0? BNE %45dyn__quit ;No -- people might want them LDR R14,dyn_stackPtr ;Load the stack pointer ADR R0,dyn_stack ;Point to the stack base CMP R14,R0 ;Are these equal? BNE %45dyn__quit ;No -- we're still used then ; --- Close down the compactor --- LDR R14,dyn_taskHandle ;Get the task handle CMP R14,#0 ;Is it a sensible one? BLGT dt_quit ;Yes -- kill the task then BL danc_quit ;Free all anchor blocks ; --- Get rid of all used pages --- LDR R0,dyn_areaSize ;Get the area size LDR R1,dyn_log2PageSize ;And log 2 of page size MOV R0,R0,LSR R1 ;Number of pages in area BL da_removePages ;Remove pages LDR R0,dyn_machine ;Get the machine type CMP R0,#&A5 ;Is it a RISC PC? BGE %60dyn__quit ;Yes -- jump ahead ; --- First, try to release OS_ChangeDynamicArea --- LDR R0,=&01F033FC ;Find the kernel dispatcher LDR R1,[R0,#OS_ChangeDynamicArea*4] ADR R14,dyn__patch ;Point to my patch routine CMP R1,R14 ;Can I release it safely? BNE %50dyn__quit ;No -- report an error LDR R1,[R0,#OS_ReadDynamicArea*4] ADR R14,dyn__readPatch ;Point to my patch routine CMP R1,R14 ;Can I release it safely? BNE %50dyn__quit ;No -- report an error LDR R1,[R0,#OS_ValidateAddress*4] ADR R14,dyn__valPatch ;Point to my patch routine CMP R1,R14 ;Can I release it safely? BNE %50dyn__quit ;No -- report an error LDR R14,dyn_oldChnArea ;Load the previous value STR R14,[R0,#OS_ChangeDynamicArea*4] LDR R14,dyn_oldReadArea ;Load the previous value STR R14,[R0,#OS_ReadDynamicArea*4] LDR R14,dyn_oldValidate ;Load the previous value STR R14,[R0,#OS_ValidateAddress*4] ; --- Free the workspace I claimed --- 40dyn__quit MOV R0,#7 ;Free RMA workspace MOV R2,R12 ;Point to my workspace SWI XOS_Module ;Try to free it nicely LDR R0,[R13],#4 ;Load private word address MOV R14,#0 ;Clear private word value STR R14,[R0,#0] ;To stop OS doing this too... LDMFD R13!,{PC}^ ;Return to caller ; --- We have handles or relocation entries --- 45dyn__quit ADRL R0,msg_errInUse ;Point to error message LDMFD R13!,{R12,R14} ;Unstack some registers ORRS PC,R14,#V_flag ;Return to caller with error ; --- We couldn't release OS_ChangeDynamicArea --- 50dyn__quit ADRL R0,msg_errRelease ;Point to error message LDMFD R13!,{R12,R14} ;Unstack some registers ORRS PC,R14,#V_flag ;Return to caller with error ; --- We're on a RISC PC --- 60dyn__quit MOV R0,#1 ;Remove dynamic area LDR R1,dyn_areaHandle ;Get the area handle SWI XOS_DynamicArea ;Remove it then B %40dyn__quit ;Now free workspace etc. LTORG ; --- dyn__patch --- ; ; On entry: As for ChangeDynamicArea ; ; On exit: Ditto ; ; Use: Mangles ChangeDynamicArea when being used on the system ; sprite area. dyn__patch ROUT LDR R12,dyn__wsAddr ;Find my workspace address CMP R0,#3 ;Is it the sprite area? LDRNE PC,dyn_oldChnArea ;No -- continue as normal ; --- Mangle the `remembered' size of the sprite area --- STMFD R13!,{R14} ;Save another register LDR R11,dyn_sprSize ;Find the size word LDR R14,[R11,#0] ;Load total sprite area size ADD R10,R14,R1 ;How much does he want? CMP R10,#4*1024*1024 ;More than 4 megs? BGT %90dyn__patch ;Yes -- that's an error LDR R10,dyn_areaSize ;Load my area's size SUB R14,R14,R10 ;Get the size of the sprites STR R14,[R11,#0] ;Save that for the duration ; --- This is nasty and complicated --- ; ; Post processing on kernel SWIs is not something normally ; to be enjoyed. The way things will have to be done is as ; follows: ; ; * We save R14 and the value &2002A (XOS_ChangeDynamicArea) ; on the stack. The SWI value is so that we regain control ; if something went sadly amiss. ; ; * We call the previous OS_ChangeDynamicArea routine, having ; set a suitably bogus return address. ; ; * We mangle the system sprite area in a manner in keeping ; with the aim of the program. ; ; * Then we examine the status returned to us, and if V was ; set AND the X bit of the R11 saved on the stack before ; we were entered at the top was clear we call ; OS_GenerateError. MOV R11,#OS_ChangeDynamicArea ;Get the SWI number ORR R11,R11,#&20000 ;Set the X bit nicely STMFD R13!,{R10-R12} ;Save other registers here STMFD R13!,{R11} ;And the obviously bogus R11 MOV R14,PC ;Set up a return address LDR PC,dyn_oldChnArea ;And let it rip... ; --- We have now done a ChangeDynamicArea --- ; ; R14 saved above is on the stack, along with the caller's ; R10-R12 from the OS SWI dispatcher. LDR R11,dyn_sprSize ;Find the size word LDR R14,[R11,#0] ;Load the new area size ADD R14,R14,R10 ;Add the old difference STR R14,[R11,#0] ;And save it back again 50dyn__patch LDMFD R13!,{R14} ;Get his return address LDMFD R13!,{R11} ;Get the SWI number too LDMVCFD R13!,{R10-R12} ;Restore his registers BICVCS PC,R14,#V_flag ;And return control to him TST R11,#&20000 ;Was the X bit set? SWIEQ OS_GenerateError ;No -- do error like things LDMFD R13!,{R10-R12} ;Restore his registers ORRS PC,R14,#V_flag ;And return the error ; --- He wanted too much memory --- 90dyn__patch ADRL R0,msg_errTooBig ;Point to the error CMP R0,#&80000000 ;Create an overflow B %50dyn__patch ;Make it look like it came ;from ChangeDynamicArea LTORG ; --- dyn__readPatch --- ; ; On entry: R0 == dynamic area to read, and flags etc. ; ; On exit: R1 == size of dynamic area, R2 == optional maximum size ; ; Use: Reads the size of a dynamic area. If the caller is ; interested in the sprite area AND they're not either the ; TaskManager or the WindowManager, we mangle the result so ; they think that the sprite area doesn't contain our clever ; heap. dyn__readPatch ROUT LDR R12,dyn__wsAddr ;Find my workspace address BIC R10,R0,#&80 ;Clear the clever flag bit CMP R10,#3 ;Is it the sprite area? LDRNE PC,dyn_oldReadArea ;No -- continue as normal ; --- Do all the work then --- ; ; Seeing as we know all there is to know about the sprite ; area, we can do all this here without the yukkiness of ; postprocessing. STMFD R13!,{R2,R14} ;Save the link register ADR R11,dyn_switchBase ;Find module addresses BIC R10,R14,#&FC000003 ;Find caller's address LDMIA R11!,{R1,R2} ;Load switcher base/size SUB R14,R10,R1 ;Subtract switcher base CMP R14,R2 ;Is it within switcher? LDMHSIA R11!,{R1,R2} ;No -- load wimp base/size SUBHS R14,R10,R1 ;Subtract wimp base CMPHS R14,R2 ;Is it within wimp? LDMFD R13!,{R2,R14} ;Unstack registers again LDR R1,dyn_sprSize ;Find sprite area size word LDR R1,[R1,#0] ;Load current sprite size LDRHS R10,dyn_areaSize ;If caller is pleb, mangle it SUBHS R1,R1,R10 ;By subtracting our heap size TST R0,#&80 ;Does he want max size? MOVNE R2,#4*1024*1024 ;Yes -- that's 4MB MOV R0,#&01400000 ;Point to sprite area start ADD R13,R13,#4 ;Skip past stacked R11 LDMFD R13!,{R10-R12} ;Unstack caller's registers BICS PC,R14,#V_flag ;And return to caller LTORG ; --- dyn__valPatch --- ; ; On entry: As for OS_ValidateAddress ; ; On exit: As for OS_ValidateAddress ; ; Use: Mangles OS_ValidateAddress so that it gets our somewhat ; rearranged sprite area correct. This is necessary due to ; braindead implementation of OS_ValidateAddress. I'd like to ; be able to do the job properly by hacking the CAM map, but ; the Mysterious Background Address Validator assumes that the ; SWI works in the way Acorn wrote it, so that's put the ; kybosh on that plan. dyn__valPatch ROUT LDR R12,dyn__wsAddr ;Find my workspace address STMFD R13!,{R14} ;Save the link register MOV R10,#&01400000 ;Get base of the sprite area MOV R11,#&01800000 ;Get limit too CMP R0,R10 ;Is it in there? CMPCS R11,R0 CMPCS R1,R10 CMPCS R11,R1 LDMCCFD R13!,{R14} ;No -- restore link LDRCC PC,dyn_oldValidate ;And let OS_VA do it then ; --- Check for weird sprite area thing and Dynamite area --- LDR R11,dyn_sprSize ;Get address of sprite size LDR R11,[R11,#0] ;Load size of sprite area LDR R14,dyn_areaSize ;Load our area size ADD R11,R10,R11 ;Find end of sprite area SUB R10,R11,R14 ;Taking our area into account MOV R11,#&01800000 ;Find top of sprite slot SUB R11,R11,R14 ;Find bottom of our area CMP R11,R0 ;This is deep -- think about CMPHI R1,R10 ;it for a while. LDMFD R13!,{R14} ;If it isn't invalid... ADD R13,R13,#4 LDMFD R13!,{R10-R12} ORRHIS PC,R14,#C_flag BICLSS PC,R14,#C_flag LTORG ; --- dyn__swis --- ; ; On entry: R11 == SWI index ; ; On exit: Depends on the SWI ; ; Use: Dispatches SWIs to other routines dyn__swis ROUT LDR R12,[R12] ;Find my module workspace CMP R11,#&3F BEQ dh_dump CMP R11,#(%10-%00)/4 ;Is the SWI in range? ADDLO PC,PC,R11,LSL #2 ;Yes -- dispatch it then B %10dyn__swis ;Otherwise report the error 00dyn__swis B dh_alloc ;Dynamite_Claim B dh_free ;Dynamite_Free B dh_freeWithID ;Dynamite_FreeWithID B dh_blockInfo ;Dynamite_BlockInfo B dh_changeID ;Dynamite_ChangeID B dh_extend ;Dynamite_Resize B dh_midExtend ;Dynamite_MidExtend B dh_save ;Dynamite_Save B dh_load ;Dynamite_Load B dh_reduce ;Dynamite_Reduce B dh_compact ;Dynamite_Compact B dh_lock ;Dynamite_Lock B dh_unlock ;Dynamite_Unlock B danc_alloc ;Dynamite_ClaimAnchor B danc_free ;Dynamite_ReleaseAnchor B da_readSize ;Dynamite_ReadSpriteSize B da_describe ;Dynamite_Describe B dh_checkHeap ;Dynamite_IntegrityCheck B dh_changeAnchor ;Dynamite_ChangeAnchor 10dyn__swis ADRL R0,msg_errBadSWI ;Point at the error ORRS PC,R14,#V_flag ;And return it back dyn__swiNames DCB "Dynamite",0 DCB "Alloc",0 DCB "Free",0 DCB "FreeWithID",0 DCB "BlockInfo",0 DCB "ChangeID",0 DCB "Resize",0 DCB "MidExtend",0 DCB "Save",0 DCB "Load",0 DCB "Reduce",0 DCB "Compact",0 DCB "Lock",0 DCB "Unlock",0 DCB "ClaimAnchor",0 DCB "ReleaseAnchor",0 DCB "ReadSpriteSize",0 DCB "Describe",0 DCB "IntegrityCheck",0 DCB "ChangeAnchor",0 DCB 0 LTORG ; --- *Dynamite_Clear --- dyn__clear ROUT STMFD R13!,{R14} ;Save a register LDR R12,[R12] ;Load the workspace address ; --- Empty the heap --- LDR R0,dyn_areaSize ;Get the area size LDR R1,dyn_log2PageSize ;And log 2 of page size MOV R0,R0,LSR R1 ;Number of pages in area BL da_removePages ;Remove pages ; --- Free all the anchors --- BL danc_quit ;Free all allocated anchors ; --- Reset the heap variables --- MOV R0,#0 ;Initialise some workspace STR R0,dyn_areaSize ;Dynamic area not created yet STR R0,dyn_heapSize ;No data in the heap STR R0,dyn_lockCount ;And clear the lock counter STR R0,dyn_ancTable ;Clear the free anchor table STR R0,dyn_ancList ;And the anchor block list MOV R0,#hpFlag_tidy ;We are compacted now STR R0,dyn_hpFlags ;So make a note of this LDMFD R13!,{PC}^ ;And return to caller LTORG ; --- *Commands --- AREA |Dynamite$$Commands|,CODE,READONLY DCB "Dynamite_Clear",0 DCD dyn__clear DCD 0 DCD synt_clear DCD help_clear AREA |Dynamite$$Commands_End|,CODE,READONLY DCD 0 ;----- That's all, folks ---------------------------------------------------- END