	INCLUDE	AINCLUDE:IncDirs.i *sets all includedirs, needed for my ASM
	INCLUDE	IDEA/xpkLibIDEA.i
	include	xpk/xpk.i
	include	xpk/xpksub.i

XPKIDEA_MAXSTATES	equ	25	;umpf. 400 bytes of state mem

; --- The IdeaStream structure
; the following struct gets allocated at the first call to Pack/UnpackChunk
; it is filled with proper values and then reused for subsequent calls.
; NO CHECKING FOR MODE/PASSWORD CHANGES IS DONE IF THE POINTER IS
; PRESENT IN SUB[0] !!!!!! (a speedup compromise)

	STRUCTURE	IdeaStream,0
	ULONG		ids_Mode
	ULONG		ids_Routine	;points to rout. to run
	ULONG		ids_NumStates	;nr. of states
	STRUCT		ids_Key,104	;the expanded (i)ideakey
	STRUCT		ids_States,8*XPKIDEA_MAXSTATES	;states for ofb mode
	STRUCT		ids_Inits,8*XPKIDEA_MAXSTATES	;init states for chaining modes
	LABEL		ids_SIZEOF

; --- The IdeaChunkHeader structure
; which precedes every hunk and contains info about the pack mode
; the checksum and the orig. len of the chunk

	STRUCTURE	IdeaChunkHeader,0
	ULONG		ich_Mode
	ULONG		ich_OrigLen
	UWORD		ich_CheckSum
	LABEL		ich_SIZEOF

; --- IDEA stuff ---------


;;; 68000 assembler functions from IDEAs crypt.c
;;; Implemented by A.B.P.Soft

;;; Idea and Mul algorithm speed up, unrolling and permutation
;;; by Colin Plumb. Thats the fastest plain 68000 version I can
;;; imagine. The former one (by me, ABP) was around 10% slower.
;;; Therefore the routines Idea and the macros Mult* are
;;; (C) Colin Plumb (colin@eecg.toronto.edu)

nofKeyPerRound	equ	6 ; number of used keys per round
nofRound	equ	8 ; number of rounds

Ones		equ	$ffff
MulMod		equ	$10001

; Multiply two numbers in the range of 1..0x10000, modulo 0x10001.
; 0x10000 is represented as 0.

Mult	MACRO	; src1, src2, dest, neither source is 0
	; dest may equal src2, but not src1 (which is zeroed)
	mulu	\2,\1	; 54
	move.w	\1,\3	;  4
	swap	\1	;  4 \1 now holds high part
	sub.w	\1,\3	;  4
	moveq	#0,\1	;  4 does not affect X flag
	addx.w	\1,\3	;  4

	ENDM		; 74

; Code to handle the cases where one argument is 0 = 0x10000 = -1.
; Negate the other modulo 0x10001, giving 0x10001 - x = 1 - x.
; In-place and out-of-place versions

Invert1	MACRO	; dest
	neg.w	\1	;  4
	addq.w	#1,\1	;  4
	ENDM		;  8

Invert2	MACRO	; src, dest
	moveq	#1,\2	;  4
	sub.w	\1,\2	;  4
	ENDM		;  8
	
; encryption and decryption algorithm IDEA

; In:
; a0 *dataIn
; a1 *dataOut
; a2 *Key

; no registers are saved !
; d0-d3 return the 64 bit result, too.
; only a3-a7 survive this routine.
; this is done for speedup.
; only save those registers in inner loops you need there !

; Multiply value in register by next available key (pointed at by a2).
; Requires corresponding MultAux code nearby.

; Using both halves of d6 (for dbra and constant 0) is particualrly
; obscene.  But I need x0, x1, x2, x3, t0, t1, a working data register,
; and a loop counter, and saving a clear instruction per multiply (4 in the
; inner loop makes for 16 cycles) pays for the extra swap instructions.

; Unrolling this loop would eliminate 10 cycles of dbra, two swaps, and
; two exg's, for a total of 30 cycles per two iterations, or 120 cycles
; total.  Is it worth blowing it out of an '020's cache?

KeyMult MACRO	; \1 = number, \2 = register, \3 = zero register
	; flags must be set to correspond to register
	beq.s	Mul\1_a_is_0	;  8
	move.w	(a2)+,d7	;  8
	beq.s	Mul\1_b_is_0	;  8
	mulu	\2,d7		; 54 (average of 8 bits set)
	move.w	d7,\2		;  4
	swap	d7		;  4 \1 now holds high part
	sub.w	d7,\2		;  4
	addx.w	\3,\2		;  4
Mul\1_done:
	ENDM			; 94 total

MultAux	MACRO	; \1 = number, \2 = register
Mul\1_a_is_0:			; 10 branch overhead
	moveq	#1,\2	;  4
	sub.w	(a2)+,\2	;  4
	bra.s	Mul\1_done	; 10 = 32 total for this path

Mul\1_b_is_0:			; 26 test & branch overhead
	neg.w	\2	;  4
	addq.w	#1,\2	;  4
	bra.s	Mul\1_done	; 10 = 44 total for this path
	ENDM

	MultAux	1,d0
	MultAux	2,d3
	MultAux	3,d4
	MultAux	4,d5

Idea:

	moveq	#nofRound-1,d6	;  4

	moveq	#0,d4		;  4

	move.w	(a0)+,d0	;  8
	KeyMult	1,d0,d4		; 94

	move.w	(a0)+,d2	;  8
	move.w	(a0)+,d1	;  8
	move.w	(a0)+,d3	;  8

