#multipass on
; Image
; ~~~~~
; Image mounting/umounting and low-level filesystem manipulation.


.Image_Mount
FNdt(">>> Image_Mount")
FNdt("  r1 (fileswitch image file handle) = %1x8")
FNdt("  r2 (buffer size if known) = %2i4")
;------------------------
; Mount the given image.
;------------------------
; On entry:
;	r0  =	21
;	r1  =	FileSwitch handle of image file
;	r2  =	Buffer size for file if known, otherwise 0
; On exit:
;	r1  =	IscaFS handle for image file
;	AORP
;------------------------
	FNfunction("r1")
;------------------------
; create block and link into chain
	MOV	r3,#len_mount%
	BL	Memory_Claim
	MOV	r6,r2			; r6 now points at mnt_*
	STR	r2,[r13]		; return in r1
	STR	r1,[r6,#mnt_filehandle%]
;------------------------
; check to see if it's a pseudo-image
	MOV	r0,#3
	ADD	r2,r6,#superblock%
	MOV	r3,#8
	MOV	r4,#0
	SWI	"XOS_GBPB"
	BVS	_bad_image
	LDMDB	r2,{r0,r3}
	ADD	r2,r6,#superblock%
	LDR	r4,_special_words
	TEQ	r4,r0
	LDREQ	r4,_special_words+4
	TEQ	r4,r3
	BEQ	_pseudo_image
	FNladr(0, Image_ReadBytes_File)
	STR	r0,[r6,#mnt_readbytes%]
	FNladr(0, Image_WriteBytes_File)
	STR	r0,[r6,#mnt_writebytes%]
	MOV	r0, #mntflag_writeable%
	STR	r0, [r6, #mnt_flags%]
;------------------------
; load in enough of the superblock
	MOV	r0,#3
	MOV	r3,#len_superblock%
	MOV	r4,#1024		; position of first superblock
	SWI	"XOS_GBPB"
	BVS	_bad_image
	TEQ	r3,#0
	BNE	_bad_image
;------------------------
; check if the magic word (ef53) is correct.
._check_image
FNdt("_check_image")
	LDR	r3,[r6,#s_magic%]
	MOV	r3, r3, LSL #16		; move r3 to the top half
	EOR	r3, r3, #&ef000000	; will be set to 0 if equal
	TEQ	r3, #&00530000		; ditto. (TEQ == EORS)
	BNE	_bad_image
;------------------------
; link in the new mount block
	LDR	r0,[r12,#firstmount]
	STR	r0,[r6,#next%]
	STR	r6,[r12,#firstmount]
	TEQ	r0,#0
	STRNE	r6,[r0,#last%]
	ADD	r3,r12,#firstmount
	STR	r3,[r6,#last%]
;------------------------
; claim memory for all the group descriptors and load them in
	LDR	r2,[r6,#s_blocks_count%]
	LDR	r3,[r6,#s_blocks_per_group%]
	BL	Divide			; r0 = r2/r3 ; r2 = r2%r3
	MOV	r7,r3
	TEQ	r2,#0
	ADDNE	r0,r0,#1
	MOV	r3,r0,LSL #shift_groupdesc%
	BL	Memory_Claim
	STR	r2,[r6,#mnt_groupdescs%]
	MOV	r9,r2
	STR	r0,[r6,#mnt_numgroups%]
	MOV	r5,r0			; number of groups to get info from
	MOV	r8,#2			; block number
._lp
	MOV	r0,r8
	ADD	r8,r8,r7		; set up for next block
	BL	Cache_GetBlock
	MOV	r1,r9
	ADD	r9,r9,#len_groupdesc%
	MOV	r2,#len_groupdesc%
	BL	Memory_BlockCopy
	SUBS	r5,r5,#1
	BGT	_lp
FNdt("<<< Image_Mount")
FNdt("  r1 (my file handle) = %1x8")
	FNreturn

._special_words
	EQUS	"hL"
._ext2_word
	EQUS	"ext2"

._error_return
	MOV	r2,r6
	BL	Memory_Free
	FNpreturn

._bad_image
FNdt("<<! Image_Mount (bad image)")
	MOV	r2,r6
	BL	Memory_Free
	FNrerror
	EQUD	0
	EQUS	"Not a valid ext2fs image."+CHR$0:ALIGN

._no_such_device
FNdt("<<! Image_Mount (no such device)")
	MOV	r2,r6
	BL	Memory_Free
	FNrerror
	EQUD	0
	EQUS	"The device this partition lives on is not active."+CHR$0:ALIGN

._filecore
	EQUS	"FileCore%"
._filesystem
	EQUS	"2341234123412341234123"+CHR$0

._pseudo_image
FNdt("_pseudo_image")
;------------------------
; sort out the read/write vectors
	ADR	r0,Image_ReadBytes_Partition
	STR	r0,[r6,#mnt_readbytes%]
	FNladr(0, Image_WriteBytes_ReadOnly)	; partitions are ALWAYS ro
	STR	r0,[r6,#mnt_writebytes%]
;------------------------
; note that it's a partition and get the sector information
	MOV	r0,#mntflag_partition%
	STR	r0,[r6,#mnt_flags%]
	MOV	r0,#3
	ADD	r2,r6,#mnt_startsector%
	MOV	r3,#16
	MOV	r4,#8
	SWI	"XOS_GBPB"
;------------------------
; get the FileCore filing system and read its instance word
	MOVVC	r0,#3
	ADRVC	r2, _filesystem
	MOVVC	r3,#22			; max bytes to read
	MOVVC	r4,#24
	SWIVC	"XOS_GBPB"
	FNpcreturn("VS")
	MOV	r0,#18
	ADR	r1,_filecore
	SWI	"XOS_Module"
	BVS	_no_such_device
	STR	r4,[r6,#mnt_instanceword%]
;------------------------
; get the FileCore descriptor block
	MOV	r3,#len_desc%		; has to be claimed from here since it
	MOV	r0,#6			; will be used shifted up
	SWI	"XOS_Module"
	STRVC	r2,[r6,#mnt_descriptor%]
	MOVVC	r1,r2
	ADDVC	r8,r6,#mnt_instanceword%
	LDRVC	r2,[r6,#mnt_drivenumber%]
	ADRVC	r0,_drive
	ADDVC	r2,r2,#ASC"0"		; NOTE - won't work on drives >9
	STRVCB	r2,[r0,#1]
	SWIVC	"XFileCore_DescribeDisc"
	BVS	_error_return
;------------------------
; alter the descriptor block so that it knows the disc is big enough
	LDR	r0,[r6,#mnt_lbtsectorlen%]
	LDR	r2,[r6,#mnt_endsector%]
	ADD	r2,r2,#1		; disc size is to the end of last sector
	MOV	r2,r2,LSL r0		; disc size in bytes
	STR	r2,[r1,#desc_disc_size%]
	LDR	r14, [r12, #debug]
	TST	r14, #1<<31		; is this old filecore
	TSTNE	r2,#&e0000000		; and if it is, is it too large?
	BNE	_partition_is_unreadable
;------------------------
; read the super block
	MOV	r1,r1,LSL #6		; descriptor shifted up
	ADD	r1,r1,#1
	MOV	r2,#1024		; disc address to read
	TST	r14, #1<<31		; EQ -> newfilecore, NE -> old
	MOVEQ	r2,r2,LSR r0		; mnt_lbtsectorlen%, from before
	LDR	r14,[r6,#mnt_startsector%]
	ADDEQ	r2,r2,r14
	ADDNE	r2,r2,r14,LSL r0	; mnt_lbtsectorlen%, from before
	LDR	r14,[r6,#mnt_drivenumber%]
	ADD	r2,r2,r14,LSL #29
	ADD	r3,r6,#superblock%
	MOV	r4,#len_superblock%
	SWIEQ	X+FileCore_SectorOp
	SWINE	"XFileCore_DiscOp"
	BVC	_check_image
	B	_error_return
;------------------------
; Check if the old FileCore is physically able to read all of the partition.
; Thanks to Matthew Wilcox for suggesting this.
._partition_is_unreadable
FNdt("<<! Image_Mount (partition is unreadable)")
	MOV	r2,r1
	MOV	r0,#7
	SWI	"OS_Module"
	MOV	r2,r6
	BL	Memory_Free
	FNrerror
	EQUD	0
	EQUS	"This partition is not readable by your version of FileCore."
	EQUB	0
	ALIGN

._drive
	EQUS	":0"+CHR$0:ALIGN



.Image_ReadBytes_File
FNdt(">>> Image_ReadBytes_File")
FNdt("  r2 (pointer to buffer) = %2x8")
FNdt("  r3 (bytes to read) = %3i4")
FNdt("  r4 (file offset) = %4i4")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Read bytes from the given image file
;------------------------
; On entry:
;	r2  =	pointer to buffer
;	r3  =	number of bytes to read
;	r4  =	file offset
;	r6  =	pointer to mount descriptor block
; On exit:
;	r3  =	number of bytes not read
;------------------------
	FNfunction("r0-r2,r4")
	MOV	r0,#3
	LDR	r1,[r6,#mnt_filehandle%]
	SWI	"XOS_GBPB"
FNdtc(vc, "<<< Image_ReadBytes_File")
	FNcreturn("VC")
FNdt("<<! Image_ReadBytes_File (OS_GBPB failed)")
	FNgerror


.Image_ReadBytes_Partition
FNdt(">>> Image_ReadBytes_Partition")
FNdt("  r2 (pointer to buffer) = %2x8")
FNdt("  r3 (bytes to read) = %3i4")
FNdt("  r4 (file offset) = %4i4")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Read bytes from the given partition
;------------------------
; On entry:
;	r2  =	pointer to buffer
;	r3  =	number of bytes to read
;	r4  =	partition offset
;	r6  =	pointer to mount descriptor block
; On exit:
;	r3  =	number of bytes not read
;------------------------
	FNfunction("r0-r2,r4-r5,r7")
	MOV	r5,r4
	MOV	r4,r3			; number of bytes to read
	MOV	r3,r2			; buffer to read data into
	LDR	r1,[r6,#mnt_descriptor%] ; alternate disc record
	LDR	r0,[r6,#mnt_lbtsectorlen%]
	ADD	r8,r6,#mnt_instanceword% ; point at instance word.
	MOV	r1,r1,LSL #6		; descriptor shifted up
	ORR	r1,r1,#1		; reason code 1 (read sectors)
	LDR	r7,[r6,#mnt_startsector%] ; first sector of partition
	LDR	r14, [r12, #debug]
	TST	r14, #1<<31		; EQ -> newfilecore, NE -> oldfilecore
	ADDEQ	r2,r7,r5,LSR r0		; mnt_lbtsectorlen%, from before
	ADDNE	r2,r5,r7,LSL r0		; 
	LDR	r7,[r6,#mnt_drivenumber%] ; get drive number
	ORR	r2,r2,r7,LSL #29	; calculate disc address
	LDR	r5,[r6,#mnt_endsector%]
	BNE	_oldfilecore
	ADD	r7,r2,r4,LSR r0
	ADD	r5,r5,#1		; otherwise last sector is never read
	CMP	r7,r5
	BGT	_sector_out_of_range
	SWI	X+FileCore_SectorOp
	MOVVC	r3,r4
FNdtc(vc, "<<< Image_ReadBytes_Partition")
	FNcreturn("VC")
FNdt("<<! Image_ReadBytes_Partition (OS_GBPB failed)")
	FNgerror

._oldfilecore
	ADD	r7,r4,r2
	CMP	r7,r5,LSL r0
	BGT	_sector_out_of_range
	SWI	"XFileCore_DiscOp"
	MOVVC	r3,r4
FNdtc(vc, "<<< Image_ReadBytes_Partition")
	FNcreturn("VC")
FNdt("<<! Image_ReadBytes_Partition (OS_GBPB failed)")
	FNgerror

._sector_out_of_range
FNdt("<<! Image_ReadBytes_Partition (sector out of range)")
	FNrerror
	EQUD	0
	EQUS	"Sector out of range."+CHR$0:ALIGN


.Image_WriteBytes_File
FNdt(">>> Image_WriteBytes_File")
FNdt("  r2 (pointer to buffer) = %2x8")
FNdt("  r3 (bytes to write) = %3i4")
FNdt("  r4 (file offset) = %4i4")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Write bytes to the given file
;------------------------
; On entry:
;	r2  =	pointer to buffer
;	r3  =	number of bytes to write
;	r4  =	file offset
;	r6  =	pointer to mount descriptor block
; On exit:
;	r3  =	number of bytes not written
;------------------------
	FNfunction("r0-r2,r4")
	MOV	r0,#1
	LDR	r1,[r6,#mnt_filehandle%]
	SWI	"XOS_GBPB"
FNdtc(vc, "<<< Image_WriteBytes_File")
	FNcreturn("VC")
FNdt("<<! Image_WriteBytes_File (OS_GBPB failed)")
	FNgerror


.Image_WriteBytes_Partition
FNdt(">>> Image_WriteBytes_Partition")
FNdt("  r2 (pointer to buffer) = %2x8")
FNdt("  r3 (bytes to write) = %3i4")
FNdt("  r4 (partition offset) = %4i4")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Write bytes to the given partition
;------------------------
; On entry:
;	r2  =	pointer to buffer
;	r3  =	number of bytes to write
;	r4  =	partition offset
;	r6  =	pointer to mount descriptor block
; On exit:
;------------------------
FNdt("<<! Image_WriteBytes_Partition (we don't do this yet)")
	MOV	r0,pc
	ORRS	pc,r14,#_PSR_Vbit%
	EQUD	0
	EQUS	"Partition writing is not yet implemented."+CHR$0:ALIGN


.Image_WriteBytes_ReadOnly
FNdt("!!! Image_WriteBytes_ReadOnly (we can't write anything)")
	MOV	r0,pc
	ORRS	pc,r14,#_PSR_Vbit%
	EQUD	0
	EQUS	"This partition/image is mounted read-only."+CHR$0:ALIGN


.Image_IdentifyDisc
FNdt(">>> Image_IdentifyDisc")
;------------------------
; Check to see if the given disc is in ext2fs format.
;------------------------
; On entry:
;	r2  =	pointer to buffer
;	r3  =	length of buffer
;	r5  =	pointer to disc record
;	r6  =	sector cache handle
;	r8  =	pointer to FileCore instance private word to use
; On exit (if format identified):
;	r1  =	0 (to claim call)
;	r2  =	filetype number for given disc
;	r5  =	pointer to disc record, which has been modified
;	AORP
; On exit (if format not recognised):
;	r6  =	new sector cache handle
;	AORP
;------------------------
;	FNfunction("r0-r5,r7")
	FNfunction("r2-r4,r7")
;------------------------
; an ext2 floppy might have almost any shape.  Let's believe Filecore.
;------------------------
; try to recognise the disc from the special word in the first super block
	MOV	r1,#9+(1<<6)		; read sectors via cache, ignore esc
	ORR	r1,r1,r5,LSL #6
	MOV	r4,#s_magic%+4
	CMP	r3, #LEN(format_name$)+1
;XXXXXXXXXXXXXX
	MOVLE	r7, #0
	MOVGT	r7, r2
; MW thinks this should be:
;	MOVLO	r7, #0
;	MOVHS	r7, r2
; this doesn't do the same thing
;XXXXXXXXXXXXXX
	LDR	r2,[r5,#&c]		; disc address of root directory
	MOV	r2,r2,LSL #28
	ADD	r2,r2,#1024		; disc address
	LDR	r3,[r12,#buffer]
	MOV	r4,#len_superblock%	; get 60 bytes of the super block
	SWI	"XFileCore_DiscOp"
	BVS	_return_error
	LDR	r4,[r12,#buffer]
	LDR	r2,[r4,#s_magic%-superblock%]
	MOV	r2, r2, LSL #16
	EOR	r2, r2, #&ef000000
	TEQ	r2, #&00530000
	MOVNE	r1,#&69
	FNcreturn("NE")
;------------------------
; it is an ext2 disc - return the right info and update the disc shape
	LDR	r1,[r4,#s_blocks_count%-superblock%]
	LDR	r2,[r4,#s_log_block_size%-superblock%]
	ADD	r2, r2, #10
	MOV	r1, r1, LSL r2
	STR	r1,[r5,#16]		; disc_size
;------------------------
; write disc name and cycle ID into FileCore descriptor block
	ADD	r4, r4, #s_volume_name%-superblock%
	LDMIA	r4, {r1-r3}		; get first 12 bytes of name
	MOV	r3, r3, LSL #16		; drop last 2 characters
	ORR	r3, r3, r2, LSR #16	; and put the two from r2 in
	MOV	r2, r2, LSL #16		; drop last 2 characters
	ORR	r2, r2, r1, LSR #16	; and put the two from r2 in
	MOV	r1, r1, LSL #16		; put the first two chars at the top
	LDR	r14, [r4, #s_volume_name%-s_wtime%]
	MOV	r14, r14, LSL #16	; put the lsb at the top..
	ORR	r1, r1, r14, LSR #16	; and then ORR it into the bottom.
	ADD	r4, r5, #20
	STMIA	r4, {r1-r3}		; set up the name & cycle ID
;------------------------
; write format name into supplied buffer
	TEQ	r7, #0
	BEQ	_no_space_for_formatname
	ADR	r2,_format_name
._lp
	LDRB	r3,[r2],#1
	TEQ	r3,#0
	STRB	r3,[r7],#1
	BNE	_lp
._no_space_for_formatname
	FNunstack
	MOV	r2,#filetype%
	MOV	r1,#0
	STR	r2,[r5,#32]		; filetype given to disc
FNdt("<<< Image_IdentifyDisc")
	MOVS	pc,r14

._return_error
FNdt("<<! Image_IdentifyDisc (error)")
	FNgerror

._format_name
	EQUS	format_name$+CHR$0:ALIGN


.Image_Umount
FNdt(">>> Image_Umount")
FNdt("  r1 (my handle for image) = %1x8")
;------------------------
; Unmount the given image.
;------------------------
; On entry:
;	r1  =	my handle for image
;------------------------
	FNfunction("")
	LDMIA	r1,{r0,r2}		; next, last
	TEQ	r0,#0
	STR	r0,[r2,#next%]
	STRNE	r2,[r0,#last%]
	MOV	r2,r1			; free memory
	LDR	r1,[r1,#mnt_groupdescs%]
	BL	Memory_Free
	MOV	r2,r1
	BL	Memory_Free
FNdt("<<< Image_Umount")
	FNreturn


.Image_ReadFreeSpace
FNdt(">>> Image_ReadFreeSpace")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Read the free space and size of the given image.
;------------------------
; On entry:
;	r6  =	pointer to mount descriptor block
; On exit:
;	r0  =	free space
;	r1  =	biggest object creatable
;	r2  =	disc size
;------------------------
	FNfunction("")
;------------------------
; get disc size
	LDR	r0,[r6,#s_log_block_size%]
	LDR	r2,[r6,#s_blocks_count%]
	ADD	r0,r0,#10
	LDR	r1,[r6,#s_free_blocks_count%]
	MOV	r2,r2,LSL r0
;------------------------
; get free space - assume biggest object creatable is same size
	MOV	r1,r1,LSL r0
	MOV	r0,r1
FNdt("<<< Image_ReadFreeSpace")
FNdt("  r0 (free space) = %0i4")
FNdt("  r1 (biggest possible object) = %1i4")
FNdt("  r2 (disc size) = %2i4")
	FNreturn


.Image_ReadUsedSpaceMap
FNdt(">>> Image_ReadUsedSpaceMap")
FNdt("  r2 (buffer for map) = %2x8")
FNdt("  r5 (size of buffer) = %5i4")
FNdt("  r6 (MDB) = %6x8")
;------------------------
; Read the used space bitmap of the given image.
;------------------------
; On entry:
;	r2  =	pointer to buffer for map (filled with 0s)
;	r5  =	size of buffer
;	r6  =	pointer to mount descriptor block
; On exit:
;	r0-r2, r5, r6 preserved
;------------------------
	FNfunction("")

FNdt("<<< Image_ReadUsedSpaceMap (we don't do this yet)")
	FNreturn
