;+asm
;do
;*
;
; ### Finnslator.library by JM v 1.4 ###
;
; - Created 901024 by JM -
;
;
; Bugs: Doesn't split the input string at word boundary on buffer full
;       conditions.
;
;
; Edited:
;
; - 901024 by JM -> v0.01	- basics written (see sample.library.asm).
; - 901025 by JM -> v1.00	- it works now.  lots of room for
;				  improvements, though.
; - 901026 by JM -> v1.1	- now special commands are processed
;				  before alphabets.
; - 901029 by JM -> v1.2	- destination buffer full condition now
;				  detected.
;				- ` now acts as an escape character.
;				- returns error code in d0.
; - 901029 by JM -> v1.3	- compatibility with original Translate()
;				  improved.
; - 901029 by JM -> v1.4	- still some fixes.  de-alping.
;
;

VERSION	EQU	1
REVISION	EQU	4

SPEC		macro	* <orig_char>,<dest_string>
		dc.b	SPEC2\@-SPEC1\@
		dc.b	\1
SPEC1\@		dc.b	\2
SPEC2\@
		endm

		include	"exec/types.i"
		include	"exec/initializers.i"
		include	"exec/libraries.i"
		include	"exec/lists.i"
		include	"exec/alerts.i"
		include	"exec/resident.i"
		include	"libraries/dos.i"

		include	"finnslatorbase.i"

begin		moveq	#-1,d0			; if somebody runs us...
		rts

ROMTag		dc.w	RTC_MATCHWORD		; UWORD RT_MATCHWORD
		dc.l	ROMTag			; APTR  RT_MATCHTAG
		dc.l	EndOfLib		; APTR  RT_ENDSKIP
		dc.b	RTF_AUTOINIT		; UBYTE RT_FLAGS
		dc.b	VERSION			; UBYTE RT_VERSION
		dc.b	NT_LIBRARY		; UBYTE RT_TYPE
		dc.b	0			; BYTE  RT_PRI
		dc.l	LibName			; APTR  RT_NAME
		dc.l	LibId			; APTR  RT_IDSTRING
		dc.l	InitStuff		; APTR  RT_INIT

LibName		FINNSLATORNAME
LibId		dc.b	'finnslator 1.4 (29 Oct 1990)',13,10,0
		ds.w	0

InitStuff	dc.l	FinnslatorBase_SIZEOF	; size of library base
		dc.l	Functions		; pointer to function table
		dc.l	LibBaseData		; pointer to lib base data
		dc.l	InitRoutine		; ptr to initializing routine

Functions	dc.l	r_Open			; library Open call
		dc.l	r_Close			; library Close call
		dc.l	r_Expunge		; library Expunge call
		dc.l	r_Null			; reserved for Exec
		dc.l	r_Translate		; Translate call
		dc.l	-1			; end marker

LibBaseData	INITBYTE	LN_TYPE,NT_LIBRARY
		INITLONG	LN_NAME,LibName
		INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
		INITWORD	LIB_VERSION,VERSION
		INITWORD	LIB_REVISION,REVISION
		INITLONG	LIB_IDSTRING,LibId
		dc.l		0

InitRoutine	move.l	a5,-(sp)		; save a5
		move.l	d0,a5			; library base into a5
		move.l	a6,finsb_SysLib(a5)	; save ExecBase into LibBase
		move.l	a0,finsb_SegList(a5)	; save SegList pointer

; additional initializing would be done here

		move.l	a5,d0			; lib base back to d0
		move.l	(sp)+,a5		; restore a5
		rts

r_Open		addq.w	#1,LIB_OPENCNT(a6)	; one more user
		bclr	#LIBB_DELEXP,finsb_Flags(a6) ; so expunge impossible
		move.l	a6,d0			; return library base in d0
		rts

r_Close		moveq	#0,d0			; return value
		subq.w	#1,LIB_OPENCNT(a6)	; one user less
		bne.s	1$			; last user?
		btst	#LIBB_DELEXP,finsb_Flags(a6) ; delayed Expunge?
		beq.s	1$
		bsr	r_Expunge		; yes, do it
1$		rts	; if other users, just return

r_Expunge	movem.l	d2/a5-a6,-(sp)
		move.l	a6,a5			; library base pointer
		move.l	finsb_SysLib(a5),a6	; ExecBase
		tst.w	LIB_OPENCNT(a5)		; are we still open?
		beq.s	1$
		bset	#LIBB_DELEXP,finsb_Flags(a5) ; yes, delayed Expunge
		moveq	#0,d0			; and return a zero
		movem.l	(sp)+,d2/a5-a6
		rts
1$		move.l	finsb_SegList(a5),d2	; SegList* to d2
		move.l	a5,a1
		xref	_LVORemove		; remove from library list
		jsr	_LVORemove(a6)

; other cleanup operations would be done here

		moveq	#0,d0
		move.l	a5,a1			; library base
		move.w	LIB_NEGSIZE(a5),d0	; get size below the base
		sub.l	d0,a1			; get pointer to mem block
		add.w	LIB_POSSIZE(a5),d0	; add size after the base
		xref	_LVOFreeMem		; free memory used by lib
		jsr	_LVOFreeMem(a6)
		move.l	d2,d0			; return SegList pointer
		movem.l	(sp)+,d2/a5-a6
		rts

r_Null		moveq	#0,d0			; Exec reserved routine
		rts

******* Actual library routine(s) begin here: *******

; error = Translate(input,length,output,bufsiz);
;
; inputs:
;	a0 = input string (may be NULL terminated)
;	d0 = length of input string
;	a1 = output buffer
;	d1 = size of output buffer
; internal variables:
;	a0 = source pointer
;	a1 = destination pointer
;	d0 = characters left in source buffer
;	d1 = number of free bytes in destination buffer
;	d2 = temporary (character being processed)
;	d3 = temporary for table search
;	d4 = storage for input length
; outputs:
;	d0 = NULL (no error) or -N where N is index to first char in input
;	     buffer that was not processed
;			

r_Translate	movem.l	d2-d4/a2,-(sp)
		move.l	d0,d4		; length of input string
		subq.l	#1,d1		; space for NULL char
		ble.s	exitnow		; no space in buffer at all
		bra.s	loop
reploop		subq.l	#1,d0		; one char less
loop		tst.l	d0		; characters left?
		beq.s	endofit		; none -> exit
		moveq	#0,d2		; clear d2
		move.b	(a0)+,d2	; get one char
		beq.s	endofit		; NULL -> exit

		cmp.b	#96,d2		; is it a ` ?
		bne.s	10$
		subq.l	#1,d1
		bmi.s	endofit		; end of destination buffer
		subq.l	#1,d0
		beq.s	endofit		; end of source string
		move.b	(a0)+,(a1)+	; copy one char literally
		bra.s	reploop