CryptLoop:
	swap	d6		;  4

	exg	d1,d2		;  6   swap x1 and x2
	add.w	(a2)+,d1	;  8   x1 += *key++;
	add.w	(a2)+,d2	;  8   x2 += *key++;

	eor.w	d4,d3		;  4   x3 ^= t0
	KeyMult	2,d3,d6		; 94   x3 = Mul(x3, *key++);

	move.w	d0,d4	 	;  4   t0 = Mul(x0^x2, *Key++);
	eor.w	d2,d4		;  4
	KeyMult	3,d4,d6		; 94

	move.w	d1,d5		;  4   t1 = Mul(t0 + (x1^x3), *Key++);
	eor.w	d3,d5		;  4
	add.w	d4,d5		;  4
	KeyMult	4,d5,d6		; 94

	add.w	d5,d4		;  4   t0 += t1

	eor.w	d5,d0		;  4   x0 ^= t1;

	KeyMult	5,d0,d6		; 94   x0 = Mul(x0, *Key++);

	eor.w	d4,d1		;  4   x1 = x1 ^ t0;
	eor.w	d5,d2		;  4   x2 = x2 ^ t1;

	swap	d6		;  4 
	dbra	d6,CryptLoop	; 10
				;  4  -> From top, if expired

	add.w	(a2)+,d1	;  8   x1 += *key++
	add.w	(a2)+,d2	;  8   x2 += *key++

	swap	d6		;  4
	eor.w	d4,d3		;  4   x3 ^= t0
	KeyMult	6,d3,d6		; 94   x3 = Mul(x3, *Key++);

	movem.w	d0-d3,(a1)	; 24
	lea	8(a1),a1 

	rts			; 16

; End of Idea

	MultAux	5,d0
	MultAux	6,d3
IdeaEnd:


; 32 bit multiplier macro

CMUL	MACRO
	move.l	\2,-(a7)
	move.l	\1,-(a7)
	swap	\2
	mulu	\1,\2
	swap	\1
	mulu	2(a7),\1
	add.l	\1,\2
	swap	\2
	clr.w	\2
	move.w	2(a7),\1
	mulu	6(a7),\1
	add.l	\1,\2
	addq.l	#8,a7
	ENDM


;The following isn't of interest for the resulting speed. Its only called once

; MulInv routine
; compute inverse of x via euclidian gcd algorithm
; in:
; d0.w = x
; out:
; d0.w = MulInv(x)

MulInv:
	and.l	#Ones,d0
	bne	mulinv1
	rts
mulinv1:
	cmp.w	#1,d0
	bne	mulinv11
	rts
mulinv11:
	move.l	#MulMod,d1
	moveq	#0,d4
	moveq	#1,d5
mulinv12:
	move.l	d1,d2
	divu	d0,d2
	move.l	d2,d3
	swap	d3
	and.l	#Ones,d2
	and.l	#Ones,d3
	bne	mulinv2
	tst.l	d5
	bge	mulinv3
	add.l	#MulMod,d5
	bra	mulinv3
mulinv2:
	move.l	d0,d1
	move.l	d3,d0
	move.l	d5,d6

	CMUL	d2,d5

	sub.l	d5,d4

	move.l	d4,d5
	move.l	d6,d4
mulinv3:
	tst.w	d3
	bne	mulinv12
	move.l	d5,d0
	and.l	#Ones,d0
	rts


; ExpandUserKey routine
; makes 832 bit idea key from 128 bit input key
; in:
; a0 *UserKey
; a1 *Key		;104 bytes buffer
; out:
; --			;result in mem

ExpandUserKey:
	move.l	a1,a2
	move.l	(a0)+,(a2)+
	move.l	(a0)+,(a2)+
	move.l	(a0)+,(a2)+
	move.l	(a0)+,(a2)+
	moveq	#nofRound,d0
ex1:
	move.l	d0,d1
	and.b	#%111,d1
	cmp.b	#6,d1
	bne	ex2

	move.w	d0,d1
	add.w	d1,d1
	move.w	-14(a1,d1.w),d2
	and.w	#$7f,d2
	lsl.w	#5,d2
	lsl.w	#4,d2	;have no 9<<
	move.w	-28(a1,d1.w),d3
	lsr.w	#7,d3
	eor.w	d3,d2
	move.w	d2,0(a1,d1.w)

	bra	exend
ex2:
	cmp.b	#7,d1
	bne	ex3


	move.w	d0,d1
	add.w	d1,d1
	move.w	-30(a1,d1.w),d2
	and.w	#$7f,d2
	lsl.w	#5,d2
	lsl.w	#4,d2	;have no 9<<
	move.w	-28(a1,d1.w),d3
	lsr.w	#7,d3
	eor.w	d3,d2
	move.w	d2,0(a1,d1.w)

	bra	exend
ex3:
	move.w	d0,d1
	add.w	d1,d1
	move.w	-14(a1,d1.w),d2
	and.w	#$7f,d2
	lsl.w	#5,d2
	lsl.w	#4,d2	;have no 9<<
	move.w	-12(a1,d1.w),d3
	lsr.w	#7,d3
	eor.w	d3,d2
	move.w	d2,0(a1,d1.w)
exend:
	addq	#1,d0
	cmp.b	#52,d0
	blt	ex1
	rts


; InvertIdeaKey routine
; makes decryption key from encryption key
; in:
; a0: *ideakey
; a1: *iideakey
; out:
; -- ; in mem

InvertIdeaKey:
	move.w	(a0)+,d0
	bsr	MulInv
	move.w	d0,96(a1)
	move.w	(a0)+,d0
	neg.w	d0
	move.w	d0,98(a1)
	move.w	(a0)+,d0
	neg.w	d0
	move.w	d0,100(a1)
	move.w	(a0)+,d0
	bsr	MulInv
	move.w	d0,102(a1)
	moveq	#42,d1
invloop:
	move.l	d1,d2
	add.w	d2,d2
	move.w	(a0)+,8(a1,d2.w)
	move.w	(a0)+,10(a1,d2.w)
	move.w	(a0)+,d0
	movem.l	d1-d2,-(a7)
	bsr	MulInv
	movem.l	(a7)+,d1-d2
	move.w	d0,0(a1,d2.w)
	move.w	(a0)+,d3
	neg.w	d3
	move.w	(a0)+,d4
	neg.w	d4
	tst.w	d1
	beq	ikey1
	exg	d3,d4
