;
; dllmerge.s
;
; Merge two DLL resources together (MDW)
;
;  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.

;----- Change history -------------------------------------------------------
;
; Version	By	Change
;
; 1.00		MDW	Initial version written.
;
; 1.01		MDW	Added error handler to stop bombing out on errors.
;
; 1.02		MDW	Done copyright and version messages with setdate.
;
; 1.03		MDW	Made most SWIs generate errors, and added confirm
;			on error handler.
;
; 1.04		MDW	Updated dm_setField in line with Sapphire version.
;
; 1.05		MDW	Moved menu definition into code area, and copied it
;			out at run-time, for the sake of tidiness.  Workspace
;			now *exactly* &0900 bytes!
;
; 1.06		MDW	Used embedded templates instead of separate file.
;
; 1.07		MDW	Fixed problem identifying !DLLs folder link dragged
;			from Kysmet -- it was returning &FC0 as the filetype,
;			instead of &1000/&2000 as expected.  I now check
;			the type with OS_File instead of relying on the
;			type in the message.
;
; 1.08		MDW	Reduced size of prompt in `merge' template and
;			indirected data buffer.  Image now less than 4K.
;			Made some other space-saving modifications, although
;			I can't reduce the memory requirements to less than
;			8K, which is a shame.
;
; 1.09		MDW	Changed LDRs to ADRs when finding version and
;			copyright strings, now that the linker can handle it.
;
; 1.10		MDW	Moved all messages into a separate file so it can
;			be easily changed for different countries should the
;			need arise.  This is converted to an AOF file and
;			linked in during the build.
;
; 1.11		MDW	Changed dm_error to base filler strings on `%0' not
;			`%1', and updated messages in line with this.

;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

		GET	libs:stream

;----- External dependencies ------------------------------------------------

		; --- Embedded template handling ---

		GET	libs:embTemp.sh.embTemp

		; --- Linker symbols ---

		IMPORT	|Image$$RW$$Limit|

		; --- Linked resources ---

		GET	sh.templates
		GET	sh.messages

		; --- Generated strings ---

		IMPORT	version
		IMPORT	cright

;----- Macros ---------------------------------------------------------------

		MACRO
$label		TWELVE	$string
		ASSERT	:LEN:"$string"<=12
		ALIGN
$label		DCB	"$string"
		%	12-:LEN:"$string"
		MEND

;----- Icon numbers ---------------------------------------------------------

dm_INFO_NAME	EQU	7
dm_INFO_PURPOSE	EQU	5
dm_INFO_AUTHOR	EQU	3
dm_INFO_VERSION	EQU	1

dm_MERGE_DEST	EQU	2
dm_MERGE_SOURCE	EQU	3
dm_MERGE_MSG	EQU	5

;----- Main code ------------------------------------------------------------

		AREA	|DLLMerge$$Code|,CODE,READONLY
		ENTRY

; --- main ---
;
; On entry:	--
; On exit:	Doesn't

main		ROUT

		BL	dm_init
		BL	dm_initWimp
		BL	dm_setHandlers
returnPoint	BL	dm_poll
exitPoint	BL	dm_die
		SWI	OS_Exit

		LTORG

; --- dm_init ---
;
; On entry:	--
; On exit:	R12 and R13 set up, other registers corrupted

dm_init		ROUT

		; --- Set up application workspace ---

		ADRL	R12,|Image$$RW$$Limit|	;Find limit of this program
		SWI	OS_GetEnv		;Find info about application
		MOV	R13,R1			;Start stack at top of slot
		SUB	R0,R13,R12		;Find how mich space I have
		LDR	R1,=dm_wsize		;How much do I need?
		CMP	R0,R1			;Do I have enough?
		ADRLTL	R0,msg_errNoMem		;No -- point to the error...
		SWILT	OS_GenerateError	;... and die

		; --- Initialise workspace contents ---

		MOV	R0,#0			;No flags set yet
		STR	R0,dm_flags		;Store that away

		MOVS	PC,R14			;Return to caller

		LTORG

; --- dm_initWimp ---
;
; On entry:	--
; On exit:	Registers corrupted

dm_initWimp	ROUT

		STMFD	R13!,{R14}		;Keep link register

		; --- Initialise WindowManager ---

		MOV	R0,#200			;WindowManager version
		LDR	R1,=&4B534154		;Magic TASK number
		ADR	R2,dm__name		;Point to my name
		SWI	Wimp_Initialise		;Start up the WIMP

		; --- Copy the menu over ---

		ADR	R0,dm_menuBk		;Point to output block
		ADR	R1,dm_menuDef		;Point to the definition
		MOV	R2,#dm_menuSize		;Get the size too
00		SUBS	R2,R2,#16		;I'll do 16 at a time
		LDMCSIA	R1!,{R3-R6}		;Load some bytes
		STMCSIA	R0!,{R3-R6}		;Save them out
		BCS	%b00			;If more to do, loop
		ADD	R2,R2,#16		;We must have overshot
00		SUBS	R2,R2,#4		;Now do one word at a time
		LDRCS	R14,[R1],#4		;Load the word
		STRCS	R14,[R0],#4		;And store it out
		BCS	%b00			;And loop back again

		; --- Load required windows ---

		MOV	R1,#0			;Not loaded any templates

		ADRL	R0,tpl_progInfo		;Find the template
		BL	dm_loadTemplate		;Load a window
		STR	R0,dm_infohand		;Store that away nicely
		STR	R0,dm_menuBk+28+0*24+4	;And store as Info... submenu

		ADRL	R0,tpl_merge		;Point to template name
		BL	dm_loadTemplate		;Load a window
		STR	R0,dm_windhand		;Store that away nicely

		; --- Fill in the Info window fields ---

		LDR	R0,dm_infohand
		MOV	R1,#dm_INFO_NAME
		ADR	R2,dm__name
		BL	dm_setField
		MOV	R1,#dm_INFO_PURPOSE
		ADRL	R2,msg_infoPurpose
		BL	dm_setField
		MOV	R1,#dm_INFO_AUTHOR
		ADRL	R2,cright
		BL	dm_setField
		MOV	R1,#dm_INFO_VERSION
		ADRL	R2,version
		BL	dm_setField

		; --- Open the merge window ---

		ADR	R1,dm_pollbk		;Point to a work block
		LDR	R0,dm_windhand		;Get the window's handle
		STR	R0,[R1,#0]		;Store in the block
		SWI	Wimp_GetWindowState	;Find the position of window
		MOV	R2,#-1			;Move the window to front
		STR	R2,[R1,#28]		;Store in `behind' field
		SWI	Wimp_OpenWindow		;Open the window

		LDMFD	R13!,{PC}^		;Return to caller