10$		cmp.b	#'a',d2		; convert to UPPER CASE
		bcs.s	1$
		cmp.b	#'z',d2
		bhi.s	1$
		sub.b	#32,d2
1$		cmp.b	#224,d2		; convert special chars also
		bcs.s	2$
		sub.b	#32,d2
2$		bsr	dospecials
		tst.l	d1
		bmi.s	endofit
		cmp.b	#'A',d2		; is it an alphabet?
		bcs.s	reploop
		cmp.b	#'Z',d2
		bhi.s	reploop
		sub.b	#'A',d2		; yes -> A->0; Z->25
		lea	alphatable(pc),a2 ; convert into phonemes
		add.w	d2,a2
		add.w	d2,a2		; address in table
		subq.l	#1,d1
		bmi.s	endofit		; destination buffer overflow
		move.b	(a2)+,d2	; get 1 or 2 bytes from table
		beq.s	100$
		subq.l	#1,d1
		bmi.s	endofit		; destination buffer overflow
		move.b	d2,(a1)+
100$		move.b	(a2)+,(a1)+	; copy bytes into buffer
		subq.l	#1,d0		; one char processed
		bra.s	loop
endofit		clr.b	(a1)		; NULL terminate string
exitnow		tst.l	d0
		beq.s	101$
		sub.l	d4,d0		; -(idx to next char) or NULL
101$		movem.l	(sp)+,d2-d4/a2
		rts

dospecials	lea	othertable(pc),a2
		moveq	#0,d3
dospecloop	add.w	d3,a2		; skip to next entry
		move.b	(a2)+,d3	; get size of entry
		beq.s	dosendeth	; NULL = end of table
		cmp.b	(a2)+,d2	; compare with this entry
		bne.s	dospecloop	; if not equal, try next
		sub.l	d3,d1		; right entry found:
		bge.s	dospecbufok	; enough room in buffer?
		moveq	#-1,d1		; nope -> d1=-1; return
		rts
dospeccopy	move.b	(a2)+,(a1)+	; copy string
dospecbufok	dbf	d3,dospeccopy
		moveq	#0,d2		; return NULL
dosendeth	rts

alphatable	dc.w	'AH','B','S','D','EH','F','G'
		dc.w	'/H','IH','Y','K','L','M','N'
		dc.w	'OH','P','KV','R','S','T','UH'
		dc.w	'V','W','KS','IX','TS'

othertable	SPEC	32,32	; SPC
		SPEC	13,32	; CR -> SPC
		SPEC	'.','.'
		SPEC	44,44	; ,
		SPEC	'?','?'
		SPEC	'(','('
		SPEC	')',')'
		SPEC	'-','-'
		SPEC	'I','IHIH'	; I
		SPEC	196,'AE'
		SPEC	214,'ER'
		SPEC	197,'OH'
		SPEC	209,'NX'	; äng
		SPEC	182,'PIHIHIHIHAH'
		SPEC	'*','TAE/HTIH'
		SPEC	'0','NOHLLAH'
		SPEC	'1','IXKSIH'
		SPEC	'2','KAHKSIH'
		SPEC	'3','KOHLMEH'
		SPEC	'4','NEHLYAE'
		SPEC	'5','VIHIHIHIHSIH'
		SPEC	'6','KUHUHSIH'
		SPEC	'7','SEHIHTSEHMAEN'
		SPEC	'8','KAH/HDEHKSAHN'
		SPEC	'9','IXIX/HDEHKSAEN'
		dc.b	0
		ds.w	0

***********************************************************************

EndOfLib	end