ikey1:
	move.w	d3,2(a1,d2.w)
	move.w	d4,4(a1,d2.w)
	move.w	(a0)+,d0
	movem.l	d1-d2,-(a7)
	bsr	MulInv
	movem.l	(a7)+,d1-d2
	move.w	d0,6(a1,d2.w)
	subq	#6,d1
	bge	invloop
	rts

; --- Idea support for xpk routines

; the following does a InvertIdeaKey a0->a1, where both may point to the
; same memory. InvertIdeaKey would produce bogus, if used this way.

SaveInvert:
	move.l	a2,-(a7)
	move.l	a1,a2		;dst buf
	lea	-104(a7),a7
	move.l	a7,a1		;tmp. dst in stack
	bsr	InvertIdeaKey
	move.l	a7,a0
	moveq	#51,d0
cpkey:
	move.w	(a0)+,(a2)+
	dbf	d0,cpkey
	lea	104(a7),a7
	move.l	(a7)+,a2
	rts

; Initializes are called from Pack/Unpack to do the following:
;  - alloc one IdeaStream struct and put it to params.Sub[0]
;  - choose the mode to use and enter the Routine in struct
;  - parse the password and fill in the key/initializers in struct

PackInit:

	movem.l	a0-a5/d1-d7,-(a7)
	move.l	a6,a5	;lib base
	move.l	a0,a4	;xparams

	move.l	4.W,a6
	move.l	#ids_SIZEOF,d0
	move.l	#$10001,d1
	JSR	_LVOAllocMem(A6)
	move.l	a5,a6				;restore libbase
	move.l	d0,a3
	move.l	d0,xsp_Sub(a4)
	bne	allocok
	move.l	#XPKERR_NOMEM,d0
	bra	endinit
allocok:
	move.l	xsp_Mode(a4),d0
	move.l	d0,ids_Mode(a3)
	moveq	#25,d1
	sub.l	d1,d0
	ble	initecb
	cmp.l	d1,d0
	ble	initcfb
	sub.l	d1,d0
	cmp.l	d1,d0
	ble	initofb
	sub.l	d1,d0
	move.l	d0,ids_NumStates(a3)
;init cbc
	lea	CBCRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	bra	buildkey
initcfb:
	move.l	d0,ids_NumStates(a3)
	lea	CFBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	bra	buildkey
initofb:
	move.l	d0,ids_NumStates(a3)
	lea	OFBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	bra	buildkey
initecb:
	clr.l	ids_NumStates(a3)	;to be clean here
	lea	ECBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
buildkey:
	move.l	a4,a0	;params
	move.l	a3,a1	;ideastream
	bsr	ParsePassword	; may ret. error state		
endinit:
	movem.l	(a7)+,a0-a5/d1-d7
	rts

UnpackInit:
	movem.l	a0-a5/d1-d7,-(a7)
	move.l	a6,a5	;lib base
	move.l	a0,a4	;xparams

	move.l	4.W,a6
	move.l	#ids_SIZEOF,d0
	move.l	#$10001,d1
	JSR	_LVOAllocMem(A6)
	move.l	a5,a6				;restore libbase
	move.l	d0,a3
	move.l	d0,xsp_Sub(a4)
	bne	uallocok
	move.l	#XPKERR_NOMEM,d0
	bra	uiniterr
uallocok:
	move.l	xsp_InBuf(a4),a0
	move.l	ich_Mode(a0),d0
	move.l	d0,xsp_Mode(a4)
	move.l	d0,ids_Mode(a3)

	moveq	#25,d1
	sub.l	d1,d0
	ble	uinitecb
	cmp.l	d1,d0
	ble	uinitcfb
	sub.l	d1,d0
	cmp.l	d1,d0
	ble	uinitofb
	sub.l	d1,d0
	move.l	d0,ids_NumStates(a3)
;init cbc
	lea	UCBCRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	moveq	#-1,d7			;need inv key
	bra	ubuildkey
uinitcfb:
	move.l	d0,ids_NumStates(a3)
	lea	UCFBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	moveq	#0,d7			;no inv
	bra	ubuildkey
uinitofb:
	move.l	d0,ids_NumStates(a3)
	lea	UOFBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	moveq	#0,d7			;no inv
	bra	ubuildkey
uinitecb:
	clr.l	ids_NumStates(a3)
	lea	UECBRoutine(pc),a0
	move.l	a0,ids_Routine(a3)
	moveq	#-1,d7			;inv
ubuildkey:
	move.l	a4,a0	;params
	move.l	a3,a1	;ideastream
	bsr	ParsePassword	; may ret. error state
	tst.l	d0
	bne	uiniterr		
uendinit:
	tst.l	d7
	beq	unokeyinversion
	move.l	xsp_Sub(a4),a0
	lea	ids_Key(a0),a0
	move.l	a0,a1
	bsr	SaveInvert
unokeyinversion:
	moveq	#0,d0
uiniterr:
	movem.l	(a7)+,a0-a5/d1-d7
	rts

;
; <password>	::= 	<keyspec>[<initializer>]*.
; <keyspec>	::=	<valuespec>.
; <initializer>	::=	":"<valuespec>.
; <valuespec>	::=	[<charstring>|<hexstring>].
; <charstring>	::=	["!".."~"]*.			;without ":"
; <hexstring>	::=	"#"["0".."9"|"a".."f"|"A".."F"]*.
;


PW_ERROR	equ	-1
PW_EOL		equ	-2
PW_COLON	equ	-3

ParsePassword:
	;a0 = parms a1 = idstream
	movem.l	d1-d7/a2-a5,-(a7)
	lea	-16(a7),a7
	move.l	xsp_Password(a0),d0
	beq	pmisspwd
	move.l	d0,a2
	move.l	a7,a3
	bsr	parsekey
	cmp.b	#PW_COLON,d0
	beq	parseinits
	cmp.b	#PW_EOL,d0
	beq	ppwdok
	bra	pbadpwd			;error during key parse
parseinits:
	lea	ids_Inits(a1),a3	;curr. init val.
	moveq	#XPKIDEA_MAXSTATES,d7	;max initilizers