dm__name	DCB	"DLLMerge",0

dm_menuDef	TWELVE	"DLLMerge"
		DCB	7,2,7,0
		DCD	16*7+16
		DCD	44
		DCD	0

		DCD	&00000000
		DCD	0
		DCD	&07000131
		DCD	msg_menuInfo,-1,1

		DCD	&00000080
		DCD	-1
		DCD	&07000131
		DCD	msg_menuQuit,-1,1

dm_menuSize	EQU	{PC}-dm_menuDef

		LTORG

; --- dm_setHandlers ---
;
; On entry:	--
; On exit:	Registers corrupted

dm_setHandlers	ROUT

		; --- Find somewhere to go on an exception ---

		STR	R13,dm_exceptStack	;Save this stack pointer away

		; --- Set up the error handler ---

		STMFD	R13!,{R14}
		ADR	R11,dm_handlers		;Point to handler buffer

		MOV	R0,#6			;Set up error handler
		ADR	R1,dm_errors		;Point to the error handler
		MOV	R2,R12			;Set up the workspace ptr
		ADR	R3,dm_stacklim		;Use stack base as error buff
		SWI	OS_ChangeEnvironment	;Set up the handler
		STMIA	R11!,{R1-R3}		;Save the old information

		; --- Set up other handlers to quit the program ---

		MOV	R0,#11			;Set up exit handler
		ADR	R1,dm_exits		;Point to the handler
		MOV	R2,R12			;Point to my workspace
		MOV	R3,#0			;No buffer pointer
		SWI	OS_ChangeEnvironment	;Set up the handler
		STMIA	R11!,{R1-R3}		;Save the old information

		MOV	R0,#16			;Set up UpCall handler
		ADR	R1,dm_upcalls		;Point to the handler
		MOV	R2,R12			;Point to my workspace
		MOV	R3,#0			;No buffer pointer
		SWI	OS_ChangeEnvironment	;Set up the handler
		STMIA	R11!,{R1-R3}		;Save the old information

		LDMFD	R13!,{PC}^		;Return to caller

		LTORG

; --- dm_killHandlers ---
;
; On entry:	--
; On exit:	Registers preserved

dm_killHandlers	ROUT

		STMFD	R13!,{R0-R3,R11,R14}	;Save registers

		ADR	R11,dm_handlers		;Point to handlers buffer

		MOV	R0,#6
		LDMIA	R11!,{R1-R3}
		SWI	XOS_ChangeEnvironment

		MOV	R0,#11
		LDMIA	R11!,{R1-R3}
		SWI	XOS_ChangeEnvironment

		MOV	R0,#16
		LDMIA	R11!,{R1-R3}
		SWI	XOS_ChangeEnvironment

		LDMFD	R13!,{R0-R3,R11,PC}^	;Return to caller

		LTORG

