;
; RISC OS / ARM assembler glue code for DOOM.
; (C) 1998 Andreas Dehmel.
;

r0	rn	0
r1	rn	1
r2	rn	2
r3	rn	3
r4	rn	4
r5	rn	5
r6	rn	6
r7	rn	7
r8	rn	8
r9	rn	9
r10	rn	10
r11	rn	11
r12	rn	12



	;
	; Chapter 1: RISC OS specific stuff.
	;

; Error codes for InitFrameBuffer
fb_err_mode	equ	1
fb_err_mem	equ	2

; Frame buffer desc struct
fbd_width	equ	0x00
fbd_height	equ	0x04
fbd_depth	equ	0x08
fbd_pitch	equ	0x0c
fbd_colours	equ	0x10
fbd_buffers	equ	0x14
fbd_eigx	equ	0x18
fbd_eigy	equ	0x1c
fbd_show	equ	0x20
fbd_plot	equ	0x24
fbd_screens	equ	0x28

	idfn	(C) 1998 by Andreas Dehmel


	AREA	CODE, READONLY
	align	4
	export	|ReadMonotonicTime|
	=	"ReadMonotonicTime"
	align	4

|ReadMonotonicTime|:
	swi	0x20042		; XOS_ReadMonotonicTime
	movs	pc,lr



	AREA	CODE, READONLY
	align	4
	export	|LowerCaseTable|
	=	"LowerCaseTable"
	align	4

|LowerCaseTable|:
	mvn	r0,#0
	swi	0x63057		; XTerritory_LowerCaseTable
	movs	pc,lr




	AREA	CODE, READONLY
	align	4
	export	|ReadFileInfo|
	=	"ReadFileInfo"
	align	4

|ReadFileInfo|:
	stmdb	sp!, {r4-r6}
	mov	r6, r1
	mov	r1, r0
	mov	r0, #0x11
	swi	0x20008		; XOS_File
	stmia	r6, {r2-r5}
	ldmia	sp!, {r4-r6}
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|SetFiletype|
	=	"SetFiletype"
	align	4

|SetFiletype|:
	mov	r2, r1
	mov	r1, r0
	mov	r0, #0x12
	swi	0x20008
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ChangeEscapeEffect|
	=	"ChangeEscapeEffect"
	align	4

|ChangeEscapeEffect|:		; r0 eor, r1 and
	mov	r2, r1
	mov	r1, r0
	mov	r0, #0xc8
	swi	0x20006		; XOS_Byte 200
	mov	r0, r1
	movs	pc, lr






	AREA	CODE, READONLY
	align	4
	export	|FillMemoryWord|
	=	"FillMemoryWord"
	align	4

|FillMemoryWord|:		; r0 *scr, r1 value (32bit), r2 size
	stmdb	sp!, {r4, lr}
	bic	r0, r0, #3
	bics	r2, r2, #3	; words only
	beq	|FMWexit|
	mov	r3, r1
	mov	r4, r1
	mov	lr, r1
	subs	r2, r2, #0x10
	blt	|FMWsmall|
|FMWloop|:
	stmia	r0!, {r1, r3, r4, lr}
	subs	r2, r2, #0x10
	bge	|FMWloop|
|FMWsmall|:
	adds	r2, r2, #0x10
	ble	|FMWexit|
	tst	r2, #0x08
	stmneia	r0!, {r1, r3}
	tst	r2, #0x04
	strne	r1, [r0], #4
|FMWexit|:
	ldmia	sp!, {r4, pc}^




	AREA	CODE, READONLY
	align	4
	export	|FillMemoryShort|
	=	"FillMemoryShort"
	align	4

|FillMemoryShort|:
	stmdb	sp!, {r4, lr}
	bic	r0, r0, #1
	bic	r2, r2, #1	; shorts only
	tst	r2, #2
	beq	|FMSnoalign|
	strb	r1, [r0], #1
	mov	lr, r1, lsr #8
	strb	lr, [r0], #1
	subs	r2, r2, #2
	ble	|FMSexit|
|FMSnoalign|:
	orr	r1, r1, r1, lsl #16
	mov	r3, r1
	mov	r4, r1
	mov	lr, r1
	subs	r2, r2, #0x10
	blt	|FMSsmall|
|FMSloop|:
	stmia	r0!, {r1, r3, r4, lr}
	subs	r2, r2, #0x10
	bge	|FMSloop|
|FMSsmall|:
	adds	r2, r2, #0x10
	ble	|FMSexit|
	tst	r2, #0x08
	stmneia	r0!, {r1, r3}
	tst	r2, #0x04
	strne	r1, [r0], #4
	tst	r2, #0x02
	strneb	r1, [r0], #1
	movne	lr, r1, lsr #8
	strneb	lr, [r0], #1
|FMSexit|:
	ldmia	sp!, {r4, pc}^






screen_desc_base	equ	0
screen_desc_size	equ	4
screen_desc_resx	equ	8
screen_desc_resy	equ	12

	AREA	CODE, READONLY
	align	4
	export	|ReadScreenInfo|
	=	"ReadScreenInfo"
	align	4