parseinitloop:
	subq	#1,d7
	blt	pbadpwd
	bsr	parseinitial
	cmp.b	#PW_COLON,d0
	beq	parseinitloop
	cmp.b	#PW_EOL,d0
	bne	pbadpwd
ppwdok:
	lea	ids_Key(a1),a1
	move.l	a7,a0
	bsr	ExpandUserKey
	moveq	#0,d0
	bra	endppw
pbadpwd:
	moveq	#XPKERR_WRONGPW,d0
	bra	endppw
pmisspwd:
	moveq	#XPKERR_NEEDPASSWD,d0
endppw:
	lea	16(a7),a7
	movem.l	(a7)+,d1-d7/a2-a5
	rts

parsekey:
	;a0 = parms a1 = ids a2 = char a3 = dest. buf
	moveq	#0,d0
	move.b	(a2)+,d0
	beq	pkeyeol
	cmp.b	#':',d0
	beq	pkeycolon
	cmp.b	#'#',d0
	beq	phexkey
	lea	-1(a2),a2
	bra	pasciikey
pkeyeol:
	moveq	#PW_EOL,d0
	rts
pkeycolon:
	clr.l	(a3)+
	clr.l	(a3)+
	clr.l	(a3)+
	clr.l	(a3)+
	moveq	#PW_COLON,d0
	rts

phexkey:
	moveq	#0,d0
	move.l	d0,d1
	move.l	d0,d2
	move.l	d0,d3
phexkeyloop:
	moveq	#0,d4
	move.b	(a2)+,d4
	beq	phexkeynohex
	cmp.b	#':',d4
	beq	phexkeynohex
	cmp.b	#'0',d4
	blt	phexkeynohex
	cmp.b	#'9',d4
	bgt	phexkeynodigit
	sub.b	#'0',d4
	bra	phexkeynextdigit
phexkeynodigit:
	and.b	#%11011111,d4
	cmp.b	#'A',d4
	blt	phexkeynohex
	cmp.b	#'F',d4
	bgt	phexkeynohex
	sub.b	#$37,d4
phexkeynextdigit:
;shift it into d0-d3

	lsl.l	#1,d3
	bcs	phexkeyerr	;any bit shift off ?
	lsl.l	#1,d3
	bcs	phexkeyerr
	lsl.l	#1,d3
	bcs	phexkeyerr
	lsl.l	#1,d3
	bcs	phexkeyerr
	rol.l	#4,d2
	move.l	d2,d5
	and.l	#$F,d5
	or.l	d5,d3
	rol.l	#4,d1
	move.l	d1,d5
	and.l	#$F,d5
	or.l	d5,d2
	rol.l	#4,d0
	move.l	d0,d5
	and.l	#$F,d5
	or.l	d5,d1

	or.l	d4,d0	;the new hex digit
	bra	phexkeyloop

phexkeynohex:
	move.l	d3,(a3)+
	move.l	d2,(a3)+
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	tst.b	d4
	bne	phexkeynoend
	moveq	#PW_EOL,d0
	rts
phexkeynoend:
	cmp.b	#':',d4
	bne	phexkeyerr
	moveq	#PW_COLON,d0
	rts
phexkeyerr:
	moveq	#PW_ERROR,d0
	rts

pasciikey:
	moveq	#0,d0
	move.l	d0,d1
	move.l	d0,d2
	move.l	d0,d3
pasckeyloop:
	moveq	#0,d4
	move.b	(a2)+,d4
	beq	pasckeyend
	cmp.b	#':',d4
	beq	pasckeycolon
	cmp.b	#'!',d4
	blt	pasckeyerr
	cmp.b	#'~',d4
	bgt	pasckeyerr
	sub.b	#'!',d4

	move.l	d4,d5

SMUL	MACRO	;src,dst,tmpreg		;simple mul with some output
	move.l	\2,\3			;for the hash function
	swap	\3
	mulu	\1,\3
	swap	\3
	clr.w	\3
	mulu	\1,\2
	add.l	\3,\2
	ENDM

	SMUL	#94,d3,d6
	add.l	d5,d3
	move.l	d3,d5
	swap	d5
	and.l	#Ones,d5

	SMUL	#94,d2,d6
	add.l	d5,d2
	move.l	d2,d5
	swap	d5
	and.l	#Ones,d5

	SMUL	#94,d1,d6
	add.l	d5,d1
	move.l	d1,d5
	swap	d5
	and.l	#Ones,d5

	SMUL	#94,d0,d6
	add.l	d5,d0

	bra	pasckeyloop

pasckeyend:
	move.l	d3,(a3)+
	move.l	d2,(a3)+
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	moveq	#PW_EOL,d0
	rts
pasckeycolon:
	move.l	d3,(a3)+
	move.l	d2,(a3)+
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	moveq	#PW_COLON,d0
	rts
pasckeyerr:
	moveq	#PW_ERROR,d0
	rts


parseinitial:
	;a0 = parms a1 = ids a2 = char a3 = dest. buf
	moveq	#0,d0
	move.b	(a2)+,d0
	beq	pinieol
	cmp.b	#':',d0
	beq	pinicolon
	cmp.b	#'#',d0
	beq	phexini
	lea	-1(a2),a2
	bra	pasciiini
pinieol:
	moveq	#PW_EOL,d0
	rts
pinicolon:
	clr.l	(a3)+
	clr.l	(a3)+
	moveq	#PW_COLON,d0
	rts

phexini:
	moveq	#0,d0
	move.l	d0,d1
phexiniloop:
	moveq	#0,d4
	move.b	(a2)+,d4
	beq	phexininohex
	cmp.b	#':',d4
	beq	phexininohex
	cmp.b	#'0',d4
	blt	phexininohex
	cmp.b	#'9',d4
	bgt	phexininodigit
	sub.b	#'0',d4
	bra	phexininextdigit
phexininodigit:
	and.b	#%11011111,d4
	cmp.b	#'A',d4
	blt	phexininohex
	cmp.b	#'F',d4
	bgt	phexininohex
	sub.b	#$37,d4
