    ;  32bit ARM modifications

 KEEP ;debug information
TRACE * 0


	GET	h.RegNames

;	IMPORT	sort_card_file
	IMPORT	src_fr
	IMPORT	chars_lc
	IMPORT  max_source_cat

	EXPORT	move_down_1
	EXPORT  move_up_1
	EXPORT	sorter_setup
	EXPORT	standard_sorter
        EXPORT  box_sorter
	EXPORT	standard_sorter2
        EXPORT  box_sorter2
        EXPORT  int2_sorter
	EXPORT	article_sorter
	EXPORT  date_sorter
	EXPORT  msgid_sorter
	EXPORT  msgid_cptr_sorter
	EXPORT  box_selector_1

; spellcheck bits

	EXPORT	get_dict_hash
	EXPORT	get_hash9
	EXPORT	lookup_dict
	EXPORT  crossword_lookup

;	IMPORT	|x$stack_overflow|
	IMPORT  |__rt_stkovf_split_small|

	IMPORT	lookup_exceptions
	IMPORT	add_word1
	IMPORT	add_word0
	IMPORT	endtab
	IMPORT	endtab_len
	IMPORT	ending_index
	IMPORT	dict_addr
	IMPORT	hash_table

	; for apply_rules()
	EXPORT	apply_rules

;	IMPORT	output
	IMPORT	rule_options
	IMPORT	index_tab
	IMPORT	rules_data


	AREA	Assemb, CODE, READONLY


	; r0   pointer to highest addressed byte to be moved
	; r1   number of bytes to move

move_down_1
;**********
	STMFD	r13!,{r4-r9,lr}
	ADD	r0,r0,r1
	SUB	r0,r0,#1	; points at last byte to be moved