; --- dm_errors ---
;
; On entry:	R0 == pointer to my workspace
; On exit:	Doesn't -- goes back to main loop at appropriate point

dm_errors	ROUT

		; --- Reset stack and workspace pointers ---

		MOV	R12,R0			;Point to my workspace
		LDR	R13,dm_exceptStack	;Restore my stack pointer

		; --- Is this a double exception? ---

		LDR	R0,dm_flags		;Get my flags
		TST	R0,#dm_ERROR		;Is this a repeated error?
		BNE	%00dm_errors		;Yes -- handle this specially

		; --- Mark that we're in the error handler ---

		ORR	R0,R0,#dm_ERROR		;Say we're in the handler
		STR	R0,dm_flags		;Store this in the flags

		; --- Report the error and give a chance to quit ---

		ADRL	R1,dm_stacklim+8	;Point to main error message
		ADRL	R0,msg_errInternal	;Point to error skeleton
		BL	dm_error		;Construct a message
		MOV	R1,#&00000003		;Provide OK and Cancel boxes
		ADR	R2,dm__name		;Point to my name
		SWI	Wimp_ReportError	;Report the error
		CMP	R1,#1			;Was OK clicked?
		BEQ	%01dm_errors		;Yes -- return to main loop

		ADRL	R0,msg_errConfirm	;Point to the confirm message
		MOV	R1,#3			;OK and Cancel buttons again
		ADR	R2,dm__name		;Point to my name
		SWI	Wimp_ReportError	;Display the question
		CMP	R1,#1			;Was OK clicked?
		BEQ	%01dm_errors		;Yes -- return to main loop

		B	exitPoint		;Commit hari-kiri then

		; --- We can continue ---

01dm_errors	LDR	R0,dm_flags		;Get my flags
		BIC	R0,R0,#dm_ERROR		;There's no error any more
		STR	R0,dm_flags		;Store the flags away
		B	returnPoint		;Go back into the top level

00dm_errors	BL	dm_killHandlers		;Restore all the handlers
		ADRL	R0,dm_stacklim+4	;Point to the error buffer
		SWI	OS_GenerateError	;Let the OS handle it all

		LTORG

; --- dm_exits ---
;
; On entry:	--
; On exit:	Doesn't -- branches to main loop

dm_exits	LDR	R13,dm_exceptStack	;Get my stack pointer
		B	exitPoint		;Close down the application

; --- dm_upcalls ---
;
; On entry:	R0 == UpCall type
; On exit:	Varies

dm_upcalls	CMP	R0,#256			;Is a new app starting?
		MOVNES	PC,R14			;No -- return to caller

                ; --- Hopefully this will never happen :-) ---

		MOVS	PC,R14			;Return to caller and hope

; --- dm_die ---
;
; On entry:	--
; On exit:	Registers corrupted

dm_die		ROUT

		STMFD	R13!,{R14}
		BL	dm_killHandlers		;Get rid of any handlers
		SWI	Wimp_CloseDown		;Stop being a Wimp task
		LDMFD	R13!,{PC}^		;Return to caller

		LTORG

; --- dm_poll ---
;
; On entry:	--
; On exit:	Registers corrupted

dm_poll		ROUT

		STMFD	R13!,{R14}		;Keep return address

		; --- Find out if we need continue ---

00dm_poll	LDR	R0,dm_flags		;Get the flags word
		TST	R0,#dm_QUIT		;Check the quit bit
		LDMNEFD	R13!,{PC}^		;Return if it failed

		; --- Get an event and dispatch it ---

		AND	R0,R0,#dm_SOURCE :OR: dm_DONE ;What's my status?
		CMP	R0,#dm_SOURCE		;If not complete...
		MOVEQ	R0,#0			;... wait for an idle event
		MOVNE	R0,#1			;Otherwise, ignore idles
		ADR	R1,dm_pollbk		;Point to the big block
		SWI	Wimp_Poll		;Get an event

		CMP	R0,#19			;Check the event is sensible
		MOV	R14,PC			;Set up return address
		ADDLS	PC,PC,R0,LSL #2		;If so, go to branch table
		B	%00dm_poll		;Silly event -- ignore it

		; --- Event dispatching table ---

		B	dm_null			;Null_Reason_Code
		B	dm_redraw		;Redraw_Window_Request
		B	dm_open			;Open_Window_Request
		B	dm_close		;Close_Window_Request
		MOVS	PC,R14			;Pointer_Leaving_Window
		MOVS	PC,R14			;Pointer_Entering_Window
		B	dm_click		;Mouse_Clicked
		MOVS	PC,R14			;User_Drag_Box
		MOVS	PC,R14			;Key_Pressed
		B	dm_menu			;Menu_Selection
		MOVS	PC,R14			;10
		MOVS	PC,R14			;11
		MOVS	PC,R14			;12
		MOVS	PC,R14			;13
		MOVS	PC,R14			;14
		MOVS	PC,R14			;15
		MOVS	PC,R14			;16
		B	dm_message		;User_Message
		B	dm_message		;User_Message_Recorded
		MOVS	PC,R14			;User_Message_Acknowledge

		LTORG