phexininextdigit:

	lsl.l	#1,d1
	bcs	phexinierr
	lsl.l	#1,d1
	bcs	phexinierr
	lsl.l	#1,d1
	bcs	phexinierr
	lsl.l	#1,d1
	bcs	phexinierr
	rol.l	#4,d0
	move.l	d0,d5
	and.l	#$F,d5
	or.l	d5,d1

	or.l	d4,d0	;the new hex digit
	bra	phexiniloop

phexininohex:
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	tst.b	d4
	bne	phexininoend
	moveq	#PW_EOL,d0
	rts
phexininoend:
	cmp.b	#':',d4
	bne	phexinierr
	moveq	#PW_COLON,d0
	rts
phexinierr:
	moveq	#PW_ERROR,d0
	rts

pasciiini:
	moveq	#0,d0
	move.l	d0,d1
pasciniloop:
	moveq	#0,d4
	move.b	(a2)+,d4
	beq	pasciniend
	cmp.b	#':',d4
	beq	pascinicolon
	cmp.b	#'!',d4
	blt	pascinierr
	cmp.b	#'~',d4
	bgt	pascinierr
	sub.b	#'!',d4

	move.l	d4,d5

	SMUL	#94,d1,d6
	add.l	d5,d1
	move.l	d1,d5
	swap	d5
	and.l	#Ones,d5

	SMUL	#94,d0,d6
	add.l	d5,d0

	bra	pasciniloop

pasciniend:
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	moveq	#PW_EOL,d0
	rts
pascinicolon:
	move.l	d1,(a3)+
	move.l	d0,(a3)+
	moveq	#PW_COLON,d0
	rts
pascinierr:
	moveq	#PW_ERROR,d0
	rts


;the ecb packer routine
;this is heavily commented for the interested
;the others are more cryptic (this IS a crypt prg, you know)
ECBRoutine:
	clr.l	-(a7)		;instead of bytebybyte clearing the stack
	clr.l	-(a7)		;we simply push 8 zero bytes.
	move.l	a0,a4		;xparms in a4
	move.l	xsp_Sub(a4),a3	;our idea stream structure in a3
	lea	ids_Key(a3),a5	;a5 points to key
	move.l	xsp_InBuf(a4),a0	;inbuf to a0
	move.l	xsp_OutBuf(a4),a1	;outbuf to a1

	move.l	xsp_InLen(a4),d0	;get inlen
	move.l	d0,ich_OrigLen(a1)	;store in chunk header
	move.l	xsp_Mode(a4),ich_Mode(a1)	;dito the mode
	lea	ich_SIZEOF(a1),a1	;place a0 behind the header
	move.l	d0,d1
	lsr.l	#3,d0		;len/8 -> num of blocks to code
	move.l	d0,d4		;d4 is the block counter
	and.l	#7,d1		;len MOD 8 -> num of exceeding bytes
	move.l	d1,d6		;d6 holds them
	beq	noextra		;if there are some of them
	addq	#1,d0		;we will write one more block
noextra:
	lsl.l	#3,d0		;num blks -> num bytes
	add.l	#ich_SIZEOF,d0	;outlen = header size + bytes crypted
	move.l	d0,xsp_OutLen(a4)
	moveq	#0,d7		; init chksum
	move.l	d6,-(a7)	; save len exceed to stack
ecbloop:
	add.w	(a0),d7		;we
	add.w	2(a0),d7	;build
	add.w	4(a0),d7	;the
	add.w	6(a0),d7	;checksum
	movem.l	d4/d7,-(a7)	;remember Idea crashs the regs
	move.l	a5,a2		;key pointer for idea
	bsr	Idea		;do what we have to do ;>
	movem.l	(a7)+,d4/d7
	subq	#1,d4		;one less block
	bne	ecbloop		;wasn't the last

	move.l	(a7)+,d6	;restore len exceed
	beq	ecbnoexceed	;was noone

	moveq	#1,d4		; one additional loop to run
	move.l	a7,a2		; stack as temp buffer
ecbcpex:
	move.b	(a0)+,(a2)+	; copy the bytes exceeding
	subq.b	#1,d6		; into the already cleared stack
	bne	ecbcpex		; (remember the 2 clr's above)
	move.l	a7,a0		; stack as crypt input
	clr.l	-(a7)		; no len exceed anymore
	bra	ecbloop		; let's crypt it ...

ecbnoexceed:
	move.l	xsp_OutBuf(a4),a1
	move.w	d7,ich_CheckSum(a1)	; store checksum to header
	moveq	#0,d0		; was no error
	lea	8(a7),a7	; untrash stack
	rts			; and went otherwards

UECBRoutine:

	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1
	move.l	ich_OrigLen(a0),d0	;simply write the orig len
	move.l	d0,xsp_OutLen(a4)	;into the xparm.OutLen
	move.l	xsp_InLen(a4),d0
	lea	ich_SIZEOF(a0),a0	;jump over header
	sub.l	#ich_SIZEOF,d0		;get crypted data size
	lsr.l	#3,d0			;-->blocks
	move.l	d0,d4
	moveq	#0,d7		;init chksum
uecbloop:
	movem.l	d4/d7,-(a7)
	move.l	a5,a2		;key
	bsr	Idea
	movem.l	(a7)+,d4/d7
	add.w	-8(a1),d7		;chksum
	add.w	-6(a1),d7		;build
	add.w	-4(a1),d7
	add.w	-2(a1),d7
	subq	#1,d4
	bne	uecbloop
	neg.w	d7			;chksum + -(chksum)
	move.l	xsp_InBuf(a4),a0	;should be Zero
	add.w	ich_CheckSum(a0),d7	;or something
	bne	uecbbadpwd		;went wrong
	moveq	#0,d0
	rts
uecbbadpwd:
	moveq	#XPKERR_WRONGPW,d0	;its at least a wrong chksum
	rts				;but Zop rulez. Its his XPK.

CBCRoutine:
	clr.l	-(a7)
	clr.l	-(a7)
	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a3
	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	xsp_InLen(a4),d0
	move.l	d0,ich_OrigLen(a1)
	move.l	xsp_Mode(a4),ich_Mode(a1)
	lea	ich_SIZEOF(a1),a1

	move.l	d0,d1
	lsr.l	#3,d0
	move.l	d0,d4
	and.l	#7,d1
	move.l	d1,d6
	beq	cbcnoextra
	addq	#1,d0
cbcnoextra:
	lsl.l	#3,d0
	add.l	#ich_SIZEOF,d0
	move.l	d0,xsp_OutLen(a4)
	moveq	#0,d7
	move.l	d6,-(a7)
cbcloop1:

	move.w	(a3)+,(a1)	;whats that ????
	move.w	(a0)+,d0	;oh yeah...
	add.w	d0,d7		;do 2 things in one...
	eor.w	d0,(a1)+	;xor the Input with the Output(i-N)
	move.w	(a3)+,(a1)	;and build up the chksum
	move.w	(a0)+,d0
	add.w	d0,d7		;even if this looks somewhat
	eor.w	d0,(a1)+	;huge, no C compiler
	move.w	(a3)+,(a1)	;would get it so small...
	move.w	(a0)+,d0
	add.w	d0,d7		;maybe nobody gets it smaller.
	eor.w	d0,(a1)+
	move.w	(a3)+,(a1)
	move.w	(a0)+,d0
	add.w	d0,d7
	eor.w	d0,(a1)+

	lea	-8(a1),a1	;stueck zurueck @;-)

	movem.l	a0/d4-d5/d7,-(a7)
	move.l	a5,a2		;key
	move.l	a1,a0
	bsr	Idea
	movem.l	(a7)+,a0/d4-d5/d7
	subq	#1,d4
	beq	cbcend
	tst.l	d5
	beq	cbcloop1
	subq	#1,d5		;if N blocks are coded
	bne	cbcloop1
	move.l	xsp_OutBuf(a4),a3	;switch from initializers
	lea	ich_SIZEOF(a3),a3	;to the real output
	bra	cbcloop1