|ReadScreenInfo|:		; r0 *screen_desc
	mov	r3, r0
	mov	r0, #2
	swi	0x2005c
	stmia	r3, {r0, r1}
	mvn	r0, #0
	mov	r1, #0x0b
	swi	0x20035		; XOS_ReadModeVariable
	add	r2, r2, #1
	str	r2, [r3, #screen_desc_resx]
	mov	r1, #0x0c
	swi	0x20035
	add	r2, r2, #1
	str	r2, [r3, #screen_desc_resy]
	movs	pc, lr





	AREA	CODE, READONLY
	align	4
	export	|ReadFrameBufferInfo|
	=	"ReadFrameBufferInfo"
	align	4

|ReadFrameBufferInfo|:		; r0 *frame_buffer_desc
	stmdb	sp!, {r4, r5, lr}
	mov	r4, r0
	mvn	r0, #0
	mov	r1, #9
	swi	0x20035
	mov	r1, #1
	mov	r2, r1, lsl r2
	ldr	r5, [r4, #fbd_depth]
	cmp	r5, r2
	movne	r0, #fb_err_mode
	bne	|RFBIfailed|
	mov	r1, #11
	swi	0x20035		; XOS_ReadModeVarible
	add	r3, r2, #1
	ldr	r1, [r4, #fbd_width]
	cmp	r1, r3
	movne	r0, #fb_err_mode	; need exact with (used in Doom to get line addresses)
	bne	|RFBIfailed|
	mov	r1, #12
	swi	0x20035
	add	r2, r2, #1
	ldr	r1, [r4, #fbd_height]
	cmp	r1, r2
	movgt	r0, #fb_err_mode	; more lines than necessary are OK!
	bgt	|RFBIfailed|
	strne	r2, [r4, #fbd_height]	; if not equal then update the info
	ldr	r3, [r4, #fbd_pitch]
	mul	r3, r2, r3	; size of one screen in bytes
	mov	r1, #4
	swi	0x20035
	str	r2, [r4, #fbd_eigx]
	mov	r1, #5
	swi	0x20035
	str	r2, [r4, #fbd_eigy]
	mov	r1, #3
	swi	0x20035
	str	r2, [r4, #fbd_colours]
	mov	r0, #2
	swi	0x2005c
	add	r2, r4, #fbd_screens
	ldr	r5, [r4, #fbd_buffers]
	sub	r1, r1, r3
|RFBIfrmloop|:
	str	r0, [r2], #4
	add	r0, r0, r3
	subs	r1, r1, r3
	blt	|RFBIfrmend|
	subs	r5, r5, #1
	bgt	|RFBIfrmloop|
|RFBIfrmend|:
	add	r0, r4, #fbd_screens
	sub	r0, r2, r0
	mov	r0, r0, lsr #2
	str	r0, [r4, #fbd_buffers]
	mov	r0, #0
|RFBIfailed|:
	ldmia	sp!, {r4, r5, pc}^




	AREA	CODE, READONLY
	align	4
	export	|InstallFrameBuffer|
	=	"InstallFrameBuffer"
	align	4

|InstallFrameBuffer|:		; read info about current mode. If width, height, depth
	stmdb	sp!,{r4-r6,lr}	; OK then try claiming at least 2 screen buffers and
	mov	r4, r0		; install frame buffer handler. r0 = *frame_buffer_desc
	ldr	r5, [r4, #fbd_buffers]	; number of buffers to try
	bl	|ReadFrameBufferInfo|
	cmp	r0, #0
	bne	|IFBfailed|
	ldr	r2, [r4, #fbd_pitch]
	ldr	r3, [r4, #fbd_height]
	mul	r3, r2, r3		; size of 1 frame buffer
|IFBtrybuffers|:
	mov	r0, #2
	swi	0x2005c		; XOS_ReadDynamicArea
	mul	r6, r3, r5
	subs	r1, r6, r1
	ble	|IFBgotbuffers|
	mov	r2, r1		; memorize distance
	mov	r0, #2
	swi	0x2002a		; XOS_ChangeDynamicArea
	cmp	r1, r2
	bge	|IFBgotbuffers|
	sub	r5, r5, #1
	cmp	r5, #2		; we need at least 2 buffers!
	bge	|IFBtrybuffers|
	mov	r0, #fb_err_mem
|IFBfailed|:
	ldmia	sp!,{r4-r6,pc}^
|IFBgotbuffers|:
	str	r5, [r4, #fbd_buffers]
	mov	r0, #2
	swi	0x2005c
	add	r2, r4, #fbd_screens
	mov	r1, #0
|IFBinitbases|:
	str	r0, [r2], #4
	add	r0, r0, r3
	add	r1, r1, #1
	cmp	r1, r5
	blt	|IFBinitbases|
	mov	r0, #0
	str	r0, [r4, #fbd_show]	; show frame number 0
	mov	r0, #1
	str	r0, [r4, #fbd_plot]	; mark frame number 0 as plotted, rest as free
	mov	r0, #0x70
	mov	r1, #1
	swi	0x20006			; write VDU screenbank
	mov	r0, #0x71
	mov	r1, #1
	swi	0x20006			; write hardware screenbank
	mov	r0, #0x10
	adr	r1, |VsyncHandler|
	mov	r2, r4
	swi	0x2001f		; XOS_Claim
	mov	r0, #0x0e
	mov	r1, #4
	swi	0x20006		; XOS_Byte (enable event)
	mov	r0, #4
	swi	0x20022		; XOS_GenerateEvent
	swi	0x20036		; XOS_RemoveCursors
	mov	r0, #0
	ldmia	sp!,{r4-r6,pc}^



	AREA	CODE, READONLY
	align	4
	export	|RemoveFrameBuffer|
	=	"RemoveFrameBuffer"
	align	4

|RemoveFrameBuffer|:		; r0 = *frame_buffer_desc
	stmdb	sp!,{r4,lr}
	mov	r4, r0
	mov	r0, #0x0d
	mov	r1, #4
	swi	0x20006		; XOS_Byte (disable event)
	mov	r0, #0x10
	adr	r1, |VsyncHandler|
	mov	r2, r4
	swi	0x20020		; XOS_Release
	mov	r0, #0x70
	ldr	r1, [r4, #fbd_show]
	add	r1, r1, #1
	swi	0x20006		; write VDU screenbank
	swi	0x20037		; XOS_RestoreCursors
	ldmia	sp!,{r4,pc}^



	AREA	CODE, READONLY
	align	4
	export	|GetNextFrame|
	=	"GetNextFrame"
	align	4

|GetNextFrame|:			; returns number of next frame of -1 if none.
	stmdb	sp!,{r4,lr}	; do this here -- C-compiler might optimise stuff out
	ldr	r1, [r0, #fbd_buffers]	; r0 = *frame_buffer_desc
	ldr	r2, [r0, #fbd_show]
	ldr	r4, [r0, #fbd_plot]
	mov	r3, r2
|GNFloop|:
	add	r3, r3, #1
	cmp	r3, r1
	movge	r3, #0
	cmp	r3, r2
	beq	|GNFfailed|
	mov	lr, #1
	tst	r4, lr, lsl r3
	bne	|GNFloop|
	mov	r0, r3
	ldmia	sp!,{r4,pc}^
|GNFfailed|:
	mvn	r0, #0
	ldmia	sp!,{r4,pc}^




	AREA	CODE, READONLY
	align	4
	export	|MarkFrameNumber|
	=	"MarkFrameNumber"
	align	4

|MarkFrameNumber|:		; r0 = *frame_buffer_desc, r1 = number
	ldr	r2, [r0, #fbd_plot]
	mov	r3, #1
	orr	r2, r2, r3, lsl r1
	str	r2, [r0, #fbd_plot]
	movs	pc, lr





|VsyncHandler|:
	cmp	r0, #4			; R12 = *frame_buffer_desc
	movnes	pc, lr
	stmdb	sp!,{r0-r4,lr}
	ldr	r1, [r12, #fbd_buffers]
	ldr	r0, [r12, #fbd_show]	; number of buffer currently shown
	add	r2, r0, #1
	cmp	r2, r1
	movge	r2, #0			; number of buffer to show next
	mov	r3, #1
	mov	r3, r3, lsl r2
	ldr	r1, [r12, #fbd_plot]	; bitfield showing plot allocation
	tst	r1, r3			; is next frame marked plotted?
	beq	|VSHexit|
	str	r2, [r12, #fbd_show]
	mov	r3, #1
	bic	r1, r1, r3, lsl r0
	str	r1, [r12, #fbd_plot]	; free buffer that was displayed
	mov	r3, pc			; now enter SVC mode
	orr	r4, r3, #3
	teqp	r4, #0
	mov	r0, r0
	str	lr, [sp, #-4]!
;	add	r1, r2, #1		; buffers are numbered from 1 here
;	mov	r0, #0x71
;	swi	0x20006			; OS_Byte 113 (set display screen bank)
	ldr	r0, [r12, #fbd_pitch]
	ldr	r1, [r12, #fbd_height]
	mul	r0, r1, r0
	mul	r1, r2, r0
	;mov	r0, #0x02000000		; for hardware
	mov	r0, #0x03000000		; hardware + VDU
	stmdb	sp!, {r0, r1}
	mov	r0, #0x16
	add	r1, sp, #3
	swi	0x20007			; OS_Word 22 (set screen base address)
	add	sp, sp, #8
	mov	r0,#0x70
	ldr	r1, [r12, #fbd_show]
	add	r1, r1, #1
	swi	0x20006
	ldr	lr, [sp], #4
	teqp	r3, #0
	mov	r0, r0
|VSHexit|:
	ldmia	sp!,{r0-r4,pc}^



	AREA	CODE, READONLY
	align	4
	export	|PrepareWithoutFrames|
	=	"PrepareWithoutFrames"
	align	4

|PrepareWithoutFrames|:			; r0 *frame_buffer
	mov	r3, r0
	mov	r0, #0x03000000
	mov	r1, #0			; use first screen
	stmdb	sp!, {r0, r1}
	mov	r0, #22
	add	r1, sp, #3
	swi	0x20007
	add	sp, sp, #8
	swi	0x20036
	mov	r0, #0
	movs	pc, lr



	AREA	CODE, READONLY
	align	4
	export	|DisplayFrameBuffer|
	=	"DisplayFrameBuffer"
	align	4

|DisplayFrameBuffer|:			; r0 *frame_buffer, r1 number
	mov	r3, r0
	ldr	r0, [r3, #fbd_pitch]
	ldr	r2, [r3, #fbd_height]
	mul	r0, r2, r0
	mul	r1, r0, r1
	mov	r0, #0x03000000
	stmdb	sp!, {r0, r1}
	mov	r0, #22
	add	r1, sp, #3
	swi	0x20007
	add	sp, sp, #8
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ScanKeyboard|
	=	"ScanKeyboard"
	align	4

|ScanKeyboard|:				; R0 = low tide mark of internal key number
	mov	r1, r0
	mov	r0, #0x79
	swi	0x20006
	mov	r0, r1
	movs	pc,lr




	AREA	CODE, READONLY
	align	4
	export	|FlushABuffer|
	=	"FlushABuffer"
	align	4

|FlushABuffer|:
	mov	r1, r0
	mov	r0, #0x15
	swi	0x20006
	movs	pc,lr




	AREA	CODE, READONLY
	align	4
	export	|InsertInBuffer|
	=	"InsertInBuffer"
	align	4

|InsertInBuffer|:
	mov	r2, r1
	mov	r1, r0
	mov	r0, #0x8a
	swi	0x20006
	movcc	r0, #0
	movcs	r0, #1
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|WaitForVSync|
	=	"WaitForVSync"
	align	4

|WaitForVSync|:
	mov	r0, #0x13
	swi	0x20006
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|HomeCursor|
	=	"HomeCursor"
	align	4

|HomeCursor|:
	swi	0x11e			; VDU 30
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ReadMouseState|
	=	"ReadMouseState"
	align	4

|ReadMouseState|:
	str	r4, [sp, #-4]!
	mov	r4, r0
	swi	0x2001c			; XOS_Mouse
	stmia	r4, {r0-r3}
	ldr	r4, [sp], #4
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|PutMouseAt|
	=	"PutMouseAt"
	align	4

|PutMouseAt|:
	mov	r2, #3
	orr	r2, r2, r0, lsl #8
	bic	r2, r2, #0xff000000
	orr	r2, r2, r1, lsl #0x18
	mov	r3, r1, lsr #8
	stmdb	sp!, {r2, r3}
	mov	r0, #0x15
	mov	r1, sp
	swi	0x20007
	add	sp, sp, #8
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ReadProcessorRegisters|
	=	"ReadProcessorRegisters"
	align	4

|ReadProcessorRegisters|:		; write current registers into kernel_unwind block
	stmia	r0, {r4-r9}
	str	fp, [r0, #24]
	str	sp, [r0, #28]
	str	lr, [r0, #32]		; use lr instead of pc! we want pc of caller.
	str	sl, [r0, #36]
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ReadExceptionRegisters|
	=	"ReadExceptionRegisters"
	align	4

|ReadExceptionRegisters|:		; read registers as stored by the
					; C runtime exception handler
	STMFD	sp!,{r14}
	MOV	r0,#7
	MOV	r1,#0
	MOV	r2,#0
	MOV	r3,#0
	SWI	&20040 ; XOS_ChangeEnvironment;
	MOVVS	r0,#0
	MOVVC	r0,r3
	LDMFD	sp!,{pc}




	AREA	CODE, READONLY
	align	4
	export	|WimpReadSlotSize|
	=	"WimpReadSlotSize"
	align	4

|WimpReadSlotSize|:
	str	r4, [sp, #-4]!
	mvn	r0, #0
	mvn	r1, #0
	swi	0x400ec
	ldr	r4, [sp], #4
	movs	pc, lr







	; Safe abort handler, for Shared C Library only.
	; The abort handler checks the validity of sl before calling the
	; actual C handler and reverts to the root chunk if it's been
	; corrupted. It also _MUST_ restore the last valid frame pointer
	; (the only real problem) or the result will be just as fatal.
	; Recovering r10/r11 will normally only be necessary if the abort
	; occurred in a plotter and the recovery procedure should work OK
	; in that case. There is no guarantee, however...

; kernel stack chunk (PRM 4-271)
ksc_mark	equ	0x00
ksc_next	equ	0x04
ksc_prev	equ	0x08
ksc_size	equ	0x0c
ksc_dealloc	equ	0x10
ksc_length	equ	0x14

; structure for storing variables used by the handler
sah_root_chunk		equ	0x00	;root stack chunk of C environment
sah_corrupt_ptr		equ	0x04	;ptr to int, in case of recovery set to 1
sah_save_offset		equ	0x08	;offset of data save instr. from data save mask ptr
sah_prev_prefetch	equ	0x0c	;previous prefetch abort handler
sah_prev_data_abort	equ	0x18	;previous data abort handler
sah_prev_adr_except	equ	0x24	;previous address exception handler
sah_user_stack		equ	0x30	;user stack when abort occurred
sah_size		equ	0x34


	; in SCL: offset of R10 from start of stack chunk
lwm_offset	equ	0x230

	AREA	CODE, READONLY
	align	4
	export	|InitSafeAbortHandler|
	=	"InitSafeAbortHandler"
	align	4

|SAHvariables|:
	dcd	|SafeAbortVariables|

|InitSafeAbortHandler|:		;r0 = pointer to recovery status word
	stmdb	sp!, {r12, lr}
	ldr	r12, [pc, #|SAHvariables| - . - 8]
	str	r0, [r12, #sah_corrupt_ptr]
	mov	r1, #0
	str	r1, [r0]
	sub	r0, r10, #lwm_offset
|ISAHloop|:
	ldr	r1, [r0, #ksc_prev]
	cmp	r1, #0
	beq	|ISAHdone|
	mov	r0, r1
	b	|ISAHloop|
|ISAHdone|:
	str	r0, [r12, #sah_root_chunk]

	; find out what processor we're running on. For a StrongARM processor the
	; data save instruction offset is 8, otherwise it's 12!
	mvn	r3, #11
	cmp	r11, #0
	beq	|ISAHdsifound|	; no fp available, just use 12 and hope like hell
	ldr	r0, [r11]	; save mask pointer
	bic	r0, r0, #0xfc000003
	ldr	r1, [pc, #|AEHfakesave| - . - 8]
	mov	r1, r1, lsr #10
|ISAHdsifind|:
	ldr	r2, [r0, r3]
	cmp	r1, r2, lsr #10
	beq	|ISAHdsifound|
	adds	r3, r3, #4
	blt	|ISAHdsifind|
	mvn	r3, #11
|ISAHdsifound|:
	rsb	r3, r3, #0
	str	r3, [r12, #sah_save_offset]

	; contrary to what it says in PRM1-114 there is a SWI to claim a
	; hardware vector: OS_ChangeEnvironment
	mov	r0, #2
	adr	r1, |PrefetchAbortHandler|
	mov	r2, #0
	mov	r3, #0
	swi	0x20040
	add	r0, r12, #sah_prev_prefetch
	stmia	r0, {r1-r3}
	mov	r0, #3
	adr	r1, |DataAbortHandler|
	mov	r2, #0
	mov	r3, #0
	swi	0x20040
	add	r0, r12, #sah_prev_data_abort
	stmia	r0, {r1-r3}
	mov	r0, #4
	adr	r1, |AddressExceptionHandler|
	mov	r2, #0
	mov	r3, #0
	swi	0x20040
	add	r0, r12, #sah_prev_adr_except
	stmia	r0, {r1-r3}
	ldmia	sp!, {r12, pc}^


|AEHstackmark|
	dcd	0xf60690ff	;stack chunk magic (PRM4-271)

|PrefetchAbortHandler|:		;reserve topmost word on stack for passproc
	sub	sp, sp, #4
	stmdb	sp!, {r0-r5,lr}
	ldr	r5, [pc, #|SAHvariables| - . - 8]
	ldr	r0, [r5, #sah_prev_prefetch]
	b	|GeneralExceptionHandler|

|DataAbortHandler|:
	sub	sp, sp, #4
	stmdb	sp!, {r0-r5,lr}
	ldr	r5, [pc, #|SAHvariables| - . - 8]
	ldr	r0, [r5, #sah_prev_data_abort]
	b	|GeneralExceptionHandler|

|AddressExceptionHandler|:
	sub	sp, sp, #4
	stmdb	sp!, {r0-r5,lr}
	ldr	r5, [pc, #|SAHvariables| - . - 8]
	ldr	r0, [r5, #sah_prev_adr_except]
	; fall through

|GeneralExceptionHandler|:		; for all kinds of address violations.
	str	r0, [sp, #0x1c]		; 7 registers already on stack
	mvn	r0, #0
	mvn	r1, #0
	swi	0x600ec
	add	r0, r0, #0x8000
	tst	r10, #3
	bne	|AEHcorrupted|
	cmp	r10, #0x8000
	cmpcs	r0, r10
	bcc	|AEHcorrupted|
	ldr	r1, [r10, #(ksc_mark - lwm_offset)]
	ldr	r2, [pc, #|AEHstackmark| - . - 8]
	cmp	r1, r2
	bne	|AEHcorrupted|
	ldr	r1, [r10, #(ksc_size - lwm_offset)]
	add	r1, r10, r1
	sub	r1, r1, #lwm_offset
	add	r2, r5, #sah_user_stack
	stmia	r2, {sp}^		;save user stack
	ldr	r3, [r2]
	cmp	r3, r10
	cmpcs	r1, r3
	bcc	|AEHcorrupted|
	cmp	r11, r10
	cmpcs	r1, r11
	bcc	|AEHcorrupted|
	ldr	r1, [r11]		;test save mask pointer
	bic	r1, r1, #0xfc000003
	ldr	lr, [r5, #sah_save_offset]
	sub	r1, r1, lr
	cmp	r1, #0x8000
	cmpcs	r0, r1
	bcc	|AEHcorrupted|
	ldr	r2, [pc, #|AEHfakesave| - . - 8]
	mov	r2, r2, lsr #10
	ldr	r1, [r1]
	cmp	r2, r1, lsr #10
	bne	|AEHcorrupted|
|AEHpasson|:
	ldmia	sp!, {r0-r5,lr}		;load normal regs
	ldmia	sp!, {pc}		;pass proc was stored in top position

|AEHcorrupted|:				;r10/r11 have been corrupted. Try to recover...
	ldr	r10, [r5, #sah_root_chunk]
	ldr	r1, [r10, #ksc_size]
	add	r1, r10, r1
	ldr	r2, [r5, #sah_corrupt_ptr]
	mov	r3, #1
	str	r3, [r2]		;make a note that we had to fix things up
	add	r2, r5, #sah_user_stack	;try to find the next valid stack frame
	stmia	r2, {sp}^		;if we fail... hasta la vista, baby!
	ldr	r2, [r2]
|AEHfindchunk|:				;r13_usr must be within stack chunk...
	cmp	r2, r10
	cmpcs	r1, r2
	bcs	|AEHrightchunk|
	ldr	r3, [r10, #ksc_prev]	;traverse stack back...
	cmp	r3, #0
	beq	|AEHrightchunk|		;couldn't find the right one...
	mov	r10, r3
	ldr	r1, [r10, #ksc_size]
	add	r1, r10, r1
	b	|AEHfindchunk|
|AEHrightchunk|:
	cmp	r11, r10		;frame pointer must be within stack chunk
	cmpcs	r1, r11
	bcs	|AEHnorecover|		;looks OK...
	ldr	r4, [pc, #|AEHfakesave| - . - 8]
	mov	r4, r4, lsr #10		;bottom 10 bits don't matter
|AEHrecoverloop|:			;if this happened in an assembler plotter, the frame
	cmp	r2, r10			;pointer is stored somewhere on the stack. Search...
	cmpcs	r1, r2
	bcc	|AEHnorecover|		;we haven't found one in this stack chunk. Goodnight.
	ldr	r3, [r2], #4		;save mask pointer
	bic	r3, r3, #0xfc000003	;clear bits 0,1,26-31 (PRM4-401)
	ldr	lr, [r5, #sah_save_offset]
	sub	r3, r3, lr
	cmp	r3, #0x8000
	cmpcs	r0, r3
	bcc	|AEHrecoverloop|
	ldr	r3, [r3]
	cmp	r4, r3, lsr #10
	bne	|AEHrecoverloop|
	ldr	r3, [r2, #-12]		;return sp value, must be within wimpslot
	tst	r3, #3
	bne	|AEHrecoverloop|
	cmp	r3, #0x8000
	cmpcs	r0, r3
	bcc	|AEHrecoverloop|
	sub	r11, r2, #4		;we found one that looks promising. Fingers crossed...
|AEHnorecover|:
	add	r10, r10, #lwm_offset
	b	|AEHpasson|
|AEHfakesave|
	stmdb	sp!, {r11,r12,lr,pc}	;data save instruction.




	AREA	CODE, READONLY
	align	4
	export	|RemoveSafeAbortHandler|
	=	"RemoveSafeAbortHandler"
	align	4

|RemoveSafeAbortHandler|:		;remove the customized exception handlers
	stmdb	sp!, {r12,lr}
	ldr	r12, [pc, #|SAHvariables| - . - 8]
	add	r0, r12, #sah_prev_prefetch
	ldmia	r0, {r1-r3}
	cmp	r1, #0
	moveqs	pc, lr
	mov	r0, #2
	swi	0x20040
	mov	r1, #0
	str	r1, [r12, #sah_prev_prefetch]
	add	r0, r12, #sah_prev_data_abort
	ldmia	r0, {r1-r3}
	mov	r0, #3
	swi	0x20040
	mov	r1, #0
	str	r1, [r12, #sah_prev_data_abort]
	add	r0, r12, #sah_prev_adr_except
	ldmia	r0, {r1-r3}
	mov	r0, #4
	swi	0x20040
	mov	r1, #0
	str	r1, [r12, #sah_prev_adr_except]
	ldmia	sp!, {r12,pc}^

	; Safe abort handler end.





	; Memory allocation from dynamic area

dyndesc_number	equ	0x00
dyndesc_arsize	equ	0x04
dyndesc_memory	equ	0x08
dyndesc_flags	equ	0x0c
dyndesc_name	equ	0x10
dyndesc_size	equ	0x30

	AREA	CODE, READONLY
	align	4
	export	|ClaimDynamicArea|
	=	"ClaimDynamicArea"
	align	4

|ClaimDynamicArea|:			; r0 *dyndesc, r1 max size, r2 leave memory free
	stmdb	sp!, {r4-r9,lr}
	mov	r9, r0
	mov	r8, r1
	mov	r7, r2
	mvn	r0, #0
	mvn	r1, #0
	swi	0x600ec			; Wimp_SlotSize
	sub	r2, r2, r7
	cmp	r2, r8
	movgt	r2, r8
	mov	r0, #0
	str	r0, [r9, #dyndesc_memory]
	mvn	r1, #0
	mvn	r3, #0
	ldr	r4, [r9, #dyndesc_flags]
	mvn	r5, #0
	mov	r6, #0
	mvn	r7, #0
	add	r8, r9, #dyndesc_name
	swi	0x20066			; OS_DynamicArea
	mvnvs	r0, #0
	bvs	|CDAexit|
	str	r1, [r9, #dyndesc_number]
	str	r3, [r9, #dyndesc_memory]
	str	r2, [r9, #dyndesc_arsize]
	mov	r0, #0			; returns 0 for OK
|CDAexit|:
	ldmia	sp!, {r4-r9,pc}^



	AREA	CODE, READONLY
	align	4
	export	|ReleaseDynamicArea|
	=	"ReleaseDynamicArea"
	align	4

|ReleaseDynamicArea|:			; r0 *dyndesc
	mov	r3, r0
	ldr	r0, [r3, #dyndesc_memory]
	cmp	r0, #0
	mvneq	r0, #0
	beq	|RDAexit|
	mov	r0, #1
	ldr	r1, [r3, #dyndesc_number]
	swi	0x20066
	mvnvs	r0, #0
	bvs	|RDAexit|
	mov	r0, #0
	str	r0, [r3, #dyndesc_memory]
|RDAexit|:
	movs	pc, lr










	AREA	CODE, READONLY
	align	4
	export	|SetStereoPosition|
	=	"SetStereoPosition"
	align	4

|SetStereoPosition|:
	swi	0x60142			; XSound_Stereo
	mov	r0, r1
	movs	pc, lr




	AREA	CODE, READONLY
	align	4
	export	|ConfigureSound|
	=	"ConfigureSound"
	align	4

|ConfigureSound|:
	stmdb	sp!, {r4, r5, lr}
	mov	r5, r0
	ldmia	r5, {r0-r4}
	swi	0x60140			; XSound_Configure
	stmia	r5, {r0-r4}
	ldmia	sp!, {r4, r5, pc}^





DigiRendChunk	equ	0x6F700		; X flag set


	AREA	CODE, READONLY
	align	4
	export	|DigitalRenderer_Activate|
	=	"DigitalRenderer_Activate"
	align	4

|DigitalRenderer_Activate|:
	swi	DigiRendChunk
	movvc	r0,#0
	movs	pc,lr


	AREA	CODE, READONLY
	align	4
	export	|DigitalRenderer_Deactivate|
	=	"DigitalRenderer_Deactivate"
	align	4

|DigitalRenderer_Deactivate|:
	swi	DigiRendChunk+1
	movvc	r0,#0
	movs	pc,lr


	AREA	CODE, READONLY
	align	4
	export	|DigitalRenderer_ReadState|
	=	"DigitalRenderer_ReadState"
	align	4

|DigitalRenderer_ReadState|:
	swi	DigiRendChunk+5
	mvnvs	r0,#0			; returns -1 on error
	movs	pc,lr






; The channel_desc structure. Don't rearrange the order!!!
channel_desc_samples	equ	0x00
channel_desc_remain	equ	0x04
channel_desc_leftvol	equ	0x08
channel_desc_rightvol	equ	0x0c
channel_desc_step	equ	0x10
channel_desc_end	equ	0x14
channel_desc_size	equ	0x18


	AREA	CODE, READONLY
	align	4
	import	sound_ready
	import	channels
	import	LinToLogTable
	export	|CallBackFillCode|
	=	"CallBackFillCode"
	align	4

NUM_CHANNELS	equ	8

cbstack_lin2log	equ	0
cbstack_size	equ	4

|CBFCready|:
	dcd	sound_ready		; int
|CBFCchannels|:
	dcd	channels		; unsigned char *[NC]	(NC = number of channels = 8)
|CBFCl2l|:
	dcd	LinToLogTable		; unsigned char *

			; FILL CODE MUST BE RE_ENTRANT! I.E. lr IS RIGHT OUT
|CallBackFillCode|:			; r0 *buffer, r1 *lintolog, r2 buffer size
	ldr	r3, [pc, #|CBFCready| - . - 8]
	ldr	r3, [r3]
	cmp	r3, #0
	beq	|CBFCempty|
	ldr	r3, [pc, #|CBFCchannels| - . - 8]	; channels array
	ldr	r1, [pc, #|CBFCl2l| - . - 8]	; lin2log table
|CBFCmainloop|:
	mov	r11, #0			; dl
	mov	r12, #0			; dr
|CBFCchannel0|:				; unrolled 8 times ==> chan no longer needed
	ldr	r9, [r3], #channel_desc_size	; samples
	cmp	r9, #0
	beq	|CBFCchannel1|
	ldmdb	r3, {r4-r8}		; r4 remainder, r5 left, r6 right, r7 step, r8 end
	ldrb	r10, [r9]		; sample
	ldr	r5, [r5, r10, lsl #2]	; leftvol
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]	; rightvol
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16	; use top 16 bits (carry!) and move remainder to r10
	adc	r9, r9, r7, lsr #16	; carry set means remainder overflowed
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}		; store channels, stepremainder
|CBFCchannel1|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel2|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel2|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel3|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel3|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel4|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel4|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel5|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel5|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel6|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel6|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel7|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel7|:
	ldr	r9, [r3], #channel_desc_size
	cmp	r9, #0
	beq	|CBFCchannel8|
	ldmdb	r3, {r4-r8}
	ldrb	r10, [r9]
	ldr	r5, [r5, r10, lsl #2]
	add	r11, r11, r5
	ldr	r6, [r6, r10, lsl #2]
	add	r12, r12, r6
	adds	r10, r4, r7, lsl #16
	adc	r9, r9, r7, lsr #16
	cmp	r9, r8
	movcs	r9, #0
	sub	r5, r3, #(channel_desc_size - channel_desc_samples)
	stmia	r5, {r9, r10}
|CBFCchannel8|:
	sub	r3, r3, #(8*channel_desc_size)
	mvn	r10, #0x80000000
	mov	r9, #0x80000000
	cmp	r11, r10, lsr #0x10
	movgt	r11, r10, lsr #0x10
	cmp	r11, r9, asr #0x10
	movlt	r11, r9, asr #0x10
	mov	r11, r11, lsl #0x10
	ldrb	r11, [r1, r11, lsr #0x13]	; bits in this here instruction.
	strb	r11, [r0], #1		; store right now (good for machines with write bufs)
	cmp	r12, r10, lsr #0x10
	movgt	r12, r10, lsr #0x10
	cmp	r12, r9, asr #0x10
	movlt	r12, r9, asr #0x10
	mov	r12, r12, lsl #0x10
	ldrb	r12, [r1, r12, lsr #0x13]
	strb	r12, [r0], #1
	subs	r2, r2, #2
	bgt	|CBFCmainloop|
|CBFCexit|:
	ldmia	sp!, {pc}
|CBFCempty|:				; sound system not ready yet: silent
	mov	r3, #0
	mov	r4, #0
	mov	r5, #0
	mov	r6, #0
	subs	r2, r2, #16
	blt	|CBFCempsmall|
|CBFCempbig|:
	stmia	r0!, {r3-r6}
	subs	r2, r2, #16
	bge	|CBFCempbig|
|CBFCempsmall|:
	adds	r2, r2, #16
	ble	|CBFCexit|
	tst	r2, #8
	stmneia	r0!, {r3,r4}
	tst	r2, #4
	strne	r3, [r0], #4
	ldmia	sp!, {pc}








	; WAD decompression code

; local vars
local_inbuff	equ	0x00
local_insize	equ	0x04
local_upper	equ	0x08
local_slwm	equ	0x0c
local_segsize	equ	0x10
; on stack anyway
local_file	equ	local_segsize + 0x28
local_csize	equ	local_segsize + 0x2c


	AREA	CODE, READONLY
	align	4
	import	|fread|
	export	|UncompressLump00|
	=	"UncompressLump00"
	align	4

|UncompressLump00|:		; r0 = inBuff, r1 = inSize, r2 outBuff, r3 outSize
	stmdb	sp!, {r4-r12, lr}	; stacked: FILE *, compSize
	sub	sp, sp, #local_segsize
	str	r0, [sp, #local_inbuff]
	str	r1, [sp, #local_insize]
	str	r10, [sp, #local_slwm]	; save stack low water mark for refill code!
	mov	r12, r2
	add	r11, r12, r3
	str	r11, [sp, #local_upper]
	mov	r3, #0		; no valid bits in r2
	bl	|Uncomp00refill|
	and	r10, r2, #0x1f	; r10 literal bit length
	mov	r2, r2, lsr #5
	and	r9, r2, #0x1f	; r9 reference bit length
	mov	r2, r2, lsr #5
	and	r8, r2, #0x1f	; r8 count bit length
	mov	r2, r2, lsr #5
	sub	r3, r3, #0x0f	; valid bits in r2
|Uncomp00Loop|:
	cmp	r12, r11
	bcs	|Uncomp00Done|
	cmp	r3, #0
	bleq	|Uncomp00refillreg|
	tst	r2, #1
	mov	r2, r2, lsr #1
	sub	r3, r3, #1
	bne	|Uncomp00repeat|
|Uncomp00literals|:
	mov	r4, #0		; number
	mov	r5, #0		; shift
	mov	r7, #1
	mov	r7, r7, lsl r10
	sub	r7, r7, #1	; mask = (1<<len)-1
|Uncomp00litnum|:
	mov	r6, r2
	cmp	r3, r10
	ble	|Uncomp00litrefill|
	add	lr, r10, #1
	mov	r2, r2, lsr lr
	sub	r3, r3, lr
|Uncomp00litjoin|:
	and	lr, r6, r7
	orr	r4, r4, lr, lsl r5
	add	r5, r5, r10
	mov	r6, r6, lsr r10
	tst	r6, #1
	bne	|Uncomp00litnum|
|Uncomp00litcopy|:
	cmp	r3, #8
	blt	|Uncomp00litcprefill|
	cmp	r3, #16
	blt	|Uncomp00litcpfull1|
	cmp	r3, #24
	blt	|Uncomp00litcpfull2|
|Uncomp00litcpfull3|:
	strb	r2, [r12], #1
	movs	r2, r2, lsr #8
	sub	r3, r3, #8
	subs	r4, r4, #1
	blt	|Uncomp00Loop|
|Uncomp00litcpfull2|:
	strb	r2, [r12], #1
	movs	r2, r2, lsr #8
	sub	r3, r3, #8
	subs	r4, r4, #1
	blt	|Uncomp00Loop|
|Uncomp00litcpfull1|:
	strb	r2, [r12], #1
	mov	r2, r2, lsr #8
	sub	r3, r3, #8
	subs	r4, r4, #1
	blt	|Uncomp00Loop|
|Uncomp00litcprefill|:
	mov	r5, r2
	bl	|Uncomp00refillreg|
	sub	lr, r3, #32
	orr	r5, r5, r2, lsl lr
	strb	r5, [r12], #1
	sub	r3, r3, #8
	rsb	lr, r3, #32
	mov	r2, r2, lsr lr
	subs	r4, r4, #1
	bge	|Uncomp00litcpfull3|
	b	|Uncomp00Loop|
|Uncomp00litrefill|:
	bl	|Uncomp00refillreg|
	sub	lr, r3, #32	; number of valid bits before refilling
	orr	r6, r6, r2, lsl lr
	sub	r3, r3, r10
	sub	r3, r3, #1
	rsb	lr, r3, #32
	mov	r2, r2, lsr lr
	b	|Uncomp00litjoin|
|Uncomp00repeat|:
	mov	r4, #0		; see literals
	mov	r5, #0
	mov	r7, #1
	mov	r7, r7, lsl r9
	sub	r7, r7, #1
|Uncomp00refnum|:
	mov	r6, r2
	cmp	r3, r9
	ble	|Uncomp00refrefill|
	add	lr, r9, #1
	mov	r2, r2, lsr lr
	sub	r3, r3, lr
|Uncomp00refjoin|:
	and	lr, r6, r7
	orr	r4, r4, lr, lsl r5
	add	r5, r5, r9
	mov	r6, r6, lsr r9
	tst	r6, #1
	bne	|Uncomp00refnum|
	mov	r11, #0
	mov	r5, #0
	mov	r7, #1
	mov	r7, r7, lsl r8
	sub	r7, r7, #1
|Uncomp00countnum|:
	mov	r6, r2
	cmp	r3, r8
	ble	|Uncomp00countrefill|
	add	lr, r8, #1
	mov	r2, r2, lsr lr
	sub	r3, r3, lr
|Uncomp00countjoin|:
	and	lr, r6, r7
	orr	r11, r11, lr, lsl r5
	add	r5, r5, r8
	mov	r6, r6, lsr r8
	tst	r6, #1
	bne	|Uncomp00countnum|
	sub	r4, r12, r4
	sub	r4, r4, #1
	subs	r5, r11, #1		; (+MINIMUM_REPEATS - 4)
	ldr	r11, [sp, #local_upper]	; restore upper output boundary
	blt	|Uncomp00repeatsmall|
|Uncomp00repeatloop|:
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	subs	r5, r5, #4
	bge	|Uncomp00repeatloop|
|Uncomp00repeatsmall|:
	adds	r5, r5, #4
	ble	|Uncomp00Loop|
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	subs	r5, r5, #1
	ble	|Uncomp00Loop|
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	subs	r5, r5, #1
	ble	|Uncomp00Loop|
	ldrb	lr, [r4], #1
	strb	lr, [r12], #1
	b	|Uncomp00Loop|
|Uncomp00refrefill|:
	bl	|Uncomp00refillreg|
	sub	lr, r3, #32
	orr	r6, r6, r2, lsl lr
	sub	r3, r3, r9
	sub	r3, r3, #1
	rsb	lr, r3, #32
	mov	r2, r2, lsr lr
	b	|Uncomp00refjoin|
|Uncomp00countrefill|:
	bl	|Uncomp00refillreg|
	sub	lr, r3, #32
	orr	r6, r6, r2, lsl lr
	sub	r3, r3, r8
	sub	r3, r3, #1
	rsb	lr, r3, #32
	mov	r2, r2, lsr lr
	b	|Uncomp00countjoin|
|Uncomp00Done|:
	mov	r0, #0
|Uncomp00Exit|:
	add	sp, sp, #local_segsize
	ldmia	sp!, {r4-r12, pc}^

|Uncomp00refillreg|:
	cmp	r0, r1
	ldrcc	r2, [r0], #4
	addcc	r3, r3, #32
	movccs	pc, lr
|Uncomp00refill|:
	ldr	r0, [sp, #local_csize]
	cmp	r0, #0
	mvnle	r0, #0
	ble	|Uncomp00Exit|
	ldr	r1, [sp, #local_insize]
	cmp	r1, r0
	movge	r2, r0
	movlt	r2, r1
	sub	r0, r0, r2
	str	r0, [sp, #local_csize]
	ldr	r0, [sp, #local_inbuff]
	stmdb	sp!, {r3, r10, r11, r12, lr}
	ldr	r3, [sp, #(local_file+0x14)]
	ldr	r10, [sp, #(local_slwm+0x14)]
	mov	r1, #1
	bl	|fread|		; fread(inbuff, 1, min(inSize, compSize), file)
	ldr	r2, [sp, #(local_inbuff+0x14)]
	add	r1, r2, r0
	mov	r0, r2
	ldr	r2, [r0], #4
	ldmia	sp!, {r3, r10, r11, r12, lr}
	add	r3, r3, #32
	movs	pc, lr








; Optional buffered keyboard by Darren Salt

	ALIGN	4
	EXPORT	InitKeypress
	=	"InitKeypress"
	ALIGN	4
InitKeypress
	MOV	r0,#0
	LDR	r1,=KeypressData
	MOV	r2,#128/4
IKwipe	STR	r0,[r1],#4
	SUBS	r2,r2,#1
	BNE	IKwipe
	MOV	r0,#16
	ADR	r1,Keypress
	LDR	r2,=KeypressData
	SWI	&2001F ; XOS_Claim
	MOVVC	r0,#14
	MOVVC	r1,#11
	SWIVC	&20006 ; XOS_Byte
	MOVVC	r0,#0
	MOVS	pc,r14


	ALIGN	4
	EXPORT	ShutdownKeypress
	=	"ClosedownKeypress"
	ALIGN	4
ShutdownKeypress
	MOV	r0,#16
	ADR	r1,Keypress
	LDR	r2,=KeypressData
	SWI	&20020 ; XOS_Release
	MOVVC	r0,#13
	MOVVC	r1,#11
	SWIVC	&20006 ; XOS_Byte
	MOVS	pc,r14


	ALIGN	4
	=	"Keypress"
	ALIGN	4
Keypress			; internal interrupt code
	TEQ	r0,#11
	MOVNES	pc,lr
	STMFD	sp!,{r4-r6,lr}
	LDRB	r5,[r12,#128]
	LDRB	r6,[r12,#129]
	ORR	r4,r1,r2,LSL #1
	ADD	lr,r5,#1
	AND	lr,lr,#127
	TEQ	lr,r6		; if =, then queue is full...
	STRNEB	r4,[r12,r5]
	STRNEB	lr,[r12,#128]	; update head
	LDMFD	sp!,{r4-r6,pc}^


	ALIGN	4
	EXPORT	GetKeypress
	=	"GetKeypress"
	ALIGN	4
GetKeypress
	LDR	r1,=KeypressData
	LDRB	r2,[r1,#128]
	LDRB	r3,[r1,#129]
	TEQ	r2,r3
	MVNEQ	r0,#0		; no key data!
	BEQ	gotkey
	LDRB	r0,[r1,r3]	; b0 set = pressed, else released; b1-7 = key
	ADD	r3,r3,#1
	AND	r3,r3,#127
	STRB	r3,[r1,#129]	; update tail
gotkey	MOVS	pc,lr


	ALIGN	4
	EXPORT	FlushKeypress
	=	"FlushKeypress"
	ALIGN	4
FlushKeypress ; lose pairs of keypress events
	STMFD	sp!,{r4-r6,lr}
	LDR	r0,=KeypressData
	LDRB	r1,[r0,#128]		; head ptr
	LDRB	r2,[r0,#129]		; tail ptr
	TEQ	r1,r2
	BEQ	flushed			; empty? exit immediately
	MOV	r1,r1,LSL #25		; (this avoids AND rn,#127)
	MOV	r2,r2,LSL #25
	MOV	r3,r2			; write ptr
	SUB	r1,r1,#1<<25
	SUB	r2,r2,#1<<25
flushloop
	LDRB	r5,[r0,r2,LSR #25]
	SUB	r2,r2,#1<<25
	TEQ	r1,r2			; first entry?
	BEQ	flushwrite
	TEQ	r5,#255			; wiped entry?
	BEQ	flushloop
	EOR	r5,r5,#1
	MOV	r4,r2
flushscan ; looking for opposite event for same key
	LDRB	r6,[r0,r4,LSR #25]
	TEQ	r6,r5
	SUBNE	r4,r4,#1<<25
	TEQNE	r4,r1
	BNE	flushscan
	TEQ	r4,r1
	EOREQ	r5,r5,#1
	SUBEQ	r3,r3,#1<<25
	STREQB	r5,[r0,r3,LSR #25]	; not found? keep this event
	MOVNE	r5,#255
	STRNEB	r5,[r0,r4,LSR #25]	; found? wipe it
	B	flushloop
flushwrite
	TEQ	r5,#255
	SUBNE	r3,r3,#1<<25
	STRNEB	r5,[r0,r3,LSR #25]	; first entry - keep this event
	MOV	r3,r3,LSR #25
	STRB	r3,[r0,#129]		; write new tail ptr
flushed	LDMFD	sp!,{r4-r6,pc}^



	AREA	|Asm$$Data_ROasm|, DATA

	EXPORT	KeypressData
KeypressData
	%	132 ; +0..127 = data, +128 = head, +129 = tail



	AREA	|Asm$$Data_ROasm|, DATA

|SafeAbortVariables|:
	%	sah_size


	END