;----- Event handlers -------------------------------------------------------

; --- Null_Reason_Code ---
;
; We copy the source to the destination

dm_null		ROUT

		STMFD	R13!,{R14}		;Stack the link register
		MOV	R0,#26			;We want to do a copy
		ADR	R1,dm_source		;Point to source app
		ADR	R2,dm_dest		;Point to destination app
		LDR	R3,=&00005003		;Copy options
		SWI	XOS_FSControl		;Do the copy operation
		BVS	%00dm_null		;If it failed, display msg

		; --- Update the DLL$Path variable ---

		MOV	R0,R2			;Point to destination name
		SWI	Wimp_StartTask		;Run its !Run file

		; --- Put the text into the message field ---

		LDR	R0,dm_windhand		;Get the window handle
		MOV	R1,#dm_MERGE_MSG	;Get the icon handle
		ADRL	R2,msg_stateDone	;Point to message
		BL	dm_setField		;Write the text in

		; --- Update the flags and quit ---

		LDR	R0,dm_flags		;Get the flags word
		ORR	R0,R0,#dm_DONE		;We've finished
		STR	R0,dm_flags		;Write the flags back
		LDMFD	R13!,{PC}^		;Return to caller

		; --- It failed.  Report an error and start again ---

00dm_null	ADD	R1,R0,#4		;Point to the message
		ADRL	R0,msg_errCopy		;Point to the error skeleton
		BL	dm_error		;Fill it in
		MOV	R1,#1			;Only have an OK box
		ADR	R2,dm__name		;Point to my name
		SWI	Wimp_ReportError	;Make an error

		LDR	R0,dm_windhand		;Get the window handle
		MOV	R1,#dm_MERGE_DEST	;Get the icon handle
		ADR	R2,dm__empty		;Point to message
		BL	dm_setField		;Write the text in
		MOV	R1,#dm_MERGE_SOURCE	;Get the icon handle
		BL	dm_setField		;Write the text in
		MOV	R1,#dm_MERGE_MSG	;Get the icon handle
		ADRL	R2,msg_stateErr		;Point to message
		BL	dm_setField		;Write the text in

		MOV	R0,#0			;Clear all the flags
		STR	R0,dm_flags		;Store them away

		LDMFD	R13!,{PC}^

dm__empty	DCB	0

		LTORG

; --- Redraw_Window_Request ---
;
; We just bundle the redraw event off to Sculptrix and forget about it

dm_redraw	ROUT

		SWI	Wimp_RedrawWindow
00dm_redraw	CMP	R0,#0
		MOVEQS	PC,R14
		SWI	XSculptrix_RedrawWindow
		SWI	Wimp_GetRectangle
		B	%00dm_redraw

		LTORG

; --- Open_Window_Request ---
;
; Just open the window.  Nothing could be easier ---

dm_open		ROUT

		SWI	Wimp_OpenWindow
		MOVS	PC,R14

		LTORG

; --- Close_Window_Request ---
;
; We close down the application.

dm_close	ROUT

		LDR	R0,dm_flags
		ORR	R0,R0,#dm_QUIT
		STR	R0,dm_flags
		MOVS	PC,R14

		LTORG

; --- Mouse_Clicked ---
;
; If it's the Info window, we start moving it about.  If it's the Merge
; window, we open a menu