cbcend:
	move.l	(a7)+,d6	;len exceed
	tst.b	d6
	beq	cbcnoexceed

	moveq	#1,d4
	move.l	a7,a2
cbccpex:
	move.b	(a0)+,(a2)+	; copy the bytes exceeding
	subq.b	#1,d6
	bne	cbccpex
	move.l	a7,a0		; stack as input
	clr.l	-(a7)		; no len exceed anymore
	bra	cbcloop1	; crypt it ...

cbcnoexceed:

	move.l	xsp_OutBuf(a4),a1
	move.w	d7,ich_CheckSum(a1)
	moveq	#0,d0
	lea	8(a7),a7
	rts

UCBCRoutine:

	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a3
	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	ich_OrigLen(a0),d0
	move.l	d0,xsp_OutLen(a4)
	move.l	xsp_InLen(a4),d0
	lea	ich_SIZEOF(a0),a0
	sub.l	#ich_SIZEOF,d0			;already read

	lsr.l	#3,d0
	move.l	d0,d4
	moveq	#0,d7

ucbcloop1:

	movem.l	d4-d5/d7,-(a7)
	move.l	a5,a2		;key
	bsr	Idea
	movem.l	(a7)+,d4-d5/d7

	lea	-8(a1),a1

	move.w	(a3)+,d0	;hmmm.
	eor.w	d0,(a1)+	;let's make this
	move.w	(a3)+,d0	;somewhat slower...
	eor.w	d0,(a1)+
	move.w	(a3)+,d0
	eor.w	d0,(a1)+
	move.w	(a3)+,d0
	eor.w	d0,(a1)+

	lea	-8(a1),a1
	add.w	(a1)+,d7
	add.w	(a1)+,d7
	add.w	(a1)+,d7
	add.w	(a1)+,d7

	subq	#1,d4
	beq	ucbcend
	tst.l	d5
	beq	ucbcloop1
	subq	#1,d5
	bne	ucbcloop1
	move.l	xsp_InBuf(a4),a3
	lea	ich_SIZEOF(a3),a3		;+len longword
	bra	ucbcloop1
ucbcend:
	neg.w	d7
	move.l	xsp_InBuf(a4),a0
	add.w	ich_CheckSum(a0),d7
	bne	ucbcbadpwd
	moveq	#0,d0
	rts
ucbcbadpwd:
	moveq	#XPKERR_WRONGPW,d0
	rts

CFBRoutine:
	clr.l	-(a7)
	clr.l	-(a7)
	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a3
	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	xsp_InLen(a4),d0
	move.l	d0,ich_OrigLen(a1)
	move.l	xsp_Mode(a4),ich_Mode(a1)
	lea	ich_SIZEOF(a1),a1

	move.l	d0,d1
	lsr.l	#3,d0
	move.l	d0,d4
	and.l	#7,d1
	move.l	d1,d6
	beq	cfbnoextra
	addq	#1,d0
cfbnoextra:
	lsl.l	#3,d0
	add.l	#ich_SIZEOF,d0
	move.l	d0,xsp_OutLen(a4)
	moveq	#0,d7
	move.l	d6,-(a7)
cfbloop1:

	move.l	(a3)+,(a1)+
	move.l	(a3)+,(a1)+
	lea	-8(a1),a1

	movem.l	a0/d4-d5/d7,-(a7)
	move.l	a5,a2		;key
	move.l	a1,a0
	bsr	Idea
	movem.l	(a7)+,a0/d4-d5/d7

	lea	-8(a1),a1

	move.w	(a0)+,d0
	add.w	d0,d7
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	eor.w	d0,(a1)+

	subq	#1,d4
	beq	cfbend
	tst.l	d5
	beq	cfbloop1
	subq	#1,d5		;if N blocks are coded
	bne	cfbloop1
	move.l	xsp_OutBuf(a4),a3	;switch from initializers
	lea	ich_SIZEOF(a3),a3	;to the real output
	bra	cfbloop1
cfbend:
	move.l	(a7)+,d6	;len exceed
	tst.b	d6
	beq	cfbnoexceed

	moveq	#1,d4
	move.l	a7,a2