mvd1
	AND	r3,r0,#3
	CMP	r3,#2
	BEQ	mvd2		; r0 now pointing to byte 2 in word
	LDRB	r2,[r0]
	STRB	r2,[r0,#1]
	SUB	r0,r0,#1
	SUBS	r1,r1,#1
	BEQ	mvd_end
	B	mvd1
mvd2
	; r0 points at byte 2 in word,  byte 3 is free
	SUB	r0,r0,#2	; r0 points to byte 0
	LDR	r2,[r0]
	MOV	r2,r2, LSL #8

mvd4
	SUBS	r1,r1,#24
	BLT	mvd5	; finished moving chunks

	SUB	r0,r0,#24
	LDMIA	r0!,{r3,r4,r5,r6,r7,r8}

	ADD	r2,r2,r8, LSR #24
	STR	r2,[r0]

	MOV	r8,r8, LSL #8
	ADD	r8,r8,r7, LSR #24
	MOV	r7,r7, LSL #8
	ADD	r7,r7,r6, LSR #24
	MOV	r6,r6, LSL #8
	ADD	r6,r6,r5, LSR #24
	MOV	r5,r5, LSL #8
	ADD	r5,r5,r4, LSR #24
	MOV	r4,r4, LSL #8
	ADD	r4,r4,r3, LSR #24
	MOV	r2,r3, LSL #8

	SUB	r0,r0,#20
	STMIA	r0,{r4,r5,r6,r7,r8}
	SUB	r0,r0,#4

	B	mvd4

mvd5	; finish off the remaining bytes
	; r0 points to word with top byte free

	ADD	r1,r1,#24
	ADD	r0,r0,#2

mvd6
	LDRB	r2,[r0]
	STRB	r2,[r0,#1]
	SUB	r0,r0,#1
	SUBS	r1,r1,#1
	BGT	mvd6

mvd_end
	LDMFD	r13!,{r4-r9,pc}



move_up_1
;********

    STMFD   r13!,{r4-r9,r14}
mvu_1
    AND     r3,r0,#3
    CMP     r3,#1
    BEQ     mvu_2
    LDRB    r2,[r0],#0
    STRB    r2,[r0,#-1]
    ADD     r0,r0,#1
    SUBS    r1,r1,#1
    BLE     mvu_7
    B       mvu_1
mvu_2
    SUB     r0,r0,#1
    LDMIA   r0,{r3-r8}
    B       mvu_4
mvu_3
    LDMIA   r0,{r3-r8}
    ADD     r2,r2,r3,LSL #24
    STR     r2,[r0,#-4]
mvu_4
    SUBS    r1,r1,#&18
    BLT     mvu_5
    MOV     r3,r3,LSR #8
    ADD     r3,r3,r4,LSL #24
    MOV     r4,r4,LSR #8
    ADD     r4,r4,r5,LSL #24
    MOV     r5,r5,LSR #8
    ADD     r5,r5,r6,LSL #24
    MOV     r6,r6,LSR #8
    ADD     r6,r6,r7,LSL #24
    MOV     r7,r7,LSR #8
    ADD     r7,r7,r8,LSL #24
    MOV     r2,r8,LSR #8
    STMIA   r0,{r3-r7}
    ADD     r0,r0,#&18
    B       mvu_3
mvu_5
    ADD     r1,r1,#&18
    ADD     r0,r0,#1
mvu_6
    LDRB    r2,[r0],#0
    STRB    r2,[r0,#-1]
    ADD     r0,r0,#1
    SUBS    r1,r1,#1
    BGT     mvu_6
mvu_7
    LDMFD   r13!,{r4-r9,pc}



;xcard_file
;	DCD	sort_card_file
xsrc_fr
	DCD	src_fr
xchars_lc
	DCD	chars_lc

sorter_setup
	MOV	pc,lr



date_sorter
;==========
; Sorter for sort by date
; R0 void *a1,  R1 void *a2
	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r0,r0,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r1,r1,LSR #7	; cptr2

	LDR	r0,[r0,#8]
	MOV	r0,r0,LSL #6
	MOV	r0,r0,LSR #6
	LDR	r1,[r1,#8]
	MOV	r1,r1,LSL #6
	MOV	r1,r1,LSR #6
	SUB	r0,r0,r1
	MOV	pc,lr


box_sorter
;=========
; Sorter for sort by box, newsgroups, title, date.
; More efficient than the general sorter
; R0 void *a1,  R1 void *a2
	STMDB	sp!,{r4,r5,r6}

	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r3,r0,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r4,r1,LSR #7	; cptr2

	LDR     r0,[r3,#8]
	MOV     r0,r0,LSR #26
        LDR     r1,[r4,#8]
        SUBS    r0,r0,r1,LSR #26
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr
	B	gs_1


standard_sorter
;==============
; Sorter for sort by newsgroups, title, date.
; More efficient than the general sorter
; R0 void *a1,  R1 void *a2
	STMDB	sp!,{r4,r5,r6}

	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r3,r0,LSR #7

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r4,r1,LSR #7

gs_1
	LDR	r2,xsrc_fr       ;****
	MOV	r1,#20		; width of CAT
	LDR	r2,[r2,#4]	; src_fr.cat_data

	LDRB	r5,[r3,#12]	; cptr->source
	MLA	r0,r5,r1,r2	; src_fr.cat_data[cptr1->source]

	LDRB	r5,[r4,#12]
	MLA	r2,r5,r1,r2	; src_fr.cat_data[cptr2->source]

	LDR	r1,[r0,#8]
	MOV	r0,r1,LSR #16	; short position
	LDR	r1,[r2,#8]
	MOV	r2,r1,LSR #16

	SUBS	r0,r0,r2
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr

; sort on title
	LDR     r6,xchars_lc
	LDRB	r5,[r3,#18]	; cptr1->title
	LDRB	r2,[r4,#18]	; cptr2->title
	ADD	r5,r5,r3
	ADD	r5,r5,#22	; Data offset, PLUTO1
	ADD	r2,r2,r4
	ADD	r2,r2,#22
;	B	sort1		; Join standard_sorter2 code
sort1
	LDRB	r0,[r5],#1
	LDRB	r0,[r6,r0]	; convert to lower case, loose accents
	LDRB	r1,[r2],#1
	LDRB	r1,[r6,r1]	; convert to lower case, loose accents
	SUBS	r0,r0,r1
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr
	CMP	r1,#0
	BNE	sort1

; sort on date
	LDR	r1,[r3,#8]
	MOV	r1,r1,LSL #6
	MOV	r1,r1,LSR #6	; cptr1->date
	LDR	r2,[r4,#8]
	MOV	r2,r2,LSL #6
	SUBS	r0,r1,r2,LSR #6
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr

; sort on article address (just to ensure they are different)
	LDR	r1,[r3,#0]
	LDR	r2,[r4,#0]
	SUBS	r0,r1,r2
	LDMIA	sp!,{r4,r5,r6}
	MOV	pc,lr



box_sorter2
;==========
; For CARD format in PLUTO2
; Sorter for sort by box, newsgroups, title, date.
; More efficient than the general sorter
; R0 void *a1,  R1 void *a2
	STMDB	sp!,{r4,r5,r6}

	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r3,r0,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r4,r1,LSR #7	; cptr2

	LDR     r0,[r3,#8]
	MOV     r0,r0,LSR #26
        LDR     r1,[r4,#8]
        SUBS    r0,r0,r1,LSR #26
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr
	B	gs_1


standard_sorter2
;===============
; For CARD format in PLUTO2
; Sorter for sort by newsgroups, title, date.
; More efficient than the general sorter
; R0 void *a1,  R1 void *a2
	STMDB	sp!,{r4,r5,r6}

	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r3,r0,LSR #7

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r4,r1,LSR #7

gs_2
	LDR	r2,xsrc_fr       ;****
	MOV	r1,#20		; width of CAT
	LDR	r2,[r2,#4]	; src_fr.cat_data

	LDRB	r5,[r3,#12]	; cptr->source
	MLA	r0,r5,r1,r2	; src_fr.cat_data[cptr1->source]

	LDRB	r5,[r4,#12]
	MLA	r2,r5,r1,r2	; src_fr.cat_data[cptr2->source]

	LDR	r1,[r0,#8]
	MOV	r0,r1,LSR #16	; short position
	LDR	r1,[r2,#8]
	MOV	r2,r1,LSR #16

	SUBS	r0,r0,r2
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr

; sort on title
	LDR     r6,xchars_lc
	LDRB	r5,[r3,#18]	; cptr1->title
	LDRB	r2,[r4,#18]	; cptr2->title
	ADD	r5,r5,r3
	ADD	r5,r5,#32	; Data offset for PLUTO2
	ADD	r2,r2,r4
	ADD	r2,r2,#32
sort2
	LDRB	r0,[r5],#1
	LDRB	r0,[r6,r0]	; convert to lower case, loose accents
	LDRB	r1,[r2],#1
	LDRB	r1,[r6,r1]	; convert to lower case, loose accents
	SUBS	r0,r0,r1
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr
	CMP	r1,#0
	BNE	sort1

; sort on date
	LDR	r1,[r3,#8]
	MOV	r1,r1,LSL #6
	MOV	r1,r1,LSR #6	; cptr1->date
	LDR	r2,[r4,#8]
	MOV	r2,r2,LSL #6
	SUBS	r0,r1,r2,LSR #6
	LDMNEIA	sp!,{r4,r5,r6}
	MOVNE	pc,lr

; sort on article address (just to ensure they are different)
	LDR	r1,[r3,#0]
	LDR	r2,[r4,#0]
	SUBS	r0,r1,r2
	LDMIA	sp!,{r4,r5,r6}
	MOV	pc,lr



article_sorter
;=============
; Sorter for sort article address
; R0 void *a1,  R1 void *a2
	LDR	r0,[r0]
	MOV	r0,r0,LSL #9
	MOV	r2,r0,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r1,r1,LSL #9
	MOV	r3,r1,LSR #7	; cptr2

	LDR	r0,[r2,#0]
	LDR	r1,[r3,#0]
	SUBS	r0,r0,r1
	MOVNE	pc,lr
; sort on date+box (just to ensure they are different)
	LDR	r0,[r2,#8]
	LDR	r1,[r3,#8]
	SUB	r0,r0,r1
	MOV	pc,lr


int2_sorter
;==========
; Just sort integers
	MOV	r3,r0
	LDR	r0,[r3]
	LDR	r2,[r1]
	SUBS	r0,r2,r0	; compare integer values
	MOVNE	pc,lr
	LDR	r0,[r3,#4]
	LDR	r2,[r1,#4]
	SUBS	r0,r0,r2	; if equal, compare 2nd integer
	MOV	pc,lr


msgid_sorter
;============
; Sorter for sort message id hash code
; R0 void *a1,  R1 void *a2
	LDR	r0,[r0]
	MOV	r2,r0,LSL #9
	MOV	r2,r2,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r3,r1,LSL #9
	MOV	r3,r3,LSR #7	; cptr2

	LDR	r0,[r2,#24]	; compare msgid hash codes
	LDR	r1,[r3,#24]
	SUBS	r0,r1,r0
	MOVNE	r0,#1
	MOVCS	r0,#&80000000
	MOVNE	pc,lr

	LDRB    r0,[r2,#12]	; msgids are equal, compare source
	LDRB	r1,[r3,#12]
	SUBS	r0,r0,r1
	SUBEQS	r0,r3,r2	; if everything is equal, compare cptrs
	MOV	pc,lr


msgid_cptr_sorter
;================
; Sorter for sort message id hash code
; ixlist already contains proper cptrs
; R0 void *a1,  R1 void *a2
	LDR	r0,[r0]
	MOV	r2,r0,LSL #7
	MOV	r2,r2,LSR #7	; cptr1

	LDR	r1,[r1]
	MOV	r3,r1,LSL #7
	MOV	r3,r3,LSR #7	; cptr2

	LDR	r0,[r2,#24]	; compare msgid hash codes
	LDR	r1,[r3,#24]
	SUBS	r0,r1,r0
	MOVNE	r0,#1
	MOVCS	r0,#&80000000
	MOVNE	pc,lr

	LDRB    r0,[r2,#12]	; msgids are equal, compare source
	LDRB	r1,[r3,#12]
	SUBS	r0,r0,r1
	SUBEQS	r0,r3,r2	; if everything is equal, compare cptrs
	MOV	pc,lr


box_selector_1
;=============
;Mark all articles that belong to a specified Box
; r0  Box number
; r1  ixlist
; r2  n_entries
; r3
; returns: number of articles selected

	STMDB	sp!,{r4-r7}
	ADD	r2,r1,r2,LSL #2	; end address
	MOV	r4,#0		; count of marked articles

bs_loop	CMP	r1,r2
	BEQ	bs_end
	LDR	r5,[r1]		; ixlist entry
	MOV	r6,r5,LSL #9
	MOV	r6,r6,LSR #7	; cptr
	LDR	r7,[r6,#8]	; date_box
	MOV	r7,r7,LSR #26
	CMP	r7,r0
	BEQ	bs_match
bs_skip	BIC	r5,r5,#&04000000  ; IXLIST_MARKED
	STR	r5,[r1],#4
	B	bs_loop

bs_match CMP	r7,#62		; BOX_BIN
	BEQ	bs_1
bs_2	ORR	r5,r5,#&04000000  ; IXLIST_MARKED
	STR	r5,[r1],#4
	ADD	r4,r4,#1	; count of marked articles
	B	bs_loop

bs_1	LDRB	r7,[r6,#14]	; status
	AND	r7,r7,#15       ; 4-bit status value
	CMP	r7,#7		; STATUS_HIDDEN
	BNE	bs_2
	B	bs_skip

bs_end	MOV	r0,r4
	LDMIA	sp!,{r4-r7}
	MOV	pc,lr


;=================================================================
;                  SPELLCHECK
;=================================================================




        ; r0 = string
        ; return  hash code

get_dict_hash
;************
	STMFD	r13!,{lr}
	MOV	r1,#0
	MOV	r3,#&1000
	SUB	r3,r3,#1

d_hash_loop
	LDRB	r2,[r0],#1
	CMP	r2,#0
	BEQ	d_hash_end
	SUB	r2,r2,#&61
	ADD	r1,r2,r1,LSL #3
	AND	r2,r1,r3
	EOR	r1,r2,r1,LSR #12
	B	d_hash_loop
d_hash_end
	AND	r0,r1,r3
	LDMFD	r13!,{pc}



get_hash9
;********
	STMFD	r13!,{lr}
             	MOV	r1,#0
	MOV   	r3,#&200
	SUB	r3,r3,#1

hash9_loop
	LDRB	r2,[r0],#1
	CMP	r2,#0
	BEQ	hash9_end
	SUB	r2,r2,#&61
	ADD	r1,r2,r1,LSL #3
	AND	r2,r1,r3
	EOR	r1,r2,r1,LSR #9
	B	hash9_loop
hash9_end
	AND	r0,r1,r3
	LDMFD	r13!,{pc}



; lookup_dict (char *word)
;=========================


              ^ 0,sp    ; variables relative to register
start_vars	# 0
xframe_size	# 0

frame_size    * xframe_size - start_vars


x_ending_index	DCD	ending_index
x_endtab	DCD	endtab
x_endtab_len	DCD	endtab_len
x_dict_addr	DCD	dict_addr
x_hash_table	DCD	hash_table



lookup_dict
;**********
	MOV	ip,sp		;set up a stack
	STMFD	sp!, {r2,r4-r9,r10, fp,ip,lr,pc}
	SUB	fp,ip,#4

	MOV	r5,r0

ld_1
	LDRB	r2,[r0],#1	;find end of word
	CMP	r2,#0
	BNE	ld_1
	SUB	r0,r0,#1	;ptr to zero after last letter in word
	B	lookup_dict_join

lookup_dict2		; entry point from within assembler
	; r0  ptr to zero at end of word
	; r1  ptr to start of word
	MOV	ip,sp		;set up a stack
	STMFD	sp!, {r2,r4-r9,r10, fp,ip,lr,pc}
	SUB	fp,ip,#4
	MOV	r5,r1		;start of word

lookup_dict_join
; find ending
	LDRB	r2,[r0,#-1]
	SUB	r2,r2,#'a'
	LDRB	r3,[r0,#-2]
	SUB	r3,r3,#'a'
	ADD	r2,r2,r3,LSL #4
	AND	r2,r2,#&ff

	LDR	r1,x_ending_index
	LDR	r1,[r1]         ; r2 is index into short[]
	MOVS	r3,r2,ROR #1
	LDR	r3,[r1,r3,LSL #2] ; avoid unaligned index
	MOVCS	r3,r3,ROR #16
	ANDS	r2,r3,#&ff	;num.of ending with this ending index
fe_1
	BNE	fe_2
fe_1a
	MOV	r2,#-1
	LDRB	r1,[r0,#-1]	;last letter of word
	CMP	r1,#'e'
	MOVEQ	r2,#1
	CMP	r1,#'s'
	MOVEQ	r2,#2
	CMP	r1,#'y'
	MOVEQ	r2,#3		;ending code in r2

	SUB	r1,r0,r5
	CMP	r2,#0
	SUBGT	r1,r1,#1
	B	ld_hash


fe_4
	ADD	r3,r3,#1
	SUBS	r2,r2,#1
	BGT	fe_3
	B	fe_1a		;not found


fe_2			;lookup ending
	SUB	r8,r0,r5	;length of word
	MOV	r3,r3,LSR #8
	AND	r3,r3,#&ff	;index to endtab

	LDR	r6,x_endtab_len
	LDR	r4,x_endtab
fe_3
	LDRB	r7,[r6,r3]	;length of ending
	CMP	r7,r8
	BGE	fe_4		;ending too long for this word

	LDR	r1,[r4,r3,LSL #2]	;r1->ending
	SUB	r9,r0,r7	;&word[length-endlen]

fe_6
	LDRB	lr,[r1],#1	;strcmp(r1,r9)
	LDRB	ip,[r9],#1
	CMP	lr,ip
	BNE	fe_4
	CMP	lr,#0
	BEQ	fe_5

	LDRB	lr,[r1],#1	;strcmp(r1,r9)   unwound loop
	LDRB	ip,[r9],#1
	CMP	lr,ip
	BNE	fe_4
	CMP	lr,#0
	BNE	fe_6
;	B	fe_5		;matched OK

fe_5
	;found ending
	MOV	r2,r3		;ending number
	SUB	r1,r8,r7	;length of stem
;	B	ld_hash

ld_hash
	MOV	r7,r1		;length of stem
	MOV	r0,r5		;word
;	MOV	r4,#0
	MOV	r3,#&1000
	SUB	r3,r3,#1

ld_hash_loop
; r0 must be on word boundary
	LDR	ip,[r0],#4	; first four bytes
	AND	r4,ip,#&ff
	SUBS	r7,r7,#1
	BLE	ld_hash_end

	MOV	ip,ip,LSR #8
	AND	r6,ip,#&ff
	ADD	r4,r6,r4, LSL #3
	SUBS	r7,r7,#1
	BLE	ld_hash_end

	MOV	ip,ip,LSR #8
	AND	r6,ip,#&ff
	ADD	r4,r6,r4, LSL #3
	SUBS	r7,r7,#1
	BLE	ld_hash_end

	MOV	r6,ip,LSR #8	; dont need AND &ff for top byte
	ADD	r4,r6,r4, LSL #3
	AND	r6,r4,r3
	EOR	r4,r6,r4,LSR #12
	SUBS	r7,r7,#1
	BLE	ld_hash_end

ld_hash_loop2
	LDRB	r6,[r0],#1
	ADD	r4,r6,r4,LSL #3
	AND	r6,r4,r3
	EOR	r4,r6,r4,LSR #12
	SUBS	r7,r7,#1	;length of stem
	BLE	ld_hash_end

	LDRB	r6,[r0],#1
	ADD	r4,r6,r4,LSL #3
	AND	r6,r4,r3
	EOR	r4,r6,r4,LSR #12
	SUBS	r7,r7,#1	;length of stem
	BGT	ld_hash_loop2

ld_hash_end
	AND	r0,r4,r3

	LDR	r3,x_dict_addr
	LDR	r3,[r3]
	LDR	r4,x_hash_table
	LDR	r4,[r4]
	LDR	r6,[r4,r0,LSL #2]
	ADD	r6,r6,r3	; p = dict_addr + hash_tab[hash]
	ADD	r4,r4,#4
	LDR	r7,[r4,r0,LSL #2]
	ADD	r7,r7,r3	; end = dict_addr + hash_tab[hash+1]

	ADR	r8,letters
	LDRB	r0,[r6],#1	; c = *p++
	B	ld_loop


ld_skip1
	LDRB	r0,[r6],#1
ld_skip
	CMP	r0,#70		; while (c > 70)
	BLE	ld_loop

	LDRB	r0,[r6],#1
	CMP	r0,#70
	BGT	ld_skip1
ld_loop
	CMP	r6,r7
	BGE	ld_end0		; finished all stems for this hash value


	CMP	r0,#35		; 1st byte indicates if stem itself is a valid word
	ADDGT	r0,r0,#35
	MOVGT	r3,#1		; stem_wod = 1
	ADDLE	r0,r0,#70
	MOVLE	r3,#0		; stem_word = 0
;NOTE It would be better to have 'word' contain 'looked up' values here
; - range 0 to 35, then we wouldn't need the 'letters' translation in
; the loop below.

	MOV	r9,r1		; length of stem
	MOV	r4,r5		; word
ld_10
	SUBS	ip,r0,#70
	MOVLE	ip,#0
	CMP	ip,#35
	MOVGT	ip,#0
	LDRB	ip,[r8,ip]	; r0 = letters[c-70]
;	 ADD ip,ip,#'a'-4	; faster, but ' is a problem
	LDRB	lr,[r4],#1
	CMP	ip,lr
	BLT	ld_skip
	BGT	ld_end0

	LDRB	r0,[r6],#1	; c = *p++
	SUBS	r9,r9,#1	; length of stem
	BGT	ld_10

	; matched stem
ld_11
	CMP	r2,#0		; required ending code
	BGE	ld_12

	; matched on stem only (ending == -1)
	CMP	r0,#254
	MOVLT	r0,r3
	BLT	ld_end

	MOV	r0,r5
	MOV	r6,r3		; stem_word (save across poc.call)
	LDR	r1,[fp,#-&2c]	; points
	BL	lookup_exceptions
	MOV	r0,r6		; stem_word
	B	ld_end		; return(stem_word)

ld_12
	; is the required ending listed ? UNFOLD LOOP x2
;NOTE We could order the ending codes in the list ??
	SUBS	r0,r0,#105
	BLE	ld_end0		; ending not found in list
	CMP	r2,r0
	BEQ	ld_13		; found
	LDRB	r0,[r6],#1

	SUBS	r0,r0,#105
	BLE	ld_end0		; ending not found in list
	CMP	r2,r0
	BEQ	ld_13
	LDRB	r0,[r6],#1

	B	ld_12
ld_13
	LDRB	r9,[r6]
	CMP	r9,#254
	MOVGE	r0,r5
	LDR	r1,[fp,#-&2c]	; points
	BLGE	lookup_exceptions
	CMP	r9,#254
	MOVEQ	r0,#0
	BEQ	ld_end		; return(0)
	MOV	r0,#1
	B	ld_end		; return(1)



ld_end0
	MOV	r0,#0
ld_end
	LDMEA	fp, {r2,r4-r9,r10, fp,sp,pc}   ; procedure exit

letters
	DCB	0
	=	"?-'abcdefghijklmnopqrstuvwxyz        "





;



	; r0 word
	; r1 index into word
	; r2 bits 0-7 count of changes
	;    bit 8   no change since last, don't add to list
	; r3 condition

              ^ 0,sp    ; variables relative to register
start_vars2	# 0
condition	# 4
new_ix		# 4
word_end	# 4
points		# 4
 [ TRACE = 1
Trace1		# 4
 ]
neww		# 44	; copy of word
xframe_size2	# 0

frame_size2    * xframe_size2 - start_vars2



x_rule_options	DCD	rule_options
x_index_tab	DCD	index_tab
x_rules_data	DCD	rules_data

;x_f_trace	DCD	f_trace
xx_fmt		=	'%','5','x',' ',' ','%','s',' ',&a,0
		ALIGN


apply_rules
;**********
	MOV	ip,sp		;set up a stack

	STMFD	sp!, {r4-r9, fp,ip,lr,pc}

	SUB	fp,ip,#4
	SUB	sp,sp,#frame_size2   ;reserve space for local variables

	CMPS	sp,sl
 BLT end_apply_rules
;	BLLT    |__rt_stkovf_split_small|
;	BLLT	|x$stack_overflow|

	STR	r3,condition

	MOV	r5,r0		; word - dont use r10 (sl)
	MOV	r9,r1		; ix
	MOV	r8,r2

	ADD	r7,r5,r9
	LDRB	r0,[r7]
	CMP	r0,#0
	BEQ	end_apply_rules	; end of word

	ANDS	r1,r8,#&f00
	BNE	rules_section	; already done 'insert between'

; call 'insert between' rule set if both letters are consonants (not 'y')
;   and they are different.
	ADR	r2,ctype2
	LDRB	ip,[r2,r0]
	ANDS	ip,ip,#9	; is_consonant(c)
	CMP	ip,#1

	BNE	rules_section
	LDRB	r1,[r7,#-1]
	CMP	r0,r1
	BEQ	rules_section	; (c != w1[-1])
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#9	; is_consonant(w1[-1])
	CMP	ip,#1
	BNE	rules_section

	MOV	r0,r5
	MOV	r1,r9
	ORR	r2,r8,#&800
	LDR	r3,condition
	BL	apply_rules	;apply_rules, dont do insert at this point

	MOV	r0,#26	; rules section 26 for inserting between consonants
	ORR	r8,r8,#&800	; bit 7 indicates we have done 'insert between'
	ADD	r8,r8,#3	; set in bits 0-3 to flag 'have inserted'
	B	l_rules

 [ TRACE = 1
xx_fmt2		=	&9,'%','4','s',' ','%','s',&a,0
	ALIGN
 ]

rules_section
	MOV	r0,r5
	ADD	r1,r9,#1
	AND	r2,r8,#&ff
	LDR	r3,condition
	BL	apply_rules	;apply_rules(word,ix+1,n,condition)

	LDRB	r0,[r5,r9]
	AND	r8,r8,#&ff	; n = n & 0xff
	SUBS	r0,r0,#&61	; c = c - 'a'
	BLT	end_apply_rules
	CMP	r0,#26
	BGE	end_apply_rules
l_rules

 [ TRACE = 1
	MOV r4,r0
	LDR r0,x_f_trace
	LDR r0,[r0]
	ADR r1,xx_fmt
	ADD r2,r8,r9, LSL #16
	MOV r3,r5
	BL  fprintf
	MOV r0,r4
 ]

	LDR	r1,x_index_tab
	LDR	r1,[r1]
	LDR	r0,[r1,r0,LSL #2]
	LDR	r1,x_rules_data
	LDR	r1,[r1]
	ADD	r4,r1,r0	; p = rules_data + index_tab[c]

next_rule
	MOV	r6,r4		; p = next_p
	LDRB	r0,[r6]		; n_bytes = p[0]
	CMP	r0,#0
	BEQ	end_apply_rules
	ADD	r4,r6,r0	; next_p = p + n_bytes
	ADD	lr,r7,#1	; w = w1 + 1
	LDRB	r3,[r6,#1]	; consume = p[1]
	ADD	r6,r6,#2	; p += 2

; Match Forwards
loop_matchf
	LDRB	r0,[r6],#1	; c = *p++
	CMP	r0,#0
	BEQ	lmatchfOK	; end of match
	LDRB	r1,[lr],#1	; c2 = *w++
	CMP	r0,r1		; if (c1 == c2) continue;
	BEQ	loop_matchf
	CMP	r0,#7
	BGT	next_rule
;NOTE: could BGT end_rules if rules are ordered by forward match string.

lmatchfjump
	ADD	pc,pc,r0,LSL #2
	B	lmatchfOK
	B	lmatchfOK
	B	lmatchf1
	B	lmatchf2
	B	lmatchf3
	B	lmatchf4
	B	lmatchf5
	B	lmatchf6
	B	lmatchf7
	B	next_rule

lmatchfOK

;Match Backwards
	MOV	lr,r7		; w = w1
loop_matchb
	LDRB	r0,[r6],#1	; c = *p++
	CMP	r0,#0
	BEQ	lmatchbOK
	LDRB	r1,[lr,#-1]!		; c2 = *(--w)
	CMP	r0,r1		; if (c1 == c2) continue;
	BEQ	loop_matchb
	CMP	r0,#7
	BGT	next_rule

lmatchbjump
	ADD	pc,pc,r0,LSL #2
	B	lmatchbOK
	B	lmatchbOK
	B	lmatchb1
	B	lmatchb2
	B	lmatchb3
	B	lmatchb4
	B	lmatchb5
	B	lmatchb6
	B	lmatchb7
	B	next_rule
lmatchbOK


	;build new word
	ADD	lr,sp,#neww-start_vars2
	MOV	ip,lr

 [ TRACE = 1
	STR	r6,Trace1
 ]

	; memcpy(neww, word, ix)
	MOV	r1,#0
lcopyword1
	CMP	r1,r9
	BGE	lcopyword2
	LDRB	r0,[r5,r1]
	STRB	r0,[lr],#1
	ADD	r1,r1,#1
	B	lcopyword1

	; strcpy(neww+ix, replacement)
lcopyword2
	LDRB	r0,[r6],#1
	CMP	r0,#0
	BEQ	lcopyword3
	STRB	r0,[lr],#1
	B	lcopyword2

	; strcpy(neww+ix+advance, &word[ix+consume])
lcopyword3
	SUB	ip,lr,ip	;ip = ix+advance
	ADD	r1,r5,r9	;word+ix
	ADD	r1,r1,r3	;word+ix+consume
lcopyword4
	LDRB	r0,[r1],#1
	STRB	r0,[lr],#1
	CMP	r0,#0
	BNE	lcopyword4
	STR	ip,new_ix


 [ TRACE = 1
	STR	lr,word_end
	LDR	r0,x_f_trace
	LDR	r0,[r0]
	ADR	r1,xx_fmt2
	LDR	r2,Trace1
	ADD	r3,sp,#neww-start_vars2
	BL	fprintf
	LDR	lr,word_end
 ]

	; check for conditional rule or adjust score
; &80	bit 0 to 5 indicates condition
; &c0   bits 0 to 3   4 bit signed value to add to score

	MOV	r2,r8		; score
	LDRB	r0,[r6],#1
	CMP	r0,#&80
	BLT	lphoncond1	; not a conditional rule
	ANDS	r1,r0,#&40	; just adjust score ?
	BEQ	lconditional

	AND	r1,r0,#&f
	SUB	r1,r1,#8
	ADD	r2,r2,r1	; adjust score
lconditional2
	LDRB	r0,[r6],#1

lphoncond1
	LDR	r6,condition	; r6 was now free
	CMP	r0,#0
	BEQ	lphoncond2
	ADD	r6,r0,r6,LSL #16  ; condition2 = (condition2) << 16 + c
	ADD	r6,r6,r9,LSL #8	;	+ (ix << 8)
lphoncond2
	; pass the changed word on

; add 'e' here, [lr,#-1] is the zero at the end of neww
	ADD	r0,r2,#&20	; inc. depth count
	ANDS	r0,r0,#&300
	BNE	loutput2
	LDRB	r0,[lr,#-2]
	CMP	r0,#'e'
	BEQ	loutput2	; already ends in 'e'
	MOV	r0,#'e'
	STRB	r0,[lr,#-1]	; add 'e' to end
	MOV	r0,#0
	STRB	r0,[lr,#0]

	STR	lr,word_end

	; if lookup_dict(word) { add_word1(word,condition) }
	ADD	r1,sp,#neww-start_vars2
	MOV	r0,lr		; ptr to zero at end of word
	BL	lookup_dict2	; saves r2 - points
	CMP	r0,#0
	BNE	loutput6
loutput5
	LDR	lr,word_end
	MOV	r0,#0		; remove the 'e' again
	STRB	r0,[lr,#-1]


loutput2
	; if lookup_dict(word) { add_word1(word,condition) }
	ADD	r1,sp,#neww-start_vars2
	SUB	r0,lr,#1	; ptr to zero at end of word
	BL	lookup_dict2	; saves r2 - points
	CMP	r0,#0
	BNE	loutput4
loutput3
	ADD	r2,r2,#&20	; inc. depth count
	ANDS	r3,r2,#&300
	BNE	next_rule	;maximum depth


	ADD	r0,sp,#neww-start_vars2
	LDR	r1,new_ix	; ix+advance
	MOV	r3,r6		; condition2
	BL	apply_rules	; unless n reaches 0 (max. depth)
	B	next_rule

loutput4
	ADD	r0,sp,#neww-start_vars2
	MOV	r1,r6		; condition2
	STR	r2,points       ; 32BIT - was MOVNE, STRNE, BLNE, LDRNE
	BL	add_word1
	LDR	r2,points
	B	loutput3

loutput6
	ADD	r0,sp,#neww-start_vars2
	MOV	r1,r6		; condition2
	STR	r2,points	; 32BIT - was MOVNE, STRNE, BLNE, LDRNE
	BL	add_word1
	LDR	r2,points
	B	loutput5


lconditional
	LDR	r1,x_rule_options
	LDR	r1,[r1]
	ANDS	r0,r1,r0	; if ( rule_options & r0 ) bits 0 to 5
	BEQ	next_rule	; this rule is not required
	ADD	r2,r2,#3	; adjust score for this word  (1.5 levels)
	B	lconditional2


end_apply_rules
	LDMEA	fp, {r4-r9, fp,sp,pc}   ; procedure exit



; bit 0  consonant
; bit 1  hard consonant
; bit 3  vowel
ctype2
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 0 */
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 10 */
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 20 */
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 30 */
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 40 */
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0   ;/* 50 */
   	DCB	0,8,3,3,3,8,3,3,3,8,3,3,1,3,3,8
   	DCB	3,3,1,3,3,8,3,1,3,9,3,0,0,0,0,0

   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
   	DCB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0




lmatchf5
	ADR	r2,ctype2	; c == '@'
	LDRB	ip,[r2,r1]	; ctype2(c2)
	ANDS	ip,ip,#8	; is_vowel
	BNE	loop_matchf
	B	next_rule

lmatchf2			; c == '#'
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]	; ctype2(c2)
	ANDS	ip,ip,#1	; is_consonant
	BNE	loop_matchf
	B	next_rule

lmatchf3			; c == '%'
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#8	; !is_vowel
	BEQ	loop_matchf
	B	next_rule

lmatchf4			; c == ''
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#2	; is_hconsonant
	BNE	loop_matchf
	B	next_rule

lmatchf1			; c == '-'
	CMP	r1,#0		; if (c2 == 0) continue
	BEQ	loop_matchf
	CMP	r1,#'s'		; if (c2=='s') && (*w==0)
	BNE	next_rule	;      continue
	LDRB	ip,[lr]
	CMP	ip,#0
	BEQ	loop_matchf
	B	next_rule

lmatchf6			; c == '~'
	LDRB	ip,[r6],#1		; if (*p != c2)
	CMP	ip,r1
	BEQ	next_rule
	CMP	ip,#4		; ~  not hard consonant
	BNE	loop_matchf
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#2
	BNE	next_rule
	B	loop_matchf	; continue

lmatchf7			; c == '+'  any vowel after this point?
	ADD	r1,r9,#1	; ix+1
	ADD	r1,r5,r1	; &word[ix+1]
	ADR	r2,ctype2
lmatchf7a
	LDRB	r0,[r1],#1
	CMP	r0,#0
	BEQ	next_rule	; no vowel found
	LDRB	ip,[r2,r0]
	ANDS	ip,ip,#8
	BNE	loop_matchf	; found vowel
	B	next_rule






lmatchb5
	ADR	r2,ctype2	; c == '@'
	LDRB	ip,[r2,r1]	; ctype2(c2)
	ANDS	ip,ip,#8	; is_vowel
	BNE	loop_matchb
	B	next_rule

lmatchb2			; c == '#'
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]	; ctype2(c2)
	ANDS	ip,ip,#1	; is_consonant
	BNE	loop_matchb
	B	next_rule

lmatchb3			; c == '%'
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#8	; !is_vowel
	BEQ	loop_matchb
	B	next_rule

lmatchb4			; c == ''
	ADR	r2,ctype2
	LDRB	ip,[r2,r1]
	ANDS	ip,ip,#2	; is_hconsonant
	BNE	loop_matchb
	B	next_rule

lmatchb1			; c == '-'
	CMP	r1,#0		; if (c2 == 0) continue
	BEQ	loop_matchb
	B	next_rule

lmatchb6			; c == '~'
	LDRB	ip,[r6]		; if (*p != c2)
	CMP	ip,r1
	BEQ	next_rule
	ADD	r6,r6,#1	; p++
	B	loop_matchb	; continue

lmatchb7			; c == '+'  any vowel before this point?
	MOV	r1,#0
	ADR	r2,ctype2
lmatchb7a
	LDRB	r0,[r5,r1]
	LDRB	ip,[r2,r0]
	ANDS	ip,ip,#8
	BNE	loop_matchb	; found vowel
	ADD	r1,r1,#1
	CMP	r1,r9
	BLT	lmatchb7a
	B	next_rule




; r0	word
; r1	length

x2_endtab	DCD	endtab
x2_dict_addr	DCD	dict_addr
x2_hash_table	DCD	hash_table

              ^ 0,sp    ; variables relative to register
start_vars3	# 0
found_word	# 64	; word with wildcards replaced
xframe_size3	# 0

frame_size3    * xframe_size3 - start_vars3




crossword_lookup
	MOV	ip,sp		;set up a stack
	STMFD	sp!, {r4-r9, fp,ip,lr,pc}
	SUB	fp,ip,#4
	SUB	sp,sp,#frame_size3   ;reserve space for local variables

	CMPS	sp,sl
	BLLT    |__rt_stkovf_split_small|
;	BLLT	|x$stack_overflow|

	MOV	r9,#0		;hash table index
	MOV	r8,r0		;word

	;copy word to output buffer
	ADD	r7,sp,#found_word-start_vars3
	MOV	r1,r8
xw_0
	LDRB	r0,[r1],#1
	STRB	r0,[r7],#1
	CMP	r0,#0
	BNE	xw_0
	ADD	r7,sp,#found_word-start_vars3

xw_1
	LDR	r0,x2_hash_table
	LDR	r0,[r0]
	ADD	r0,r0,r9,LSL #2	;this hash list
	LDR	r6,[r0]
	LDR	r5,[r0,#4]	;next hash list
	LDR	r0,x2_dict_addr
	LDR	r0,[r0]
	ADD	r6,r6,r0
	ADD	r5,r5,r0
xw_2	;next entry in this hash list
	MOV	ip,#0		;index into word
	LDRB	r0,[r6],#1	;1st byte from dictionary
	CMP	r0,#35
	MOV	r3,#0
	MOVGE	r3,#1		;stem is valid
	ADDLT	r0,r0,#35		;now in range 35-69
	LDRB	r1,[r8,ip]
	ADD	r0,r0,#'a'-39	;now range 'a' to 'z'
	CMP	r1,#'?'
	STREQB	r0,[r7],ip	;replace wildcard
	BEQ	xw_3
	CMP	r1,r0
	BNE	xw_10		;not matched

xw_3
	ADD	ip,ip,#1
	LDRB	r1,[r8,ip]
	LDRB	r0,[r6],#1
	CMP	r0,#105
	BGE	xw_5		;end of stem, endings follow
	CMP	r0,#70
	BLT	xw_4		;end of stem, no endings follow

	SUB	r0,r0,#74-'a'	;to range 'a' to 'z'
	CMP	r1,#'?'
	STREQB	r0,[r7,ip]
	BEQ	xw_3
	CMP	r1,r0
	BNE	xw_10		;match failed
	B	xw_3

xw_4	;end of stem, no endings follow
xw_5	;end of stem, endings follow
	SUB	r6,r6,#1	;now ptr to 1st ending
	CMP	r1,#0		;end of word ?
	BNE	xw_6
	CMP	r3,#1
	BNE	xw_10		;stem is not a valid word
	; matched complete word on stem
	; add to word list
	MOV	r0,r7		;word
	MOV	r1,#0		;points
	BL	add_word0
	B	xw_10		;next stem

xw_6	;look through list of endings
	LDR	r4,x2_endtab
xw_7
	LDRB	r0,[r6],#1	;next ending in entry
	CMP	r0,#254
	BGE	xw_7
	SUBS	r0,r0,#105
	BLT	xw_11
	LDR	r3,[r4,r0,LSL #2]	;get ending string
	ADD	r2,r8,ip
xw_8
	LDRB	r1,[r2],#1
	LDRB	r0,[r3],#1
	CMP	r0,#0
	BEQ	xw_9
	CMP	r1,#'?'
	BNE	xw_9
	SUB	r1,r2,r8
	SUB	r1,r1,#1
	STRB	r0,[r7,r1]	;replace wildcard in output
	B	xw_8

xw_9
	CMP	r0,r1
	BNE	xw_7		;next ending
	CMP	r0,#0
	BNE	xw_8
	LDRB	r0,[r6]		;check next ending byte
	CMP	r0,#254
	BEQ	xw_7		;254 indictes not valid, just a marker to exceptions list
	;matched on ending
	MOV	r0,r7		;word
	MOV	r1,#0		;points
	BL	add_word0
	B	xw_10

xw_10	;match failed in stem, skip to end of this entry
	LDRB	r0,[r6],#1
	CMP	r0,#70
	BGE	xw_10
xw_11	;reached end of entry
	CMP	r6,r5
	SUB	r6,r6,#1
	BLT	xw_2		;next entry in thisd hash list
	ADD	r9,r9,#1	;next hash list
	CMP	r9,#&1000
	BLT	xw_1
	; completed, end if hash table reached, exit from function

	LDMEA	fp, {r4-r9, fp,sp,pc}   ; procedure exit


   END