dm_click	ROUT

		LDR	R0,[R1,#12]		;Get the window handle
		LDR	R2,dm_windhand		;Is it the Merge window?
		CMP	R0,R2			;Find out
		BEQ	%10dm_click		;If so, handle it there
		LDR	R2,dm_infohand		;It may be the Info window
		CMP	R0,R2			;So check
		BEQ	%20dm_click		;If so, handle it
		MOVS	PC,R14			;Not recognised -- ignore it

		; --- Handle a click on the Merge window ---

10dm_click	LDR	R0,[R1,#8]		;Get the button status
		TST	R0,#2			;Check the menu button bit
		MOVEQS	PC,R14			;If not, return

		LDMIA	R1,{R2,R3}		;Get coordinates from block
		SUB	R2,R2,#64		;Displace the menu
		ADR	R1,dm_menuBk		;Point to the menu defn
		SWI	Wimp_CreateMenu		;Display the menu
		MOVS	PC,R14			;Return to caller

		; --- Handle a click on the Info window ---

20dm_click	LDR	R0,[R1,#8]		;Get the button status
		TST	R0,#5			;Check for Select or Adjust
		MOVEQS	PC,R14			;If not, return

		SUB	R13,R13,#56		;Make a drag info block
		LDR	R0,dm_infohand		;Get the Info window handle
		STR	R0,[R13,#0]		;Store the window handle
		MOV	R0,#1			;Move the window
		STR	R0,[R13,#4]		;Store the drag type
		MOV	R1,R13			;Point to the block
		SWI	Wimp_DragBox		;Start the window moving
		ADD	R13,R13,#56		;Reclaim the stack space
		MOVS	PC,R14			;Return to caller

		LTORG

; --- Menu_Selection ---
;
; If it's Info, open the Info box.  If it's Quit, then quit.

dm_menu		ROUT

		LDR	R0,[R1,#0]		;Get toplevel selection
		CMP	R0,#0			;Is it `Info...'?
		BEQ	%10dm_menu		;Yes -- process it
		CMP	R0,#1			;Is it `Quit'?
		BEQ	dm_close		;Yes -- quit the program
		MOVS	PC,R14			;Return to caller

		; --- Open the Info... window ---

10dm_menu	SUB	R13,R13,#36		;Get some workspace
		MOV	R1,R13			;Point to this new block
		SWI	Wimp_GetPointerInfo	;Where is the mouse now?
		LDMIA	R1,{R2,R3}		;Get the mouse coordinates
		LDR	R0,dm_infohand		;Get the window handle
		STR	R0,[R1,#0]		;Store the window handle
		SWI	Wimp_GetWindowState	;Get the window's information
		LDMIA	R1,{R1,R4-R7}		;Get the window positions
		SUB	R4,R6,R4		;Get the window width
		SUB	R5,R7,R5		;And the window height
		SUB	R2,R2,R4,LSR #1		;Centre window over X
		ADD	R3,R3,R5,LSR #1		;Centre window over Y
		ADD	R13,R13,#36		;Reclaim the stack space
		SWI	Wimp_CreateMenu		;Display the Info window
		MOVS	PC,R14			;And return to caller

		LTORG

; --- User_Message and User_Message_Recorded ---
;
; Handle Quit messages, and files dropped on the window

dm_message	ROUT

		LDR	R0,[R1,#16]		;Get message type
		CMP	R0,#0			;Is it a Message_Quit?
		BEQ	dm_close		;This has the code for quit
		CMP	R0,#3			;Is it a load message?
		BEQ	%10dm_message		;Yes -- deal with it
		MOVS	PC,R14			;If not interested, return

		; --- Someone dropped a file on my window ---

10dm_message	LDR	R0,[R1,#20]		;Where was the file dropped?
		LDR	R11,dm_windhand		;Get my window handle
		CMP	R0,R11			;Do they match?
		MOVNES	PC,R14			;No -- ignore it

		; --- Check what to do with it ---

		LDR	R10,dm_flags		;Get my flags word
		TST	R10,#dm_SOURCE		;Have I finished?
		MOVNES	PC,R14			;Yes -- not interested then

		; --- Got to do something now ---

		STMFD	R13!,{R14}		;Stack link register now
		BL	dm_checkFile		;Make sure it's a !DLLs
		CMP	R0,#0			;Did it fail?
		LDMEQFD	R13!,{PC}^		;Yes -- return to poll loop

		; --- Which filename do I fill in? ---

		TST	R10,#dm_DEST		;Have I got one filename?
		BNE	%20dm_message		;Yes -- fill in the other

		; --- Fill in the first filename ---

		ADD	R1,R1,#44		;Point to filename
		ADR	R0,dm_dest		;That's the destination file
		BL	dm_strcpy		;Copy it across
		MOV	R2,R1			;Point to name again
		MOV	R1,#dm_MERGE_DEST+(1<<31)
		MOV	R0,R11			;And set up the window handle
		BL	dm_setField		;Fill that icon in

		; --- Now display a new message ---

		ADRL	R2,msg_stateSource	;Point to the new message
		MOV	R1,#dm_MERGE_MSG	;Put it in the message icon
		BL	dm_setField		;Fill that icon in

		ORR	R10,R10,#dm_DEST	;Remember we've got a name
		STR	R10,dm_flags		;Store the new flags away
		B	%30dm_message		;Reply to the message now

		; --- Fill in the second filename ---

20dm_message	ADD	R1,R1,#44		;Point to filename
		ADR	R0,dm_source		;That's the source file
		BL	dm_strcpy		;Copy it across
		MOV	R2,R1			;Point to name again
		MOV	R1,#dm_MERGE_SOURCE+(1<<31)
		MOV	R0,R11			;And set up the window handle
		BL	dm_setField		;Fill that icon in

		; --- Now display a new message ---

		ADRL	R2,msg_stateGoing	;Point to the new message
		MOV	R1,#dm_MERGE_MSG	;Put it in the message icon
		BL	dm_setField		;Fill that icon in
		ORR	R10,R10,#dm_SOURCE	;Remember we've got a name
		STR	R10,dm_flags		;Store the new flags away

		; --- Reply to the message in the block ---

30dm_message	ADR	R1,dm_pollbk		;Point to the message
		LDR	R0,[R1,#8]		;Get his reference
		STR	R0,[R1,#12]		;Store as his reference
		MOV	R0,#4			;This is an acknowledgement
		STR	R0,[R1,#16]		;Store as the message type
		MOV	R0,#17			;Don't bother recording it
		LDR	R2,[R1,#4]		;Get his task handle
		SWI	Wimp_SendMessage	;Reply to his message now
		LDMFD	R13!,{PC}^		;Return to the caller

		LTORG

;----- Support routines -----------------------------------------------------

; --- dm_setField ---
;
; On entry:	R0 == window handle
;		R1 == icon handle
;		R2 == string to write
; On exit:	Everything preserved unless there was an error

dm_setField	ROUT

		STMFD	R13!,{R0-R5,R14}	;Stash registers away

		; --- Find out about the icon ---

		AND	R4,R1,#&FF000000	;Get the flag bits out
		BIC	R1,R1,#&FF000000	;Leave just the icon number
		SUB	R13,R13,#40		;Make space for icon block
		STMIA	R13,{R0,R1}		;Store the info in it
		MOV	R1,R13			;Point to the icon block
		SWI	Wimp_GetIconState	;Get the icon's information

		; --- Make sure we can change the text ---

		LDR	R1,[R13,#24]		;Get the icon's flags

		; --- Now find how much we actually have to copy ---

		LDR	R5,[R13,#36]		;Get the buffer length
		SUB	R5,R5,#1		;Take terminator into account
		MOV	R0,R2			;Point to the string to copy
		BL	dm_strlen		;Find out how long it is
		SUBS	R0,R0,R5		;Find out the difference
		BICLE	R4,R4,#(1<<31)		;If it fits, don't add dots
		BLE	%00dm_setField		;And skip ahead
		TST	R1,#1<<9		;Is it right aligned?
		ADDNE	R2,R2,R0		;Yes, chop off front
		ORRNE	R4,R4,#1		;And set a flag to remember

		; --- Copy the text into the buffer ---

00dm_setField	LDR	R0,[R13,#28]		;Find the buffer address
		MOV	R3,#0			;Count the length too

10dm_setField	CMP	R5,R3			;How much space left in buff?
		MOVLE	R1,#0			;None -- pretend null char
		LDRGTB	R1,[R2],#1		;Get a byte from the string
		CMP	R1,#' '			;Is it a control char?
		MOVLO	R1,#0			;Yes -- say it's a zero
		BLO	%15dm_setField		;And don't bother with dots

		; --- Handle ellipsis generation ---

		TST	R4,#(1<<31)		;Do we put the ellipsis in?
		BEQ	%15dm_setField		;No -- skip ahead then
		TST	R4,#1			;Are we right-justified?
		ADDNE	R14,R3,#1		;Yes -- just get the length
		SUBEQ	R14,R5,R3		;Otherwise find what's left
		CMP	R14,#4			;Are we within three?
		MOVLO	R1,#'.'			;Yes -- put in a dot then

		; --- Return to normality ---

15dm_setField	LDRB	R14,[R0],#1		;Get one from the buffer
		CMP	R14,#' '		;Same for the buffer char
		MOVLO	R14,#0

		CMP	R1,R14			;Are they different
		ORRNE	R4,R4,#2		;Yes -- remember this
		STRNEB	R1,[R0,#-1]		;And store the different char

		CMP	R1,#0			;Is that end of the string?
		ADDNE	R3,R3,#1		;No -- bump the length on
		BNE	%10dm_setField		;And go round for another

		; --- We've copied the string -- now update the icon ---

		TST	R4,#2			;Is the string different?
		BEQ	%20dm_setField		;No -- skip ahead

		MOV	R1,#0
		STR	R1,[R13,#8]		;The EOR mask for setstate
		STR	R1,[R13,#12]		;The BIC mask for setstate
		MOV	R1,R13			;Point to the block
		SWI	Wimp_SetIconState	;Flicker the icon nastily

		; --- Now check for the caret ---

		SWI	Wimp_GetCaretPosition	;Find out where the caret is
		LDMIA	R13,{R2,R4}		;Get the window and icon
		ADD	R0,R13,#40		;Point past this block
		LDMIA	R0,{R0,R1}		;Get the old window and icon
		CMP	R0,R2			;Do the window handles match?
		CMPEQ	R1,R4			;And the icon handles?
		BNE	%20dm_setField		;No -- skip ahead

		; --- Push the caret back a little ---

		LDR	R5,[R13,#20]		;Get the caret index
		CMP	R5,R3			;Is this bigger than new len?
		MOVGT	R5,R3			;Yes -- trim the index

		; --- Now put the caret in the right place ---

		MOV	R2,#-1			;Don't set the x coord
		MOV	R3,#-1			;Don't set the y coord
		MOV	R4,#-1			;Don't set the height
		SWI	Wimp_SetCaretPosition	;Put the caret in its place

		; --- Return nicely ---

20dm_setField	ADD	R13,R13,#40		;Reclaim that temporary space
		LDMFD	R13!,{R0-R5,PC}^	;Return to caller

		LTORG

; --- dm_checkFile ---
;
; On entry:	R1 == pointer to message block
; On exit:	R0 == 0 if it *wasn't* a DLL folder

dm_checkFile	ROUT

		STMFD	R13!,{R1-R5,R14}	;Stack registers

		; --- Ensure it's a directory ---
		;
		; Kysmet seems to ignore the fact that it might be an image
		; and returns &FC0 for links, instead of &1000/&2000 which
		; the Filer gives you.  So we examine the file using OS_File
		; and check bit 1 of the object type from that.

		ADD	R1,R1,#44		;Find the name
		MOV	R0,#17			;Try to read information
		SWI	XOS_File		;Read the file information
		MOVVS	R0,#0			;Say not there if it failed
		TST	R0,#2			;Is it a directory?
		BEQ	%10dm_checkFile		;No -- then complain

		; --- Now find the leafname ---

		MOV	R0,R1			;Keep pointer to name
01dm_checkFile	LDRB	R14,[R1],#1		;Get a character
		CMP	R14,#'.'		;Is it a dot?
		MOVEQ	R0,R1			;Yes -- this it the leafname
		CMP	R14,#' '		;Is it the end?
		BGE	%01dm_checkFile		;No -- go round again

		; --- Compare this to what it should be ---

		ADR	R1,dm__dlls		;Point to the ideal one
		MOV	R2,#0			;Not case-sensitive
		BL	dm_strcmp		;Is it a match?
		BNE	%10dm_checkFile		;No -- be unhappy

		MOV	R0,#1			;It was a real !DLLs resource
		LDMFD	R13!,{R1-R5,PC}^	;Return to caller

10dm_checkFile	ADRL	R0,msg_errNotDLL	;Point to error message
		LDR	R1,[R13,#0]		;Get the message pointer
		ADD	R1,R1,#44		;Find the name string
		BL	dm_error		;Fill in the error
		MOV	R1,#1			;Just an OK box please
		ADRL	R2,dm__name		;Point to my name
		SWI	Wimp_ReportError	;Report the error
		MOV	R0,#0			;It wasn't a real one
		LDMFD	R13!,{R1-R5,PC}^	;Return to caller

dm__dlls	DCB	"!DLLs",0

		LTORG

; --- dm_loadTemplate ---
;
; On entry:	R0 == pointer to template
;		R1 == 0 on first call, or R1 from previous
; On exit:	R0 == window handle
;		R1 == a new magic number

dm_loadTemplate	ROUT

		STMFD	R13!,{R2-R5,R14}	;Stack registers

		; --- Set up indirected data pointer ---

		CMP	R1,#0			;Is this the first call?
		ADREQL	R2,dm_indspace		;Yes -- point to buff start
		MOVNE	R2,R1			;Set up indirected data ptr

		; --- Load template into buffer ---

		ADRL	R3,dm_eindspace		;Point to buff end
		ADR	R1,dm_pollbk		;Point to poll block
		MOV	R4,#1			;Use the Wimp sprite area
		MOV	R5,#1			;I really mean that
		BL	embTemp_extract		;Extract the template

		; --- Create the window and return ---

		SWI	Wimp_CreateWindow	;Create the window
		MOV	R1,R2			;Get magic number to return
		LDMFD	R13!,{R2-R5,PC}^	;Return to caller

		LTORG

; --- dm_strlen ---
;
; On entry:	R0 == pointer to string
; On exit:	R0 == length of string

dm_strlen	ROUT

		STMFD	R13!,{R1,R14}		;Save some registers
		MOV	R1,#0			;Start the counter nicely
00dm_strlen	LDRB	R14,[R0],#1		;Load a byte from the string
		ADD	R1,R1,#1		;Bump on the counter
		CMP	R14,#32			;Is it the end?
		BHS	%00dm_strlen		;No -- go round again then
		SUB	R0,R1,#1		;Return count of actual chars
		LDMFD	R13!,{R1,PC}^		;Return to caller

		LTORG

; --- dm_strcpy ---
;
; On entry:	R0 == destination string
; 		R1 == source string
; On exit:	R0 == pointer to terminator of destination

dm_strcpy	ROUT

		STMFD	R13!,{R1,R14}		;Keep return address safe
00dm_strcpy	LDRB	R14,[R1],#1		;Get a byte from source
		CMP	R14,#' '		;Is it a control character
		MOVLT	R14,#0			;Yes -- translate to a 0
		STRB	R14,[R0],#1		;Store in destination
		BGE	%00dm_strcpy		;No -- copy another byte
		SUB	R0,R0,#1		;Point back at terminator
		LDMFD	R13!,{R1,PC}^		;Return to caller

		LTORG

; --- dm_strcmp ---
;
; On entry:	R0 == pointer to string A
; 		R1 == pointer to string B
;		R2 == 0 => case insensitive, 1 => case sensitive
;
; On exit:	Flags as appropriate

dm_strcmp	ROUT

		STMFD	R13!,{R0,R1,R3,R4,R14}
00dm_strcmp	LDRB	R3,[R0],#1		;Get a character from A
		LDRB	R4,[R1],#1		;And one from B

		CMP	R2,#0			;Do we want to do case xlate?
		BNE	%10dm_strcmp		;No -- miss it out then

		SUB	R14,R3,#'a'		;Subtract the bottom limit
		CMP	R14,#26			;Is it a lower case letter?
		BICLO	R3,R3,#&20		;Yes -- convert to upper
		SUB	R14,R4,#'a'		;Subtract the bottom limit
		CMP	R14,#26			;Is it a lower case letter?
		BICLO	R4,R4,#&20		;Yes -- convert to upper

10dm_strcmp	CMP	R3,#&20			;Is that the end of A?
		MOVLO	R3,#0			;Yes -- pretend it's null
		CMP	R4,#&20			;Is that the end of B?
		MOVLO	R4,#0			;Yes -- pretend it's null

		CMP	R3,R4			;How do they match up?
		LDMNEFD	R13!,{R0,R1,R3,R4,PC}	;If NE, return condition
		CMP	R3,#0			;Is this the end?
		BNE	%00dm_strcmp		;No -- loop again
		LDMFD	R13!,{R0,R1,R3,R4,PC}	;Return to caller

		LTORG

; --- dm_error ---
;
; On entry:	R0 == Pointer to error message skeleton
;		R1 == Filler 1
;		R2 == Filler 2
;		R3 == Filler 3
;		R4 == Filler 4
; On exit:	R0 == Pointer to constructed error in dm_errorbuf

		EXPORT	dm_error
dm_error	ROUT

		STMFD	R13!,{R1-R6,R14}
		ADR	R5,dm_pollbk		;Point to error buffer
		LDR	R14,[R0],#4		;Read the error's number
		STR	R14,[R5],#4		;And store in the new buffer

00dm_error	LDRB	R14,[R0],#1		;Get an input character
		CMP	R14,#'%'		;Is it a '%' sign?
		BEQ	%01dm_error		;Yes -- deal with it
02dm_error	STRB	R14,[R5],#1		;Not special, so store it
		CMP	R14,#0			;Is it the end of input?
		BNE	%00dm_error		;No -- get another one
		ADR	R0,dm_pollbk		;Point to error start
		LDMFD	R13!,{R1-R6,PC}^	;And return to caller

01dm_error	LDRB	R14,[R0],#1		;Get the next character
		SUB	R14,R14,#'0'		;Convert to binary (0..3)
		CMP	R14,#4			;Is it in range?
		BCS	%02dm_error		;No -- just ignore the '%'
		LDR	R6,[R13,R14,LSL #2]	;Load appropriate register

03dm_error	LDRB	R14,[R6],#1		;Get an input byte
		CMP	R14,#&20		;Is it the end of the string?
		BLT	%00dm_error		;Yes -- read main string
		STRB	R14,[R5],#1		;No -- store it in output
		B	%03dm_error		;... and get another one

		LTORG

;----- Workspace layout -----------------------------------------------------

		^	0,R12

dm_wstart	#	0

dm_exceptStack	#	4

dm_windhand	#	4
dm_infohand	#	4

dm_flags	#	4

dm_handlers	#	36

dm_menuBk	#	dm_menuSize

dm_QUIT		EQU	1<<0
dm_DEST		EQU	1<<1
dm_SOURCE	EQU	1<<2
dm_DONE		EQU	1<<3
dm_ERROR	EQU	1<<4

dm_pollbk	#	256
dm_dest		#	256
dm_source	#	256

dm_indspace	#	384
dm_eindspace	#	0

dm_stacklim	#	288

dm_wend		#	0

dm_wsize	EQU	dm_wend-dm_wstart

;----- That's all folks -----------------------------------------------------

		END