cfbcpex:
	move.b	(a0)+,(a2)+	; copy the bytes exceeding
	subq.b	#1,d6
	bne	cfbcpex
	move.l	a7,a0		; stack as input
	clr.l	-(a7)		; no len exceed anymore
	bra	cfbloop1	; crypt it ...

cfbnoexceed:

	move.l	xsp_OutBuf(a4),a1
	move.w	d7,ich_CheckSum(a1)
	moveq	#0,d0
	lea	8(a7),a7
	rts

UCFBRoutine:

	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a3
	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	ich_OrigLen(a0),d0
	move.l	d0,xsp_OutLen(a4)
	move.l	xsp_InLen(a4),d0
	lea	ich_SIZEOF(a0),a0
	sub.l	#ich_SIZEOF,d0			;already read

	lsr.l	#3,d0
	move.l	d0,d4
	moveq	#0,d7

ucfbloop1:

	move.l	(a3)+,(a1)+
	move.l	(a3)+,(a1)+
	lea	-8(a1),a1

	movem.l	a0/d4-d5/d7,-(a7)
	move.l	a1,a0
	move.l	a5,a2		;key
	bsr	Idea
	movem.l	(a7)+,a0/d4-d5/d7

	lea	-8(a1),a1
	move.w	(a0)+,d0
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	eor.w	d0,(a1)+

	lea	-8(a1),a1
	add.w	(a1)+,d7
	add.w	(a1)+,d7
	add.w	(a1)+,d7
	add.w	(a1)+,d7

	subq	#1,d4
	beq	ucfbend
	tst.l	d5
	beq	ucfbloop1
	subq	#1,d5
	bne	ucfbloop1
	move.l	xsp_InBuf(a4),a3
	lea	ich_SIZEOF(a3),a3		;+len longword
	bra	ucfbloop1
ucfbend:
	neg.w	d7
	move.l	xsp_InBuf(a4),a0
	add.w	ich_CheckSum(a0),d7
	bne	ucfbbadpwd
	moveq	#0,d0
	rts
ucfbbadpwd:
	moveq	#XPKERR_WRONGPW,d0
	rts

OFBRoutine:
;ofb is somewhat mysterious mode. It circulary crypts the states, which
;are initialized by the initializers, and only xors the data over this
;states. This is both slow and unsafe, but its a standard method...

	clr.l	-(a7)
	clr.l	-(a7)
	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a0
;copy inits to stats
	lea	ids_States(a3),a1
ofbstatcp:
	move.l	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	subq	#1,d5
	bne	ofbstatcp

	move.l	ids_NumStates(a3),d5
	lea	ids_States(a3),a3

	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	xsp_InLen(a4),d0
	move.l	d0,ich_OrigLen(a1)
	move.l	xsp_Mode(a4),ich_Mode(a1)
	lea	ich_SIZEOF(a1),a1

	move.l	d0,d1
	lsr.l	#3,d0
	move.l	d0,d4
	and.l	#7,d1
	move.l	d1,d6
	beq	ofbnoextra
	addq	#1,d0
ofbnoextra:
	lsl.l	#3,d0
	add.l	#ich_SIZEOF,d0
	move.l	d0,xsp_OutLen(a4)
	moveq	#0,d7
	move.l	d6,-(a7)
ofbloop1:

	movem.l	a0-a1/d4-d5/d7,-(a7)
	move.l	a5,a2		;key
	move.l	a3,a0
	move.l	a3,a1		;crypt from and to states
	bsr	Idea
	movem.l	(a7)+,a0-a1/d4-d5/d7

	move.w	(a0)+,d0
	add.w	d0,d7
	move.w	(a3)+,(a1)
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	move.w	(a3)+,(a1)
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	move.w	(a3)+,(a1)
	eor.w	d0,(a1)+
	move.w	(a0)+,d0
	add.w	d0,d7
	move.w	(a3)+,(a1)
	eor.w	d0,(a1)+

	subq	#1,d4
	beq	ofbend
	subq	#1,d5		;if N blocks are coded
	bne	ofbloop1
	move.l	xsp_Sub(a4),a3
	move.l	ids_NumStates(a3),d5
	lea	ids_States(a3),a3	;reset states
	bra	ofbloop1
ofbend:
	move.l	(a7)+,d6	;len exceed
	tst.b	d6
	beq	ofbnoexceed

	moveq	#1,d4
	move.l	a7,a2
ofbcpex:
	move.b	(a0)+,(a2)+	; copy the bytes exceeding
	subq.b	#1,d6
	bne	ofbcpex
	move.l	a7,a0		; stack as input
	clr.l	-(a7)		; no len exceed anymore
	bra	ofbloop1	; crypt it ...

ofbnoexceed:

	move.l	xsp_OutBuf(a4),a1
	move.w	d7,ich_CheckSum(a1)
	moveq	#0,d0
	lea	8(a7),a7
	rts


UOFBRoutine:

	move.l	a0,a4
	move.l	xsp_Sub(a4),a3
	lea	ids_Key(a3),a5
	move.l	ids_NumStates(a3),d5
	lea	ids_Inits(a3),a0
;copy inits to stats
	lea	ids_States(a3),a1
uofbstatcp:
	move.l	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	subq	#1,d5
	bne	uofbstatcp

	move.l	ids_NumStates(a3),d5
	lea	ids_States(a3),a3

	move.l	xsp_InBuf(a4),a0
	move.l	xsp_OutBuf(a4),a1

	move.l	ich_OrigLen(a0),d0
	move.l	d0,xsp_OutLen(a4)
	move.l	xsp_InLen(a4),d0
	lea	ich_SIZEOF(a0),a0
	sub.l	#ich_SIZEOF,d0			;already read

	lsr.l	#3,d0
	move.l	d0,d4
	moveq	#0,d7

