; ; dll.s ; ; Handling of DLL data structures ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Dynamic Linking System (SDLS) ; ; SDLS 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. ; ; SDLS 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 SDLS. 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.wSpace GET sh.dllblock GET sh.linkblock GET sh.misc GET sh.app GET sh.messages ;----- External routines ---------------------------------------------------- AREA |DLLM$$Code|,CODE,READONLY GBLL debug debug SETL {FALSE} ; --- dll_find --- ; ; On entry: R0 == name of DLL to find ; R1 == minimum version number of DLL ; On exit: R0 == pointer to DLL block, or error EXPORT dll_find dll_find ROUT STMFD R13!,{R1-R4,R14} ;Preserve registers MOV R4,R1 ;Keep hold of version number MOV R1,R0 ;Keep pointer to string LDR R3,dll__list ;Find the list 00dll_find CMP R3,#0 ;Is this the end of the line? BEQ %40dll_find ;Yes -- give an error LDR R0,[R3,#dl_name] ;Find the name string MOV R2,#0 ;Caseless compare BL misc_strcmp ;Compare the strings LDRNE R3,[R3,#dl_next] ;If no match, move on... BNE %00dll_find ;... and try again ; --- We found a name match --- LDR R0,[R3,#dl_version] ;Get version of DLL CMP R0,R4 ;Check against version here LDRLT R3,[R3,#dl_next] ;If too low, move on... BLT %00dll_find ;... and try again ; --- The name checked out -- return --- MOV R0,R3 ;Point to DLL (give handle) LDMFD R13!,{R1-R4,PC}^ ;And return to caller ; --- It wasn't there. Return an error --- 40dll_find MOV R0,R4 ;Get version number BL dll_convertVersion ;Convert it to a string MOV R2,R0 ;Keep pointer to the string ADRL R0,msg_errDLLNotFound ;Couldn't find DLL name BL misc_error ;... create an error message LDMFD R13!,{R1-R4,R14} ;... restore registers ORRS PC,R14,#V_flag ;... and return an error LTORG ; --- dll_ensure --- ; ; On entry: R0 == pointer to name to load ; R1 == version number of DLL ; On exit: R0 == DLL handle EXPORT dll_ensure dll_ensure ROUT STMFD R13!,{R1-R5,R9-R11,R14} ;Stash registers somewhere ; --- Find the name, and see if it's in memory --- MOV R10,R1 ;Save the version number BL dll__createName ;Set up the name CMP R0,#0 ;Was it in memory? LDMNEFD R13!,{R1-R5,R9-R11,PC} ;If so, we should be happy MOV R9,R2 ;Keep pointer to leafname ; --- Find the size of the DLL and allocate memory --- 01dll_ensure MOV R0,#17 ;Read catalogue information SWI XOS_File ;Read the information LDMVSFD R13!,{R1-R5,R9-R11,PC} ;If it failed, return now CMP R0,#1 ;Check that the file was OK BNE %08dll_ensure ;If not, complain properly ADD R3,R4,#dl_extra ;Size of memory to get MOV R0,#6 ;Allocate memory from RMA SWI XOS_Module ;Allocate it LDMVSFD R13!,{R1-R5,R9-R11,PC} ;If no memory, return error ; --- Load the DLL into the block --- MOV R0,R2 ;Pointer to the block to use MOV R11,R0 ;Keep hold of DLL handle BL dll__load ;Load the DLL into the block BVS %10dll_ensure ;If it failed, return error ; --- Check the DLL's version number --- LDR R1,[R11,#dl_version] ;Load the DLL's version CMP R1,R10 ;Compare against version BLT %09dll_ensure ;If too old, give an error ; --- Link the new DLL into the list --- ; ; This also marks the DLL as being shared, since the shared ; marker is dl_next not being -1. LDR R2,dll__list ;Point to list head STR R11,dll__list ;Store the next one MOV R0,#0 ;Ready to zero bits STR R0,[R11,#dl_prev] ;No previous DLL yet STR R2,[R11,#dl_next] ;Fix up next link CMP R2,#0 ;Is there another block? STRNE R11,[R2,#dl_prev] ;If so, fix up prev link ; --- Fix up other bits of data --- MOV R0,#dl_tentative ;Set DLL's `tentative' bit STR R0,[R11,#dl_clients] ;No clients registered yet ; --- Start up any required DLLs --- LDR R1,[R11,#dl_dllLimit] ;Find limit of DLL block LDR R0,[R11,#dl_dllBase] ;Find base of same BL app_sfromtbl ;Load any other required DLLs ; --- Now return the DLL handle --- MOVVC R0,R11 ;Get the DLL handle LDMFD R13!,{R1-R5,R9-R11,PC} ;Return ; --- Say that the file wasn't found --- 08dll_ensure ADRL R0,msg_errFileNotFound ;Point to error skeleton BL misc_error ;Create the message B %10dll_ensure ;Return to an error ; --- Create an error about old version --- 09dll_ensure MOV R0,R10 ;Get the required version BL dll_convertVersion ;Convert to a string MOV R2,R0 ;Make that fillin 2 MOV R1,R9 ;Point to DLL name string ADRL R0,msg_errDLLTooOld ;Point to error skeleton BL misc_error ;Set up the error block ; --- Free up memory and return an error --- 10dll_ensure MOV R1,R0 ;Keep hold of the error MOV R0,#7 ;Free the block I allocated MOV R2,R11 ;Point to the DLL block SWI XOS_Module ;Do the free operation MOV R0,R1 ;Put the error pointer back LDMFD R13!,{R1-R5,R9-R11,R14} ;Retreive registers ORRS PC,R14,#V_flag ;Return with an error LTORG ; --- dll_check --- ; ; On entry: R0 == pointer to name to load version number ; On exit: -- ; --- The format string for the command line --- dll__checkfmt DCB "/a/g," DCB "/a/g",0 ALIGN EXPORT dll_check dll_check ROUT STMFD R13!,{R1-R5,R9-R11,R14} ;Stack cunning registers ; --- Allocate a nice buffer --- MOV R10,R0 ;Keep pointer to cmd string MOV R0,#6 ;Allocate some memory MOV R3,#256 ;The size of said memory SWI XOS_Module ;Get it LDMVSFD R13!,{R1-R5,R9-R11,PC} ;Return if we can't have it MOV R11,R2 ;Save the pointer ; --- Parse up the command string --- ADR R0,dll__checkfmt ;Point to format string MOV R1,R10 ;Point to command string MOV R2,R11 ;Point to my nice buffer MOV R3,#256 ;And do the business SWI XOS_ReadArgs ;Get the OS to parse it up BVS %90dll_check ;If it failed, return ; --- Read the version number --- LDR R5,[R11,#4] ;Get info about version strng LDRB R4,[R5,#0] ;Get LSB of its length LDRB R3,[R5,#1] ;Get MSB of its length ORR R4,R4,R3,LSL #8 ;Turn this into a length MOV R3,#10 ;We're multiplying a lot MOV R10,#0 ;The version as we know it MOV R2,#0 ;Decimal part of version ADD R5,R5,#2 ;Point to the real string CMP R4,#0 ;Is there a string there? BEQ %70dll_check ;No -- that's an error 00dll_check LDRB R0,[R5],#1 ;Get a character CMP R0,#'.' ;Is it a decimal point? BEQ %01dll_check ;Yes -- next bit please BL %80dll_check ;Convert it to a number MLA R10,R3,R10,R0 ;Add the digit on SUBS R4,R4,#1 ;Decrement length count BNE %00dll_check ;Get another digit if I can B %03dll_check ;Now check for the DLL... 01dll_check CMP R4,#1 ;Is the string empty now? BEQ %03dll_check ;Yes -- get on with it LDRB R0,[R5] ;Get the next digit BL %80dll_check ;Convert it to a number MUL R2,R3,R0 ;And put it nicely away CMP R4,#2 ;Is there only one char? BEQ %03dll_check ;Yes -- we've done it now LDRB R0,[R5,#1] ;Get the remaining digit BL %80dll_check ;Convert it to a number ADD R2,R2,R0 ;And put it in CMP R4,#3 ;Make sure that's all BNE %70dll_check ;If not, complain 03dll_check MOV R3,#100 ;Now join the two together MLA R10,R3,R10,R2 ;Now we have a version! ; --- Now mangle the name to something usable --- LDR R0,[R11,#0] ;Point to name info LDRB R4,[R0,#0] ;Get LSB of name length LDRB R3,[R0,#1] ;Get MSB of name length ORR R4,R4,R3,LSL #8 ;Convert to a real length ADD R0,R0,#2 ;Point to the actual name MOV R1,#0 ;Zero-terminate it STRB R1,[R0,R4] ;Store that in right place ; --- Find out a good name to use --- MOV R1,R10 ;Get the version number BL dll__createName ;Turn this into a filename CMP R0,#0 ;Was it in memory? BEQ %10dll_check ;No -- continue onwards MOV R0,#7 ;Free that buffer MOV R2,R11 ;Point to it SWI XOS_Module ;Free it for real now LDMFD R13!,{R1-R5,R9-R11,PC}^ ;Return to caller ; --- Free the OS_ReadArgs buffer now --- 10dll_check MOV R9,R2 ;Keep pointer to leafname MOV R0,#7 ;Free that buffer MOV R2,R11 ;Point to it SWI XOS_Module ;Free it for real now ADD R11,R1,#200 ;Keep a pointer to misc_buf ; --- Open a file for the DLL --- MOV R0,#&4F ;Open, with errors, no path SWI XOS_Find ;Find the file BVS %90dll_check ;If it failed, give error MOV R5,R0 ;Look after the handle ; --- Now load a bit of the file --- MOV R2,R11 ;Point to the misc_buf MOV R1,R5 ;Get the file handle MOV R3,#20 ;Get the first twenty bytes MOV R4,#0 ;Read from the beginning MOV R0,#3 ;Read bytes from file SWI XOS_GBPB ;Quick, now, do it ; --- Close the file --- MOV R0,#0 ;Close the file MOV R1,R5 ;Get the file handle SWI XOS_Find ;Close the file ; --- Now check the fields in the block --- LDR R0,[R11,#dl_magic-dl_extra] ;Get the magic DLL word LDR R1,=dl_MAGIC ;Get the real version CMP R0,R1 ;Check it's kosher BNE %91dll_check ;If not, make an error LDR R0,[R11,#dl_bversion-dl_extra] ;Get format version LDR R1,=dl_VERSION ;Get the one we're on now CMP R0,R1 ;Compare the versions BGT %92dll_check ;If too late, complain LDR R0,[R11,#dl_version-dl_extra] ;Get DLL version CMP R0,R10 ;Cmp with caller's version BLT %93dll_check ;If too late, complain LDMFD R13!,{R1-R5,R9-R11,PC}^ ;Return, job well done ; --- Get a digit, and convert --- 80dll_check CMP R0,#'0' ;Is it less than 0? BLT %70dll_check ;Yes -- that's an error CMP R0,#'9' ;Is it greater than 9? BGT %70dll_check ;Yes -- that's an error SUB R0,R0,#'0' ;Convert to a digit MOVS PC,R14 ;Return to caller ; --- Make an error about a mangled version --- 70dll_check ADRL R0,msg_errBadVersion ;Point to error message B %90dll_check ;Free memory and return error ; --- Tidy up after an error and return --- 90dll_check MOV R9,R0 ;Look after error pointer MOV R0,#7 ;Free that buffer MOV R2,R11 ;Point to it SWI XOS_Module ;Free it for real now MOV R0,R9 ;Point to the error LDMFD R13!,{R1-R5,R9-R11,R14} ;Unstack all the registers ORRS PC,R14,#V_flag ;And return the error ; --- A file wasn't a real DLL --- 91dll_check ADRL R0,msg_errNotADLL ;Point to error MOV R1,R9 ;Point to leafname BL misc_error LDMFD R13!,{R1-R5,R9-R11,R14} ;Unstack all the registers ORRS PC,R14,#V_flag ;And return the error ; --- A file had a silly version number --- 92dll_check ADRL R0,msg_errTooNew ;Point to error MOV R1,R9 ;Point to leafname BL misc_error LDMFD R13!,{R1-R5,R9-R11,R14} ;Unstack all the registers ORRS PC,R14,#V_flag ;And return the error ; --- A file was too old for the caller --- 93dll_check MOV R0,R10 ;Get version number wanted BL dll_convertVersion ;Convert to printable form MOV R2,R0 ;That's fillin number 2 ADRL R0,msg_errDLLTooOld ;Point to error MOV R1,R9 ;Point to leafname BL misc_error LDMFD R13!,{R1-R5,R9-R11,R14} ;Unstack all the registers ORRS PC,R14,#V_flag ;And return the error LTORG ; --- dll_load --- ; ; On entry: R0 == DLL handle (block to load into) ; R1 == filename of DLL ; On exit: -- EXPORT dll_load dll_load ROUT STMFD R13!,{R1,R10,R11,R14} ;Keep link register safe ; --- Load the DLL into the block MOV R11,R0 ;Keep DLL pointer safe BL dll__load ;Load the DLL LDMVSFD R13!,{R1,R10,R11,PC} ;Return if there's an error ; --- If it's late enough, fit it up to the application --- LDR R10,[R11,#dl_bversion] ;Get the format version CMP R10,#100 ;Is it the old version? LDRGT R1,[R11,#dl_appStubs] ;Find the app stubs table CMPGT R1,R11 ;Is this pointer sensible? BLE %90dll_load ;Yes -- skip this bit LDR R0,[R11,#dl_appStubNames] ;Find the names table CMP R0,R0 ;Clear V flag BL app_fix ;Yes -- fix up the table LDMVSFD R13!,{R1,R10,R11,PC} ;Return if there's an error ; --- Load any required shared DLLs --- 90dll_load LDR R1,[R11,#dl_dllLimit] ;Find limit of DLL block LDR R0,[R11,#dl_dllBase] ;Find base of same BL app_fromtable ;Load any other required DLLs LDMFD R13!,{R1,R10,R11,PC} ;Return to caller LTORG ; --- dll_appEntry --- ; ; On entry: R0 == pointer to application's entry table ; R1 == pointer to application's name table ; R2 == pointer to entry point name ; On exit: R0 == pointer to entry point (if present) EXPORT dll_appEntry dll_appEntry ROUT STMFD R13!,{R1-R5,R14} ;Stack registers MOV R3,R2 ;Keep pointer to entry point ; --- Main matching loop --- 00dll_appEntry LDRB R4,[R1],#1 ;Get first byte of next name CMP R4,#0 ;Is it a null byte? BEQ %41dll_appEntry ;Yes -- couldn't find entry CMP R4,#1 ;Is it a dummy entry? BEQ %10dll_appEntry ;Yes -- don't check it then 01dll_appEntry LDRB R5,[R3],#1 ;Get byte from name too CMP R4,R5 ;Do they match? BNE %02dll_appEntry ;No -- find the next name CMP R4,#0 ;Is this the end? LDRNEB R4,[R1],#1 ;No -- get another name byte BNE %01dll_appEntry ;And go round again ; --- We found it --- LDR R0,[R0,#0] ;Get the actual entry address LDMFD R13!,{R1-R5,PC}^ ;Return to caller happy ; --- Move on to next name in the table --- 02dll_appEntry CMP R4,#0 ;Is this the end of the name? LDRNEB R4,[R1],#1 ;No -- get another byte BNE %02dll_appEntry ;And go round again 10dll_appEntry ADD R0,R0,#4 ;Move on to next entry ptr MOV R3,R2 ;Point to start of entry name B %00dll_appEntry ;And try that one out ; --- Entry point could not be found --- 41dll_appEntry MOV R1,R2 ;Point to entry point name ADRL R0,msg_errAppEntry ;Point to error message BL misc_error ;Create the error message LDMFD R13!,{R1-R5,R14} ;Find saved registers ORRS PC,R14,#V_flag ;Return the error to caller ALIGN ; --- dll_findEntry --- ; ; On entry: R0 == pointer to DLL ; R1 == pointer to entry point name ; On exit: R0 == pointer to entry point EXPORT dll_findEntry dll_findEntry ROUT STMFD R13!,{R1-R7,R14} ;Stack registers ; --- Set up for main loop --- LDR R7,[R0,#dl_entries] ;Find number of entry points LDR R3,[R0,#dl_enames] ;Point to start of name table LDR R4,[R0,#dl_eveneer] ;No entry points found yet TST R7,#dl_noNames ;No names? BNE %42dll_findEntry ;Then deal with this BICS R2,R7,#&FF000000 ;Clear entry type flags BEQ %40dll_findEntry ;No entry points -- weird ; --- Find whether this name matches --- 00dll_findEntry MOV R5,R1 ;Point to string to match LDRB R14,[R3,#0] ;Load first byte from name CMP R14,#1 ;Is this a dummy entry? ADDEQ R3,R3,#1 ;Yes -- skip past the byte BEQ %10dll_findEntry ;Yes -- don't check the name 01dll_findEntry LDRB R6,[R5],#1 ;Read byte from pattern LDRB R14,[R3],#1 ;Read byte from target CMP R6,R14 ;Do they match? BNE %02dll_findEntry ;No -- try another string CMP R6,#0 ;Is this the string end? BNE %01dll_findEntry ;No -- try another char ; --- We found the entry point --- TST R7,#dl_shortEntries ;Are these APCS veneers? MOVEQ R0,R4 ;Yes -- point to veneer base LDRNE R0,[R4,#0] ;No -- return base address LDMFD R13!,{R1-R7,PC}^ ;Return to caller ; --- No luck -- try another entry point --- 02dll_findEntry CMP R14,#0 ;Is this the end of the name? LDRNEB R14,[R3],#1 ;No -- get another character BNE %02dll_findEntry ;And try again 10dll_findEntry SUBS R2,R2,#1 ;Decrement entry point count BEQ %41dll_findEntry ;If we ran out, that's it TST R7,#dl_shortEntries ;Are these APCS veneers? ADDEQ R4,R4,#16 ;Yes -- move to next veneer ADDNE R4,R4,#4 ;No -- move to next word B %00dll_findEntry ;And try again ; --- DLL has no entry points --- 40dll_findEntry LDR R1,[R0,#dl_name] ;Point to DLL name ADRL R0,msg_errNoEntry ;Point to error message BL misc_error ;Create the error message LDMFD R13!,{R1-R7,R14} ;Find saved registers ORRS PC,R14,#V_flag ;Return the error to caller ; --- Entry point could not be found --- 41dll_findEntry MOV R2,R1 ;Point to entry point name LDR R1,[R0,#dl_name] ;Point to DLL name ADRL R0,msg_errDLLEntry ;Point to error message BL misc_error ;Create the error message LDMFD R13!,{R1-R7,R14} ;Find saved registers ORRS PC,R14,#V_flag ;Return the error to caller ; --- DLL has no named entry points --- 42dll_findEntry LDR R1,[R0,#dl_name] ;Point to DLL name ADRL R0,msg_errNoNames ;Point to error message BL misc_error ;Create the error message LDMFD R13!,{R1-R7,R14} ;Find saved registers ORRS PC,R14,#V_flag ;Return the error to caller LTORG ; --- dll_showInfo --- ; ; On entry: R0 == pointer to argument string ; On exit: -- EXPORT dll_showInfo dll_showInfo ROUT STMFD R13!,{R1-R4,R14} ;Stack some registers ; --- Parse some arguments --- MOV R1,R0 ;Point to the command tail ADR R0,dll_sinfoDef ;Point to definition string ADR R2,misc__sharedBuf ;Point to scratch buffer MOV R3,#256 ;Size of my buffer SWI XOS_ReadArgs ;Read the command line LDMVSFD R13!,{R1-R4,PC} ;Return if it didn't work MOV R4,#0 ;Clear some flags LDR R14,[R2,#0] ;Load the `full' flag CMP R14,#0 ;Is it clear? ORRNE R4,R4,#1 ;No -- set the flag then LDR R0,[R2,#4] ;Load the string pointer CMP R0,#0 ;Is it there? ADREQL R0,synt_DLLInfo - 4 ;No -- point to syntax string LDMEQFD R13!,{R1-R4,R14} ;Restore registers ORREQS PC,R14,#V_flag ;And return to caller ; --- Find the DLL --- MOV R2,R0 ;Keep hold of the pointer MOV R1,#0 ;Don't care about version BL dll_find ;Find the DLL pointer BVS %10dll_showInfo ;Give an error if not found ; --- Show the info :-) --- MOV R3,R0 ;Keep pointer to DLL block ADRL R0,msg_dinfoName SWI XOS_Write0 LDR R0,[R3,#dl_name] ;Point to the DLL's name SWI XOS_Write0 SWI XOS_NewLine ADRL R0,msg_dinfoAuthor SWI XOS_Write0 LDR R0,[R3,#dl_copyright] ;Point to the DLL's author SWI XOS_Write0 SWI XOS_NewLine ADRL R0,msg_dinfoVersion SWI XOS_Write0 LDR R0,[R3,#dl_version] ;Point to the DLL's version BL dll_convertVersion ;Convert the version number SWI XOS_Write0 SWI XOS_NewLine ADRL R0,msg_dinfoReferences SWI XOS_Write0 LDR R0,[R3,#dl_clients] ;Get client count BIC R0,R0,#dl_tentative ;Clear tentative bit if any SUB R13,R13,#12 ;Give some space for string MOV R1,R13 ;Point to this space MOV R2,#12 ;Size of the buffer SWI XOS_ConvertInteger4 ;Convert it to a string SWI XOS_Write0 ADD R13,R13,#12 ;Reclaim workspace SWI XOS_NewLine TST R4,#1 ;Do we want to print this? BEQ %02dll_showInfo ;No -- don't then ADRL R0,msg_dinfoEntries SWI XOS_Write0 ; --- Write out entry point names --- LDR R2,[R3,#dl_entries] ;Get number of entry points TST R2,#dl_noNames ;No name table? BNE %08dll_showInfo ;No -- tell user BICS R2,R2,#&FF000000 ;Clear type bits BEQ %09dll_showInfo ;No entries -- spooky LDR R1,[R3,#dl_enames] ;Point to first entry name 00dll_showInfo SWI XOS_WriteI+' ' ;Indent the list a bit SWI XOS_WriteI+' ' 01dll_showInfo LDRB R0,[R1],#1 ;Get a name byte CMP R0,#1 ;Is it a dummy entry? SWIHI XOS_WriteC ;No -- write it out BHS %01dll_showInfo ;And go round for the next SWI XOS_NewLine ;Start a new line SUBS R2,R2,#1 ;Done another one BGT %00dll_showInfo ;If more to do, continue ; --- Return to caller --- 02dll_showInfo LDMFD R13!,{R1-R4,PC}^ ;Return with registers nice ; --- Entry point table suppressed --- 08dll_showInfo ADRL R0,msg_dinfoHidden ;Point to the string SWI XOS_Write0 ;Display that on the screen B %02dll_showInfo ;And return to caller ; --- No entry points -- freaky --- 09dll_showInfo ADRL R0,msg_dinfoNone ;Point to the string SWI XOS_Write0 ;Display that on the screen B %02dll_showInfo ;Resume information ; --- Couldn't find the DLL --- 10dll_showInfo MOV R1,R2 ;Point to DLL name ADRL R0,msg_errDLLNotInMem ;Point to the error BL misc_error ;Create error message fully LDMFD R13!,{R1-R4,R14} ;Unstack all registers ORRS PC,R14,#V_flag ;Return to caller with error dll_sinfoDef DCB "full/s,",0 LTORG ; --- dll_writeTitle --- ; ; On entry: -- ; On exit: -- EXPORT dll_writeTitle dll_writeTitle ROUT STMFD R13!,{R14} ;Stack link register for this ADRL R0,msg_dllHeader ;Point to the header line SWI XOS_Write0 ;Display it nicely LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- dll_writeInfo --- ; ; On entry: R0 == pointer to a DLL block ; On exit: -- EXPORT dll_writeInfo dll_writeInfo ROUT STMFD R13!,{R1-R3,R14} ;Stack some registers MOV R2,R0 ;Keep hold of pointer MOV R1,#17 ;Length of DLL name LDR R0,[R2,#dl_name] ;Point to the name MOV R3,#0 ;Length so far 00 LDRB R14,[R0,R3] ;Load a byte CMP R14,#0 ;At the end? ADDNE R3,R3,#1 ;No -- inc length BNE %b00 ;And keep on looping CMP R3,#16 ;Is this to long? BL dll_field ;Display the name SWIGT OS_NewLine ;Too long -- print a newline MOVGT R1,#17 ;Print 21 spaces ADRGT R0,dll__nullStr ;Point to the string BLGT dll_field ;And do that thing LDR R0,[R2,#dl_version] ;Load the version BL dll_convertVersion ;Convert it to a string MOV R1,#10 ;Length of version field BL dll_field ;Display it on the screen LDR R0,[R2,#dl_copyright] ;Point to copyright string SWI XOS_Write0 ;Display on the screen SWI XOS_NewLine ;Follow with a newline LDR R2,[R2,#dl_next] ;Point to next one along LDMFD R13!,{R1-R3,PC}^ ;Return to caller dll__nullStr DCB 0 LTORG ; --- dll_list --- ; ; On entry: -- ; On exit: -- EXPORT dll_list dll_list ROUT STMFD R13!,{R1,R2,R14} ; --- Set up for a loop through the list --- LDR R2,dll__list ;Point to list CMP R2,#0 ;Check that it's non-null BEQ %01dll_list ;If so, give special message ; --- Do a bit of screen set-up --- BL dll_writeTitle ;Display the title line ; --- Main loop --- 00dll_list MOV R0,R2 ;Point to DLL block BL dll_writeInfo ;Display info about it LDR R2,[R2,#dl_next] ;Point to next one along CMP R2,#0 ;Is there another one? BNE %00dll_list ;Yes -- display its stuff LDMFD R13!,{R1,R2,PC}^ 01dll_list ADRL R0,msg_noDLLs ;Point to the message SWI XOS_Write0 ;Display that on the screen LDMFD R13!,{R1,R2,PC}^ LTORG ; --- dll_convertVersion --- ; ; On entry: R0 == version number ; On exit: R0 == pointer to version string EXPORT dll_convertVersion dll_convertVersion ROUT STMFD R13!,{R1-R3,R14} ;Preserve registers MOV R1,#100 ;Divisor BL dll__divide ;Find major and minor version LDMVSFD R13!,{R1-R3,PC} ;Return if there's an error MOV R3,R1 ;Keep minor version ADR R1,misc__sharedBuf ;Point to buffer MOV R2,#16 ;Give it a sensible size SWI XOS_ConvertCardinal3 ;This is excessive, but... LDMVSFD R13!,{R1-R3,PC} ;Return if there's an error MOV R0,#'.' ;To be written to buffer STRB R0,[R1],#1 ;Write the decimal separator SUB R2,R2,#1 ;Another byte used in buffer MOV R0,R3 ;Get minor version number SWI XOS_ConvertCardinal1 ;Can't be more than 100 LDMVSFD R13!,{R1-R3,PC} ;Return if there's an error LDRB R2,[R0,#1] ;Get second character CMP R2,#0 ;Is there one? BNE %00dll_convertVersion ;Yes -- skip ahead a bit STRB R2,[R0,#2] ;Leave exactly 2 digits LDRB R2,[R0,#0] ;Get first character STRB R2,[R0,#1] ;Store in second position MOV R2,#'0' ;And write a leading 0 STRB R2,[R0,#0] ;In first position 00dll_convertVersion ADR R0,misc__sharedBuf ;Point to buffer LDMFD R13!,{R1-R3,PC}^ ;Return to caller nicely LTORG ; --- dll_field --- ; ; On entry: R0 == pointer to string ; R1 == length of field EXPORT dll_field dll_field ROUT STMFD R13!,{R1,R2,R14} ;Stash registers MOV R2,R0 ;Keep hold of string ptr 00dll_field LDRB R0,[R2],#1 ;Get a string byte CMP R0,#0 ;Check the byte BEQ %01dll_field ;If end, write pad chars SWI XOS_WriteC ;Write the character SUB R1,R1,#1 ;Decrement counter thing B %00dll_field ;If allowed, get the next one 01dll_field SUBS R1,R1,#1 ;Decrement counter thing LDMLEFD R13!,{R1,R2,PC}^ ;Return if done 02dll_field SWI XOS_WriteI+' ' ;Write a space SUBS R1,R1,#1 ;Decrement counter thing BGT %02dll_field ;If allowed, write another LDMFD R13!,{R1,R2,PC}^ ;Return happy :-) LTORG ; --- dll_compare --- ; ; On entry: R0 == pointer to DLL ; R1 == pointer to name string ; R2 == required version number ; On exit: R0 == 1 for a match, or 0 for no match EXPORT dll_compare dll_compare ROUT STMFD R13!,{R1,R2,R14} ;Keep registers safe LDR R14,[R0,#dl_version] ;Find the version number CMP R2,R14 ;How does it shape up? BGT %00dll_compare ;If too low, return 0 MOV R2,#0 ;Case insensitive bitty LDR R0,[R0,#dl_name] ;Point to DLL's name string BL misc_strcmp ;Compare the strings MOVEQ R0,#1 ;If there's a match, return 1 LDMEQFD R13!,{R1,R2,PC}^ ;Return to caller 00dll_compare MOV R0,#0 ;No match here LDMFD R13!,{R1,R2,PC}^ ;Return to caller LTORG ; --- dll_tentative --- ; ; On entry: R0 == pointer to DLL ; On exit: -- EXPORT dll_tentative dll_tentative ROUT STMFD R13!,{R14} ;Store return address LDR R14,[R0,#dl_clients] ;Find the magic counter ORR R14,R14,#dl_tentative ;Set the `tentative' bit STR R14,[R0,#dl_clients] ;And store back again LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- dll_confirm --- ; ; On entry: -- ; On exit: -- EXPORT dll_confirm dll_confirm ROUT STMFD R13!,{R14} ;Store return address LDR R0,dll__list ;Find list base address CMP R0,#0 ;Are there any entries LDMEQFD R13!,{PC}^ ;Return to caller if not 00dll_confirm LDR R14,[R0,#dl_clients] ;Find the magic counter TST R14,#dl_tentative ;Is it a tentative one? BICNE R14,R14,#dl_tentative ;Yes -- clear `tentative' bit ADDNE R14,R14,#1 ;Increment the counter STRNE R14,[R0,#dl_clients] ;And store back again LDR R0,[R0,#dl_next] ;Find next DLL in the chain CMP R0,#0 ;Is this the end yet? BNE %00dll_confirm ;Go round again if not LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- dll_retrace --- ; ; On entry: -- ; On exit: -- EXPORT dll_retrace dll_retrace ROUT STMFD R13!,{R1,R14} ;Keep registers safe LDR R0,dll__list ;Find list base address CMP R0,#0 ;Are there any more entries LDMEQFD R13!,{R1,PC}^ ;Return to caller if not 00dll_retrace LDR R1,[R0,#dl_next] ;Find the next block along LDR R14,[R0,#dl_clients] ;Load the magic counter BICS R14,R14,#dl_tentative ;Clear the `tentative' bit STR R14,[R0,#dl_clients] ;Store back in structure BLEQ dll__free ;Free the block if no count MOVS R0,R1 ;Point to next block along BNE %00dll_retrace ;If any more to do, do more LDMFD R13!,{R1,PC}^ ;Return to caller LTORG ; --- dll_freeAll --- ; ; On entry: -- ; On exit: -- EXPORT dll_freeAll dll_freeAll ROUT STMFD R13!,{R1,R2,R14} ;Preserve registers nicely MOV R0,#7 ;Freeing memory now LDR R2,dll__list ;Point to first entry CMP R2,#0 ;Is there anything to do? LDMEQFD R13!,{R1,R2,PC}^ ;No -- just leave now 00dll_freeAll LDR R1,[R2,#dl_next] ;Get next pointer in list SWI XOS_Module ;Free the block MOVS R2,R1 ;Point to the next entry BNE %00dll_freeAll ;If any more to do, do them STR R2,dll__list ;Store 0 back into list head 01dll_freeAll LDMFD R13!,{R1,R2,PC}^ ;Return to caller LTORG ; --- dll_dec --- ; ; On entry: R0 == pointer to DLL to decrement ; On exit: -- EXPORT dll_dec dll_dec ROUT STMFD R13!,{R14} ;Preserve link register LDR R14,[R0,#dl_next] ;Find whether it's shared CMP R14,#-1 ;Just check to make sure BEQ %00dll_dec ;If not, give an error LDR R14,[R0,#dl_clients] ;Load the DLL count SUBS R14,R14,#1 ;Chop one off the counter STRNE R14,[R0,#dl_clients] ;Store the count back LDMNEFD R13!,{PC}^ ;Return to caller LDMFD R13!,{R14} ;Pull back return address B dll__free ;Free the DLL -- no clients 00dll_dec LDR R1,[R0,#dl_name] ;Point to the DLL's name ADRL R0,msg_errNotShared ;Point to error message BL misc_error ;Turn into a real error LDMFD R13!,{R1,R14} ;Pull back return address ORRS PC,R14,#V_flag ;Return the error LTORG ; --- dll_instvars --- ; ; On entry: R0 == pointer to DLL ; R1 == pointer to data ; On exit: -- EXPORT dll_instvars dll_instvars ROUT STMFD R13!,{R1-R6,R14} ;Stack registers ; --- Store pointer if needs be --- MOV R3,R0 ;Look after this pointer LDR R2,[R3,#dl_instBase] ;Find start of data block SUB R4,R1,R2 ;Calculate relocation LDR R14,[R3,#dl_next] ;Is there a next pointer? CMP R14,#-1 ;Is the DLL shared? BNE %00dll_instvars ;Yes -- don't store reloc STR R4,[R3,#dl_wspace] ;Store in special offset ; --- Copy the data across --- 00dll_instvars LDR R0,[R3,#dl_bversion] ;Get the format version CMP R0,#100 ;Is it too old for this? BLE %90dll_instvars ;Yes -- skip ahead then LDR R5,[R3,#dl_zinitBase] ;Get zero-init base address LDR R6,[R3,#dl_zinitLimit] ;Get zero-init limit address MOV R0,R1 ;Move destination pointer MOV R1,R2 ;Find start of data block CMP R5,R6 ;Is there a zinit area? MOVLT R2,R5 ;Yes -- use zinit base addr LDRGE R2,[R3,#dl_instLimit] ;No -- use data end address SUB R2,R2,R1 ;Convert pointer to length BL misc_memcpy ;Copy the data across ; --- If the format supports it, do zero-initing --- CMP R5,R6 ;Are these sensible? ADDLT R0,R5,R4 ;Yes -- add relocation ADDLT R1,R6,R4 BLLT misc_zinit ;And zero-init the area B %99dll_instvars ; --- Copy the whole lot across --- 90dll_instvars MOV R0,R1 ;Move destination pointer MOV R1,R2 ;Find start of data block LDR R2,[R3,#dl_instLimit] ;Find end of data block SUB R2,R2,R1 ;Convert pointer to length BL misc_memcpy ;Copy the data across 99dll_instvars LDMFD R13!,{R1-R6,PC}^ ;Return to caller LTORG ; --- dll_findWorkspace --- ; ; On entry: R0 == DLL handle ; On exit: R0 == pointer to workspace for the DLL ; ; So that the caller can free it when they kill the DLL EXPORT dll_findWorkspace dll_findWorkspace ROUT STMFD R13!,{R14} ;Stack link register LDR R14,[R0,#dl_instBase] ;Find workspace base address LDR R0,[R0,#dl_wspace] ;Find the relocation ADD R0,R0,R14 ;Relocate the address LDMFD R13!,{PC}^ ;Return to the caller LTORG ; --- dll_convreloc --- ; ; On entry: R0 == pointer to DLL ; R1 == pointer to data block ; On exit: R0 == relocation offset to use EXPORT dll_convreloc dll_convreloc ROUT STMFD R13!,{R14} ;Keep link register LDR R14,[R0,#dl_instBase] ;Find base of static data SUB R0,R1,R14 ;Convert to a relocation LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- dll_info --- ; ; On entry: R0 == pointer to DLL ; On exit: R0 preserved ; R1 == pointer to DLL name ; R2 == version number ; R3 == pointer to copyright string ; R4 == size of instance variables EXPORT dll_info dll_info ROUT LDR R3,[R0,#dl_instBase] ;Find base of variables LDR R4,[R0,#dl_instLimit] ;Find limit of variables SUB R4,R4,R3 ;Turn this into length LDR R1,[R0,#dl_name] ;Point to DLL's name LDR R2,[R0,#dl_version] ;Load version number LDR R3,[R0,#dl_copyright] ;Point to copyright string MOVS PC,R14 ;Return to caller LTORG ; --- dll_datasize --- ; ; On entry: R0 == pointer to DLL ; On exit: R0 == number of bytes to allocate EXPORT dll_datasize dll_datasize ROUT STMFD R13!,{R14} ;Keep link register LDR R14,[R0,#dl_instBase] ;Find base of area LDR R0,[R0,#dl_instLimit] ;Find end of area SUB R0,R0,R14 ;Convert to length LDMFD R13!,{PC}^ ;Return to caller LTORG ;----- Private routines ----------------------------------------------------- ; --- dll__createName --- ; ; On entry: R0 == pointer to name to load ; R1 == version number to check for ; On exit: R0 == DLL handle if found in memory, or 0 ; R1 == pointer to filename of DLL to use ; R2 == pointer to leafname of DLL if not in memory dll__createName ROUT STMFD R13!,{R9-R11,R14} ;Stash registers somewhere ; --- Start off by putting `DLL:' in the buffer --- ; ; We may not need it, but at least it's there if we do. MOV R11,R0 ;Remember this name pointer MOV R10,R1 ;And remember the version LDR R14,dll__pathPrefix ;Load the path prefix ADR R0,misc__sharedBuf+8 ;Point to shared buffer STR R14,[R0],#4 ;And store that away ; --- Now set up for our search --- ADR R9,misc__sharedBuf+8 ;Filename assumed to be leaf MOV R2,R0 ;Assume DLL name is leaf ; --- The first character is special --- LDRB R14,[R11],#1 ;Load the next byte out CMP R14,#'[' ;Is this a new-style leaf? BEQ %20dll__createName ;Yes -- skip to copy rest ; --- Now enter the main loop --- 00 CMP R14,#'[' ;Found new-style name delim? ADREQ R9,misc__sharedBuf+8+4 ;Yes -- ignore `DLL:' bit MOVEQ R2,R0 ;DLL name starts here BEQ %20dll__createName ;Yes -- skip to copy rest STRB R14,[R0],#1 ;Store character in output CMP R14,#'.' ;Found a path separator? ADREQ R9,misc__sharedBuf+8+4 ;Yes -- ignore `DLL:' bit MOVEQ R2,R0 ;DLL name is here or later CMP R14,#&21 ;Is this the end of it all? LDRCSB R14,[R11],#1 ;No -- get the next byte BCS %b00 ;And keep on looping MOV R14,#0 ;Terminate the string STRB R14,[R0,#-1] ;Stuff that over old term B %50dll__createName ;Now go and find the DLL ; --- Mess about with new-style names --- 20 LDRB R14,[R11],#1 ;Load another byte out CMP R14,#&21 ;Dropped off the end? MOVCC R14,#']' ;Yes -- pretend it was right CMP R14,#']' ;Finished yet? MOVEQ R14,#0 ;Yes -- zero terminate STRB R14,[R0],#1 ;Store in the buffer BNE %20dll__createName ;And keep looping ; --- We've found all the names now --- 50 MOV R0,R2 ;Point to the DLL leaf MOV R1,R10 ;Find the version number BL dll_find ;Try to find the dll LDMVCFD R13!,{R9-R11,PC} ;It was OK -- return then MOV R0,#0 ;Couldn't find the DLL MOV R1,R9 ;Point to the filename LDMFD R13!,{R9-R11,PC}^ ;Restore registers dll__pathPrefix DCB "dll:",0 ;Path variable to search ALIGN LTORG ; --- dll__free --- ; ; On entry: R0 == DLL handle to release ; On exit: -- dll__free ROUT STMFD R13!,{R1,R2,R14} ;Keep registers safe ; --- Mangle the list nicely --- LDR R1,[R0,#dl_next] ;Get pointer to next DLL LDR R2,[R0,#dl_prev] ;And pointer to previous one CMP R1,#0 ;Is there a next one? STRNE R2,[R1,#dl_prev] ;Yes -- fix up previous ptr CMP R2,#0 ;Is there a previous one? ADREQ R2,dll__list ;No -- point to list head STR R1,[R2,#dl_next] ;Fix up the pointer ; --- Free the block --- MOV R2,R0 ;Point to the block MOV R0,#7 ;Magic number to free it SWI XOS_Module ;Free it now LDMFD R13!,{R1,R2,PC} ;Return to caller LTORG ; --- dll__load --- ; ; On entry: R0 == pointer to block to load ; R1 == pointer to filename ; On exit: -- dll__load ROUT STMFD R13!,{R1-R5,R9-R11,R14} MOV R10,R0 ;Keep pointer to block safe MOV R9,R1 ;And look after the filename ; --- Load the DLL into the buffer --- MOV R0,#16 ;Load a file into memory MOV R3,#(1<<31) ;Resync code areas when done ADD R2,R10,#dl_extra ;Leave space for extra info SWI XOS_File ;Try to load the file LDMVSFD R13!,{R1-R5,R9-R11,PC} ;Return the error if any ; --- Check that it really is a DLL --- LDR R0,=dl_MAGIC ;Find the magic DLL number LDR R1,[R10,#dl_magic] ;Get the version from DLL CMP R0,R1 ;Check they're the same BNE %40dll__load ;If not, give an error LDR R0,=dl_VERSION ;Get known version number LDR R1,[R10,#dl_bversion] ;Get DLL's version number CMP R0,R1 ;How do they match up? BLT %41dll__load ;If too new, complain ; --- Relocate the image --- MOV R14,PC ;Set up return address ADD PC,R10,#dl_relocate ;Perform the relocation ; --- Fill in the C library stubs --- LDR R0,[R10,#dl_stubs] ;Get pointer to stubs table CMP R0,R10 ;If it isn't invalid BLS %10dll__load ;... don't skip ahead BL misc_copyStubs ;Copy the clib branch table LDMVSFD R13!,{R1-R5,R9-R11,PC} ;Return the error if any ; --- Mark the DLL as being non-shared --- ; ; Here we also clear the data relocation for non-shared DLLs ; since they don't need to be multiply instantiated. We *do* ; allow extension DLLs to be multiply instantiated, though. ; Clearing the relocation also has the side effect of ; clearing a *shared* DLL's client counter. 10dll__load MOV R0,#-1 ;Non-shared is indicated... STR R0,[R10,#dl_next] ;... by dl_next being -1 MOV R0,#0 ;Also, clear relocation STR R0,[R10,#dl_wspace] ;(also clears client count) ; --- That's it, then --- LDMFD R13!,{R1-R5,R9-R11,PC}^ ;Return to caller ; --- Give an error about a bad DLL image --- 40dll__load ADRL R0,msg_errNotADLL ;Point to error message B %49dll__load ;Go to error generation bit ; --- Give an error about an unrecognised format version --- 41dll__load ADRL R0,msg_errTooNew ;Point to error message B %49dll__load ;Go to error generation bit ; --- Create an error and leave --- 49dll__load MOV R1,R9 ;Point to the filename BL misc_error ;Fill in the error message LDMFD R13!,{R1-R5,R9-R11,R14} ;Restore registers ORRS PC,R14,#V_flag ;And return an error LTORG ; --- dll__divide --- ; ; On entry: R0 == dividend ; R1 == divisor ; On exit: R0 == quotient ; R1 == remainder ; ; This routine is mostly uncommented, 'cos I copied from Acorn's Assembler ; documentation, and it should be accurate. It's not exactly optimised, ; but it should hold up to the sort of treatment I'm going to be giving it ; (very delicate and occasional use). dll__divide ROUT STMFD R13!,{R2,R3,R14} CMP R1,#0 ;Check for stupidity BEQ %10dll__divide ;If stupid, give an error MOV R3,R1 CMP R3,R0,LSR #1 00dll__divide MOVLS R3,R3,LSL #1 CMP R3,R0,LSR #1 BLS %00dll__divide MOV R2,#0 01dll__divide CMP R0,R3 SUBCS R0,R0,R3 ADC R2,R2,R2 MOV R3,R3,LSR #1 CMP R3,R1 BCS %01dll__divide MOV R1,R0 ;Move results into right... MOV R0,R2 ;... registers LDMFD R13!,{R2,R3,PC}^ 10dll__divide ADRL R0,msg_errDivide ;Point to error message LDMFD R13!,{R2,R3,R14} ;Retreive registers ORRS PC,R14,#V_flag ;And returnt the error LTORG ;----- That's all folks ----------------------------------------------------- END