uofbloop1:

	movem.l	a0-a1/d4-d5/d7,-(a7)
	move.l	a5,a2		;key
	move.l	a3,a0
	move.l	a3,a1		;crypt from and to states
	bsr	Idea
	movem.l	(a7)+,a0-a1/d4-d5/d7

	move.w	(a0)+,d0
	move.w	(a3)+,d1
	eor.w	d1,d0
	add.w	d0,d7
	move.w	d0,(a1)+
	move.w	(a0)+,d0
	move.w	(a3)+,d1
	eor.w	d1,d0
	add.w	d0,d7
	move.w	d0,(a1)+
	move.w	(a0)+,d0
	move.w	(a3)+,d1
	eor.w	d1,d0
	add.w	d0,d7
	move.w	d0,(a1)+
	move.w	(a0)+,d0
	move.w	(a3)+,d1
	eor.w	d1,d0
	add.w	d0,d7
	move.w	d0,(a1)+

	subq	#1,d4
	beq	uofbend
	subq	#1,d5		;if N blocks are coded
	bne	uofbloop1
	move.l	xsp_Sub(a4),a3
	move.l	ids_NumStates(a3),d5
	lea	ids_States(a3),a3	;reset states
	bra	uofbloop1
uofbend:
	neg.w	d7
	move.l	xsp_InBuf(a4),a0
	add.w	ich_CheckSum(a0),d7
	bne	uofbbadpwd
	moveq	#0,d0
	rts
uofbbadpwd:
	moveq	#XPKERR_WRONGPW,d0
	rts

; --- Lib Entrys ---------

_XpksPackerInfo:
	move.l	a0,d0
	lea	IdeaInfo(pc),a0
	exg	a0,d0
	rts

_XpksPackChunk:
	movem.l	a0-a5/d1-d7,-(a7)
	tst.l	xsp_Sub(a0)
	bne	packisinit
	bsr	PackInit
	tst.l	d0
	bne	pcend
packisinit:
	move.l	xsp_Sub(a0),a1
	move.l	ids_Routine(a1),a1
	jsr	(a1)	;looks a little bit like assembler ooP
pcend:
	movem.l	(a7)+,a0-a5/d1-d7
	rts
	
_XpksUnpackFree:
_XpksPackFree:
	movem.l	a0-a3/d1-d3,-(a7)
	move.l	xsp_Sub(a0),a1
	move.l	#ids_SIZEOF,d0

;even if the ami is no multi user machine, we don't let key info hang
;around in the memory.

	lsr.l	#1,d0
FreeZero:
	clr.w	(a1)+
	subq	#1,d0
	bne	FreeZero

	move.l	xsp_Sub(a0),a1
	move.l	#ids_SIZEOF,d0
	move.l	a6,a3
	move.l	4.W,a6
	JSR	_LVOFreeMem(A6)
	move.l	a3,a6
	movem.l	(a7)+,a0-a3/d1-d3
	moveq	#0,d0
	rts


_XpksPackReset:
	moveq	#0,d0	;there is nothing to reset, coz any chunk is
	rts		;crypted the same way. To be honest, in ofb mode
			;the initializers are copied to the states for
			;every chunk to make this sure !

_XpksUnpackChunk:
	movem.l	a0-a5/d1-d7,-(a7)
	tst.l	xsp_Sub(a0)
	bne	upackisinit
	bsr	UnpackInit
	tst.l	d0
	bne	upcend
upackisinit:
	move.l	xsp_Sub(a0),a1
	move.l	ids_Routine(a1),a1
	jsr	(a1)
upcend:
	movem.l	(a7)+,a0-a5/d1-d7
	rts

; --- Static Data --------
IdeaInfo:
	dc.w	1
	dc.w	VERSION
	dc.w	0
	dc.w	0
	dc.l	IdeaName
	dc.l	LongName
	dc.l	Description
	dc.l	'IDEA'
	dc.l	XPKIF_PK_CHUNK|XPKIF_UP_CHUNK|XPKIF_ENCRYPTION|XPKIF_NEEDPASSWD|XPKIF_MODES
;I had 4 hours debugged, before I got that a68k doesn't want to have
;+ to add the flag masks above...
	dc.l	512*1024
	dc.l	8		; at least 64 bits
	dc.l	32768	; def. is 32 K, which gives ~ 3 chunks/sec on A3000
	dc.l	PackMsg
	dc.l	UnpackMsg
	dc.l	PackedMsg
	dc.l	UnpackedMsg
	dc.w	76		;cbc 1 is default mode
	dc.w	0
	dc.l	IdeaMode1
	dc.l	0,0,0,0,0,0

IdeaName:
	dc.b	'IDEA',0
LongName:
	dc.b	'ABPs IDEA for XPK',0
Description:
	dc.b	'The International Data Encryption Algorithm, XPK Implementation',0
PackMsg:
	dc.b	'Encrypting',0
UnpackMsg:
	dc.b	'Decrypting',0
PackedMsg:
	dc.b	'Encrypted',0
UnpackedMsg:
	dc.b	'Decrypted',0

	EVEN

IdeaMode1:
	dc.l	IdeaMode2
	dc.l	25
	dc.l	XPKMF_A3000SPEED
	dc.l	0
	dc.l	0
	dc.l	90
	dc.l	90
	dc.w	1
	dc.w	0
	dc.b	'ECB',0,0,0,0,0,0,0

IdeaMode2:
	dc.l	IdeaMode3
	dc.l	50
	dc.l	XPKMF_A3000SPEED
	dc.l	0
	dc.l	0
	dc.l	87
	dc.l	87
	dc.w	1
	dc.w	0
	dc.b	'CFB 1-25',0,0

IdeaMode3:
	dc.l	IdeaMode4
	dc.l	75
	dc.l	XPKMF_A3000SPEED
	dc.l	0
	dc.l	0
	dc.l	84
	dc.l	84
	dc.w	1
	dc.w	0
	dc.b	'OFB 1-25',0,0

IdeaMode4:
	dc.l	0
	dc.l	100
	dc.l	XPKMF_A3000SPEED
	dc.l	0
	dc.l	0
	dc.l	84
	dc.l	84
	dc.w	1
	dc.w	0
	dc.b	'CBC 1-25',0,0
	END
