misc/060tsys/t27isp.s- Source code ソースコード - XEiJ - STUDIO KAMADA
misc/060tsys/t27isp.s

    ;----------------------------------------------------------------
    ;
    ;	68060 INTEGER SOFTWARE PACKAGE
    ;
    ;----------------------------------------------------------------

    	.cpu	68060

    	.xdef	_060ISP_TABLE

    	.xdef	_isp_unimp		;_060ISP_TABLE+$00
    					;_060ISP_TABLE+$00000238
    	.xdef	_isp_cas		;_060ISP_TABLE+$08
    					;_060ISP_TABLE+$00001630
    	.xdef	_isp_cas2		;_060ISP_TABLE+$10
    					;_060ISP_TABLE+$000012EE
    	.xdef	_isp_cas_finish		;_060ISP_TABLE+$18
    					;_060ISP_TABLE+$00001204
    	.xdef	_isp_cas2_finish	;_060ISP_TABLE+$20
    					;_060ISP_TABLE+$00001100
    	.xdef	_isp_cas_inrange	;_060ISP_TABLE+$28
    					;_060ISP_TABLE+$000012CE
    	.xdef	_isp_cas_terminate	;_060ISP_TABLE+$30
    					;_060ISP_TABLE+$00001288
    	.xdef	_isp_cas_restart	;_060ISP_TABLE+$38
    					;_060ISP_TABLE+$00001264

    global	.macro
    	.endm

    ;----------------------------------------------------------------
    ;ISPコールアウトテーブル
    _I_CALL_TOP::
    	.dc.l	_060_real_chk-_I_CALL_TOP
    	.dc.l	_060_real_divbyzero-_I_CALL_TOP
    	.dc.l	_060_real_trace-_I_CALL_TOP
    	.dc.l	_060_real_access-_I_CALL_TOP
    	.dc.l	_060_isp_done-_I_CALL_TOP
    	.dc.l	_isp_cas-_I_CALL_TOP	;_060ISP_TABLE+$08-_I_CALL_TOP
    					;_060ISP_TABLE+$00001630-_I_CALL_TOP
    	.dc.l	_isp_cas2-_I_CALL_TOP	;_060ISP_TABLE+$10-_I_CALL_TOP
    					;_060ISP_TABLE+$000012EE-_I_CALL_TOP
    	.dc.l	_060_real_lock_page-_I_CALL_TOP
    	.dc.l	_060_real_unlock_page-_I_CALL_TOP
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	_060_imem_read-_I_CALL_TOP
    	.dc.l	_060_dmem_read-_I_CALL_TOP
    	.dc.l	_060_dmem_write-_I_CALL_TOP
    	.dc.l	_060_imem_read_word-_I_CALL_TOP
    	.dc.l	_060_imem_read_long-_I_CALL_TOP
    	.dc.l	_060_dmem_read_byte-_I_CALL_TOP
    	.dc.l	_060_dmem_read_word-_I_CALL_TOP
    	.dc.l	_060_dmem_read_long-_I_CALL_TOP
    	.dc.l	_060_dmem_write_byte-_I_CALL_TOP
    	.dc.l	_060_dmem_write_word-_I_CALL_TOP
    	.dc.l	_060_dmem_write_long-_I_CALL_TOP
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    	.dc.l	0
    ;----------------------------------------------------------------
    ;ISP本体

    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    * MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP
    * M68000 Hi-Performance Microprocessor Division
    * M68060 Software Package Production Release
    *
    * M68060 Software Package Copyright (C) 1993, 1994, 1995, 1996 Motorola Inc.
    * All rights reserved.
    *
    * THE SOFTWARE is provided on an "AS IS" basis and without warranty.
    * To the maximum extent permitted by applicable law,
    * MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED,
    * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS
    * FOR A PARTICULAR PURPOSE and any warranty against infringement with
    * regard to the SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF)
    * and any accompanying written materials.
    *
    * To the maximum extent permitted by applicable law,
    * IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER
    * (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
    * BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS)
    * ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE.
    *
    * Motorola assumes no responsibility for the maintenance and support
    * of the SOFTWARE.
    *
    * You are hereby granted a copyright license to use, modify, and distribute the
    * SOFTWARE so long as this entire notice is retained without alteration
    * in any modified and/or redistributed versions, and that such modified
    * versions are clearly identified as such.
    * No licenses are granted by implication, estoppel or otherwise under any
    * patents or trademarks of Motorola, Inc.
    *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    *
    * ireal.s:
    *	This file is appended to the top of the 060ISP package
    * and contains the entry points into the package. The user, in
    * effect, branches to one of the branch table entries located
    * after _060ISP_TABLE.
    *	Also, subroutine stubs exist in this file (_isp_done for
    * example) that are referenced by the ISP package itself in order
    * to call a given routine. The stub routine actually performs the
    * callout. The ISP code does a "bsr" to the stub routine. This
    * extra layer of hierarchy adds a slight performance penalty but
    * it makes the ISP code easier to read and more mainatinable.
    *

    _off_chk	set	$00
    _off_divbyzero	set	$04
    _off_trace	set	$08
    _off_access	set	$0c
    _off_done	set	$10

    _off_cas	set	$14
    _off_cas2	set	$18
    _off_lock	set	$1c
    _off_unlock	set	$20

    _off_imr	set	$40
    _off_dmr	set	$44
    _off_dmw	set	$48
    _off_irw	set	$4c
    _off_irl	set	$50
    _off_drb	set	$54
    _off_drw	set	$58
    _off_drl	set	$5c
    _off_dwb	set	$60
    _off_dww	set	$64
    _off_dwl	set	$68

    _060ISP_TABLE:

    * Here's the table of ENTRY POINTS for those linking the package.
    	bra.l	_isp_unimp
    	.dc.w	$0000

    	bra.l	_isp_cas
    	.dc.w	$0000

    	bra.l	_isp_cas2
    	.dc.w	$0000

    	bra.l	_isp_cas_finish
    	.dc.w	$0000

    	bra.l	_isp_cas2_finish
    	.dc.w	$0000

    	bra.l	_isp_cas_inrange
    	.dc.w	$0000

    	bra.l	_isp_cas_terminate
    	.dc.w	$0000

    	bra.l	_isp_cas_restart
    	.dc.w	$0000

    	.dcb.w	64/2,$51FC

    *############################################################

    	global	_real_chk
    _real_chk:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_chk,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_divbyzero
    _real_divbyzero:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_divbyzero,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_trace
    _real_trace:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_trace,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_access
    _real_access:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_access,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_isp_done
    _isp_done:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_done,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    *######################################

    	global	_real_cas
    _real_cas:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_cas,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_cas2
    _real_cas2:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_cas2,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_lock_page
    _real_lock_page:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_lock,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_real_unlock_page
    _real_unlock_page:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_unlock,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    *######################################

    	global	_imem_read
    _imem_read:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_imr,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_read
    _dmem_read:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_dmr,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_write
    _dmem_write:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_dmw,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_imem_read_word
    _imem_read_word:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_irw,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_imem_read_long
    _imem_read_long:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_irl,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_read_byte
    _dmem_read_byte:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_drb,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_read_word
    _dmem_read_word:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_drw,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_read_long
    _dmem_read_long:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_drl,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_write_byte
    _dmem_write_byte:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_dwb,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_write_word
    _dmem_write_word:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_dww,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    	global	_dmem_write_long
    _dmem_write_long:
    	move.l	d0,-(sp)
    	move.l	(_060ISP_TABLE-$80+_off_dwl,pc),d0
    	pea.l	(_060ISP_TABLE-$80.l,pc,d0.l)
    	move.l	$4(sp),d0
    	rtd	#$4

    *
    * This file contains a set of define statements for constants
    * in oreder to promote readability within the core code itself.
    *

    LOCAL_SIZE	set	96		* stack frame size(bytes)
    LV	set	-LOCAL_SIZE		* stack offset

    EXC_ISR	set	$4			* stack status register
    EXC_IPC	set	$6			* stack pc
    EXC_IVOFF	set	$a		* stacked vector offset

    EXC_AREGS	set	LV+64		* offset of all address regs
    EXC_DREGS	set	LV+32		* offset of all data regs

    EXC_A7	set	EXC_AREGS+(7*4)		* offset of a7
    EXC_A6	set	EXC_AREGS+(6*4)		* offset of a6
    EXC_A5	set	EXC_AREGS+(5*4)		* offset of a5
    EXC_A4	set	EXC_AREGS+(4*4)		* offset of a4
    EXC_A3	set	EXC_AREGS+(3*4)		* offset of a3
    EXC_A2	set	EXC_AREGS+(2*4)		* offset of a2
    EXC_A1	set	EXC_AREGS+(1*4)		* offset of a1
    EXC_A0	set	EXC_AREGS+(0*4)		* offset of a0
    EXC_D7	set	EXC_DREGS+(7*4)		* offset of d7
    EXC_D6	set	EXC_DREGS+(6*4)		* offset of d6
    EXC_D5	set	EXC_DREGS+(5*4)		* offset of d5
    EXC_D4	set	EXC_DREGS+(4*4)		* offset of d4
    EXC_D3	set	EXC_DREGS+(3*4)		* offset of d3
    EXC_D2	set	EXC_DREGS+(2*4)		* offset of d2
    EXC_D1	set	EXC_DREGS+(1*4)		* offset of d1
    EXC_D0	set	EXC_DREGS+(0*4)		* offset of d0

    EXC_TEMP	set	LV+16		* offset of temp stack space

    EXC_SAVVAL	set	LV+12		* offset of old areg value
    EXC_SAVREG	set	LV+11		* offset of old areg index

    SPCOND_FLG	set	LV+10		* offset of spc condition flg

    EXC_CC	set	LV+8			* offset of cc register
    EXC_EXTWPTR	set	LV+4		* offset of current PC
    EXC_EXTWORD	set	LV+2		* offset of current ext opword
    EXC_OPWORD	set	LV+0		* offset of current opword

    *##########################
    * SPecial CONDition FLaGs #
    *##########################
    mia7_flg	set	$04		* (a7)+ flag
    mda7_flg	set	$08		* -(a7) flag
    ichk_flg	set	$10		* chk exception flag
    idbyz_flg	set	$20		* divbyzero flag
    restore_flg	set	$40		* restore -(an)+ flag
    immed_flg	set	$80		* immediate data flag

    mia7_bit	set	$2		* (a7)+ bit
    mda7_bit	set	$3		* -(a7) bit
    ichk_bit	set	$4		* chk exception bit
    idbyz_bit	set	$5		* divbyzero bit
    restore_bit	set	$6		* restore -(a7)+ bit
    immed_bit	set	$7		* immediate data bit

    *########
    * Misc. #
    *########
    BYTE	set	1			* len(byte) == 1 byte
    WORD	set	2			* len(word) == 2 bytes
    LONG	set	4			* len(longword) == 4 bytes

    *########################################################################
    * XDEF ****************************************************************	#
    *	_isp_unimp(): 060ISP entry point for Unimplemented Instruction	#
    *									#
    *	This handler should be the first code executed upon taking the 	#
    * 	"Unimplemented Integer Instruction" exception in an operating	#
    *	system.								#
    *									#
    * XREF ****************************************************************	#
    *	_imem_read_{word,long}() - read instruction word/longword	#
    *	_mul64() - emulate 64-bit multiply				#
    * 	_div64() - emulate 64-bit divide				#
    *	_moveperipheral() - emulate "movep"				#
    *	_compandset() - emulate misaligned "cas"			#
    *	_compandset2() - emulate "cas2"					#
    *	_chk2_cmp2() - emulate "cmp2" and "chk2"			#
    *	_isp_done() - "callout" for normal final exit			#
    *	_real_trace() - "callout" for Trace exception			#
    *	_real_chk() - "callout" for Chk exception			#
    *	_real_divbyzero() - "callout" for DZ exception			#
    *	_real_access() - "callout" for access error exception		#
    *									#
    * INPUT ***************************************************************	#
    *	- The system stack contains the Unimp Int Instr stack frame	#
    * 									#
    * OUTPUT **************************************************************	#
    *	If Trace exception:						#
    *	- The system stack changed to contain Trace exc stack frame	#
    *	If Chk exception:						#
    *	- The system stack changed to contain Chk exc stack frame	#
    *	If DZ exception:						#
    *	- The system stack changed to contain DZ exc stack frame	#
    *	If access error exception:					#
    *	- The system stack changed to contain access err exc stk frame	#
    *	Else:								#
    *	- Results saved as appropriate					#
    *									#
    * ALGORITHM ***********************************************************	#
    *	This handler fetches the first instruction longword from	#
    * memory and decodes it to determine which of the unimplemented		#
    * integer instructions caused this exception. This handler then calls	#
    * one of _mul64(), _div64(), _moveperipheral(), _compandset(), 		#
    * _compandset2(), or _chk2_cmp2() as appropriate. 			#
    *	Some of these instructions, by their nature, may produce other	#
    * types of exceptions. "div" can produce a divide-by-zero exception,	#
    * and "chk2" can cause a "Chk" exception. In both cases, the current	#
    * exception stack frame must be converted to an exception stack frame	#
    * of the correct exception type and an exit must be made through	#
    * _real_divbyzero() or _real_chk() as appropriate. In addition, all	#
    * instructions may be executing while Trace is enabled. If so, then	#
    * a Trace exception stack frame must be created and an exit made 	#
    * through _real_trace().						#
    *	Meanwhile, if any read or write to memory using the		#
    * _mem_{read,write}() "callout"s returns a failing value, then an	#
    * access error frame must be created and an exit made through		#
    * _real_access().							#
    *	If none of these occur, then a normal exit is made through	#
    * _isp_done().								#
    *									#
    *	This handler, upon entry, saves almost all user-visible 	#
    * address and data registers to the stack. Although this may seem to	#
    * cause excess memory traffic, it was found that due to having to	#
    * access these register files for things like data retrieval and <ea>	#
    * calculations, it was more efficient to have them on the stack where	#
    * they could be accessed by indexing rather than to make subroutine 	#
    * calls to retrieve a register of a particular index. 			#
    *									#
    *########################################################################

    	global	_isp_unimp
    _isp_unimp:
    	link.w	a6,#-LOCAL_SIZE		* create room for stack frame

    	movem.l	d0-d7/a0-a5,EXC_DREGS(a6)	* store d0-d7/a0-a5
    	move.l	(a6),EXC_A6(a6)		* store a6

    	btst	#$5,EXC_ISR(a6)		* from s or u mode?
    	bne.b	uieh_s			* supervisor mode
    uieh_u:
    	move.l	usp,a0			* fetch user stack pointer
    	move.l	a0,EXC_A7(a6)		* store a7
    	bra.b	uieh_cont
    uieh_s:
    	lea	$c(a6),a0
    	move.l	a0,EXC_A7(a6)		* store corrected sp

    *##############################################################################

    uieh_cont:
    	clr.b	SPCOND_FLG(a6)		* clear "special case" flag

    	move.w	EXC_ISR(a6),EXC_CC(a6)		* store cc copy on stack
    	move.l	EXC_IPC(a6),EXC_EXTWPTR(a6)	* store extwptr on stack

    *
    * fetch the opword and first extension word pointed to by the stacked pc
    * and store them to the stack for now
    *
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$4,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_long		* fetch opword & extword
    	move.l	d0,EXC_OPWORD(a6)	* store extword on stack

    *########################################################################
    * muls.l	0100 1100 00 |<ea>|	0*** 1100 0000 0*** 		#
    * mulu.l	0100 1100 00 |<ea>|	0*** 0100 0000 0***		#
    *									#
    * divs.l	0100 1100 01 |<ea>|	0*** 1100 0000 0***		#
    * divu.l	0100 1100 01 |<ea>|	0*** 0100 0000 0***		#
    *									#
    * movep.w m2r	0000 ***1 00 001***	| <displacement>  |		#
    * movep.l m2r	0000 ***1 01 001***	| <displacement>  |		#
    * movep.w r2m	0000 ***1 10 001***	| <displacement>  |		#
    * movep.l r2m	0000 ***1 11 001***	| <displacement>  |		#
    *									#
    * cas.w		0000 1100 11 |<ea>|	0000 000* **00 0***		#
    * cas.l		0000 1110 11 |<ea>|	0000 000* **00 0***		#
    *									#
    * cas2.w	0000 1100 11 111100	**** 000* **00 0***		#
    *					**** 000* **00 0***		#
    * cas2.l	0000 1110 11 111100	**** 000* **00 0***		#
    *					**** 000* **00 0***		#
    *									#
    * chk2.b	0000 0000 11 |<ea>|	**** 1000 0000 0000		#
    * chk2.w	0000 0010 11 |<ea>|	**** 1000 0000 0000		#
    * chk2.l	0000 0100 11 |<ea>|	**** 1000 0000 0000		#
    *									#
    * cmp2.b	0000 0000 11 |<ea>|	**** 0000 0000 0000		#
    * cmp2.w	0000 0010 11 |<ea>|	**** 0000 0000 0000		#
    * cmp2.l	0000 0100 11 |<ea>|	**** 0000 0000 0000		#
    *########################################################################

    *
    * using bit 14 of the operation word, separate into 2 groups:
    * (group1) mul64, div64
    * (group2) movep, chk2, cmp2, cas2, cas
    *
    	btst	#$1e,d0			* group1 or group2
    	beq.b	uieh_group2		* go handle group2

    *
    * now, w/ group1, make mul64's decode the fastest since it will
    * most likely be used the most.
    *
    uieh_group1:
    	btst	#$16,d0			* test for div64
    	bne.b	uieh_div64		* go handle div64

    uieh_mul64:
    * mul64() may use ()+ addressing and may, therefore, alter a7

    	bsr.l	_mul64			* _mul64()

    	btst	#$5,EXC_ISR(a6)		* supervisor mode?
    	beq.w	uieh_done
    	btst	#mia7_bit,SPCOND_FLG(a6)	* was a7 changed?
    	beq.w	uieh_done		* no
    	btst	#$7,EXC_ISR(a6)		* is trace enabled?
    	bne.w	uieh_trace_a7		* yes
    	bra.w	uieh_a7			* no

    uieh_div64:
    * div64() may use ()+ addressing and may, therefore, alter a7.
    * div64() may take a divide by zero exception.

    	bsr.l	_div64			* _div64()

    * here, we sort out all of the special cases that may have happened.
    	btst	#mia7_bit,SPCOND_FLG(a6)	* was a7 changed?
    	bne.b	uieh_div64_a7		* yes
    uieh_div64_dbyz:
    	btst	#idbyz_bit,SPCOND_FLG(a6)	* did divide-by-zero occur?
    	bne.w	uieh_divbyzero		* yes
    	bra.w	uieh_done		* no
    uieh_div64_a7:
    	btst	#$5,EXC_ISR(a6)		* supervisor mode?
    	beq.b	uieh_div64_dbyz		* no
    * here, a7 has been incremented by 4 bytes in supervisor mode. we still
    * may have the following 3 cases:
    *	(i)	(a7)+
    *	(ii)	(a7)+; trace
    *	(iii)	(a7)+; divide-by-zero
    *
    	btst	#idbyz_bit,SPCOND_FLG(a6)	* did divide-by-zero occur?
    	bne.w	uieh_divbyzero_a7	* yes
    	tst.b	EXC_ISR(a6)		* no; is trace enabled?
    	bmi.w	uieh_trace_a7		* yes
    	bra.w	uieh_a7			* no

    *
    * now, w/ group2, make movep's decode the fastest since it will
    * most likely be used the most.
    *
    uieh_group2:
    	btst	#$18,d0			* test for not movep
    	beq.b	uieh_not_movep

    	bsr.l	_moveperipheral		* _movep()
    	bra.w	uieh_done

    uieh_not_movep:
    	btst	#$1b,d0			* test for chk2,cmp2
    	beq.b	uieh_chk2cmp2		* go handle chk2,cmp2

    	swap	d0			* put opword in lo word
    	cmpi.b	#$fc,d0			* test for cas2
    	beq.b	uieh_cas2		* go handle cas2

    uieh_cas:

    	bsr.l	_compandset		* _cas()

    * the cases of "cas Dc,Du,(a7)+" and "cas Dc,Du,-(a7)" used from supervisor
    * mode are simply not considered valid and therefore are not handled.

    	bra.w	uieh_done

    uieh_cas2:

    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word		* read extension word

    	tst.l	d1			* ifetch error?
    	bne.w	isp_iacc		* yes

    	bsr.l	_compandset2		* _cas2()
    	bra.w	uieh_done

    uieh_chk2cmp2:
    * chk2 may take a chk exception

    	bsr.l	_chk2_cmp2		* _chk2_cmp2()

    * here we check to see if a chk trap should be taken
    	cmpi.b	#ichk_flg,SPCOND_FLG(a6)
    	bne.w	uieh_done
    	bra.b	uieh_chk_trap

    *##########################################################################

    *
    * the required emulation has been completed. now, clean up the necessary stack
    * info and prepare for rte
    *
    uieh_done:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes

    * if exception occurred in user mode, then we have to restore a7 in case it
    * changed. we don't have to update a7  for supervisor mose because that case
    * doesn't flow through here
    	btst	#$5,EXC_ISR(a6)		* user or supervisor?
    	bne.b	uieh_finish		* supervisor

    	move.l	EXC_A7(a6),a0		* fetch user stack pointer
    	move.l	a0,usp			* restore it

    uieh_finish:
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	btst	#$7,EXC_ISR(a6)		* is trace mode on?
    	bne.b	uieh_trace		* yes;go handle trace mode

    	move.l	EXC_EXTWPTR(a6),EXC_IPC(a6)	* new pc on stack frame
    	move.l	EXC_A6(a6),(a6)		* prepare new a6 for unlink
    	unlk	a6			* unlink stack frame
    	bra.l	_isp_done

    *
    * The instruction that was just emulated was also being traced. The trace
    * trap for this instruction will be lost unless we jump to the trace handler.
    * So, here we create a Trace Exception format number two exception stack
    * frame from the Unimplemented Integer Intruction Exception stack frame
    * format number zero and jump to the user supplied hook "_real_trace()".
    *
    *		   UIEH FRAME		   TRACE FRAME
    *		*****************	*****************
    *		* 0x0 *  0x0f4	*	*    Current	*
    *		*****************	*      PC	*
    *		*    Current	*	*****************
    *		*      PC 	*	* 0x2 *  0x024	*
    *		*****************	*****************
    *		*      SR	*	*     Next	*
    *		*****************	*      PC	*
    *	      ->*     Old   	*	*****************
    *  from link -->*      A6	*	*      SR	*
    *	        *****************	*****************
    *	       /*      A7	*	*      New	* <-- for final unlink
    *	      / *		*	*      A6	*
    * link frame <  *****************	*****************
    *	      \ ~		~	~		~
    *	       \*****************	*****************
    *
    uieh_trace:
    	move.l	EXC_A6(a6),-$4(a6)
    	move.w	EXC_ISR(a6),$0.w(a6)
    	move.l	EXC_IPC(a6),$8(a6)
    	move.l	EXC_EXTWPTR(a6),$2(a6)
    	move.w	#$2024,$6(a6)
    	sub.l	#$4,a6
    	unlk	a6
    	bra.l	_real_trace

    *
    *	   UIEH FRAME		    CHK FRAME
    *	*****************	*****************
    *	* 0x0 *  0x0f4	*	*    Current	*
    *	*****************	*      PC	*
    *	*    Current	*	*****************
    *	*      PC	*	* 0x2 *  0x018	*
    *	*****************	*****************
    *	*      SR	*	*     Next	*
    *	*****************	*      PC	*
    *	    (4 words)		*****************
    *				*      SR	*
    *				*****************
    *				    (6 words)
    *
    * the chk2 instruction should take a chk trap. so, here we must create a
    * chk stack frame from an unimplemented integer instruction exception frame
    * and jump to the user supplied entry point "_real_chk()".
    *
    uieh_chk_trap:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	move.w	EXC_ISR(a6),(a6)	* put new SR on stack
    	move.l	EXC_IPC(a6),$8(a6)	* put "Current PC" on stack
    	move.l	EXC_EXTWPTR(a6),$2(a6)		* put "Next PC" on stack
    	move.w	#$2018,$6(a6)		* put Vector Offset on stack

    	move.l	EXC_A6(a6),a6		* restore a6
    	add.l	#LOCAL_SIZE,sp		* clear stack frame

    	bra.l	_real_chk

    *
    *	   UIEH FRAME		 DIVBYZERO FRAME
    *	*****************	*****************
    *	* 0x0 *  0x0f4	*	*    Current	*
    *	*****************	*      PC	*
    *	*    Current	*	*****************
    *	*      PC	*	* 0x2 *  0x014	*
    *	*****************	*****************
    *	*      SR	*	*     Next	*
    *	*****************	*      PC	*
    *	    (4 words)		*****************
    *				*      SR	*
    *				*****************
    *				    (6 words)
    *
    * the divide instruction should take an integer divide by zero trap. so, here
    * we must create a divbyzero stack frame from an unimplemented integer
    * instruction exception frame and jump to the user supplied entry point
    * "_real_divbyzero()".
    *
    uieh_divbyzero:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	move.w	EXC_ISR(a6),(a6)	* put new SR on stack
    	move.l	EXC_IPC(a6),$8(a6)	* put "Current PC" on stack
    	move.l	EXC_EXTWPTR(a6),$2(a6)		* put "Next PC" on stack
    	move.w	#$2014,$6(a6)		* put Vector Offset on stack

    	move.l	EXC_A6(a6),a6		* restore a6
    	add.l	#LOCAL_SIZE,sp		* clear stack frame

    	bra.l	_real_divbyzero

    *
    *				 DIVBYZERO FRAME
    *				*****************
    *				*    Current	*
    *	   UIEH FRAME		*      PC	*
    *	*****************	*****************
    *	* 0x0 *  0x0f4	*	* 0x2 * 0x014	*
    *	*****************	*****************
    *	*    Current	*	*     Next	*
    *	*      PC	*	*      PC	*
    *	*****************	*****************
    *	*      SR	*	*      SR	*
    *	*****************	*****************
    *	    (4 words)		    (6 words)
    *
    * the divide instruction should take an integer divide by zero trap. so, here
    * we must create a divbyzero stack frame from an unimplemented integer
    * instruction exception frame and jump to the user supplied entry point
    * "_real_divbyzero()".
    *
    * However, we must also deal with the fact that (a7)+ was used from supervisor
    * mode, thereby shifting the stack frame up 4 bytes.
    *
    uieh_divbyzero_a7:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	move.l	EXC_IPC(a6),$c(a6)	* put "Current PC" on stack
    	move.w	#$2014,$a(a6)		* put Vector Offset on stack
    	move.l	EXC_EXTWPTR(a6),$6(a6)		* put "Next PC" on stack

    	move.l	EXC_A6(a6),a6		* restore a6
    	add.l	#4+LOCAL_SIZE,sp	* clear stack frame

    	bra.l	_real_divbyzero

    *
    *				   TRACE FRAME
    *				*****************
    *				*    Current	*
    *	   UIEH FRAME		*      PC	*
    *	*****************	*****************
    *	* 0x0 *  0x0f4	*	* 0x2 * 0x024	*
    *	*****************	*****************
    *	*    Current	*	*     Next	*
    *	*      PC	*	*      PC	*
    *	*****************	*****************
    *	*      SR	*	*      SR	*
    *	*****************	*****************
    *	    (4 words)		    (6 words)
    *
    *
    * The instruction that was just emulated was also being traced. The trace
    * trap for this instruction will be lost unless we jump to the trace handler.
    * So, here we create a Trace Exception format number two exception stack
    * frame from the Unimplemented Integer Intruction Exception stack frame
    * format number zero and jump to the user supplied hook "_real_trace()".
    *
    * However, we must also deal with the fact that (a7)+ was used from supervisor
    * mode, thereby shifting the stack frame up 4 bytes.
    *
    uieh_trace_a7:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	move.l	EXC_IPC(a6),$c(a6)	* put "Current PC" on stack
    	move.w	#$2024,$a(a6)		* put Vector Offset on stack
    	move.l	EXC_EXTWPTR(a6),$6(a6)		* put "Next PC" on stack

    	move.l	EXC_A6(a6),a6		* restore a6
    	add.l	#4+LOCAL_SIZE,sp	* clear stack frame

    	bra.l	_real_trace

    *
    *				   UIEH FRAME
    *				*****************
    *				* 0x0 * 0x0f4	*
    *	   UIEH FRAME		*****************
    *	*****************	*     Next	*
    *	* 0x0 *  0x0f4	*	*      PC	*
    *	*****************	*****************
    *	*    Current	*	*      SR	*
    *	*      PC	*	*****************
    *	*****************	    (4 words)
    *	*      SR	*
    *	*****************
    *	    (4 words)
    uieh_a7:
    	move.b	EXC_CC+1(a6),EXC_ISR+1(a6)	* insert new ccodes
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5

    	move.w	#$00f4,$e(a6)		* put Vector Offset on stack
    	move.l	EXC_EXTWPTR(a6),$a(a6)		* put "Next PC" on stack
    	move.w	EXC_ISR(a6),$8(a6)	* put SR on stack

    	move.l	EXC_A6(a6),a6		* restore a6
    	add.l	#8+LOCAL_SIZE,sp	* clear stack frame
    	bra.l	_isp_done

    *#########

    * this is the exit point if a data read or write fails.
    * a0 = failing address
    * d0 = fslw
    isp_dacc:
    	move.l	a0,(a6)			* save address
    	move.l	d0,-$4(a6)		* save partial fslw

    	lea	-64(a6),sp
    	movem.l	(sp)+,d0-d7/a0-a6	* restore d0-d7/a0-a6

    	move.l	$c(sp),-(sp)		* move voff,hi(pc)
    	move.l	$4(sp),$10(sp)		* store fslw
    	move.l	$c(sp),$4(sp)		* store sr,lo(pc)
    	move.l	$8(sp),$c(sp)		* store address
    	move.l	(sp)+,$4(sp)		* store voff,hi(pc)
    	move.w	#$4008,$6(sp)		* store new voff

    	bra.b	isp_acc_exit

    * this is the exit point if an instruction word read fails.
    * FSLW:
    *	misaligned = true
    *	read = true
    * 	size = word
    * 	instruction = true
    * 	software emulation error = true
    isp_iacc:
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore d0-d7/a0-a5
    	unlk	a6			* unlink frame
    	sub.w	#$8,sp			* make room for acc frame
    	move.l	$8(sp),(sp)		* store sr,lo(pc)
    	move.w	$c(sp),$4(sp)		* store hi(pc)
    	move.w	#$4008,$6(sp)		* store new voff
    	move.l	$2(sp),$8(sp)		* store address (=pc)
    	move.l	#$09428001,$c(sp)	* store fslw

    isp_acc_exit:
    	btst	#$5,(sp)		* user or supervisor?
    	beq.b	isp_acc_exit2		* user
    	bset	#$2,$d(sp)		* set supervisor TM bit
    isp_acc_exit2:
    	bra.l	_real_access

    * if the addressing mode was (an)+ or -(an), the address register must
    * be restored to it's pre-exception value before entering _real_access.
    isp_restore:
    	cmpi.b	#restore_flg,SPCOND_FLG(a6)	* do we need a restore?
    	bne.b	isp_restore_done	* no
    	clr.l	d0
    	move.b	EXC_SAVREG(a6),d0	* regno to restore
    	move.l	EXC_SAVVAL(a6),(EXC_AREGS,a6,d0.l*4)	* restore value
    isp_restore_done:
    	rts

    *########################################################################
    * XDEF ****************************************************************	#
    *	_calc_ea(): routine to calculate effective address		#
    *									#
    * XREF ****************************************************************	#
    * 	_imem_read_word() - read instruction word			#
    * 	_imem_read_long() - read instruction longword			#
    * 	_dmem_read_long() - read data longword (for memory indirect)	#
    * 	isp_iacc() - handle instruction access error exception		#
    *	isp_dacc() - handle data access error exception			#
    *									#
    * INPUT ***************************************************************	#
    * 	d0 = number of bytes related to effective address (w,l)		#
    *									#
    * OUTPUT **************************************************************	#
    *	If exiting through isp_dacc...					#
    *		a0 = failing address					#
    *		d0 = FSLW						#
    *	elsif exiting though isp_iacc...				#
    *		none							#
    *	else								#
    *		a0 = effective address					#
    *									#
    * ALGORITHM ***********************************************************	#
    * 	The effective address type is decoded from the opword residing	#
    * on the stack. A jump table is used to vector to a routine for the 	#
    * appropriate mode. Since none of the emulated integer instructions	#
    * uses byte-sized operands, only handle word and long operations.	#
    *									#
    * 	Dn,An	- shouldn't enter here					#
    *	(An)	- fetch An value from stack				#
    * 	-(An)	- fetch An value from stack; return decr value;		#
    *		  place decr value on stack; store old value in case of	#
    *		  future access error; if -(a7), set mda7_flg in 	#
    *		  SPCOND_FLG						#
    *	(An)+	- fetch An value from stack; return value;		#
    *		  place incr value on stack; store old value in case of	#
    *		  future access error; if (a7)+, set mia7_flg in	#
    *		  SPCOND_FLG						#
    *	(d16,An) - fetch An value from stack; read d16 using 		#
    *		  _imem_read_word(); fetch may fail -> branch to	#
    *		  isp_iacc()						#
    *	(xxx).w,(xxx).l - use _imem_read_{word,long}() to fetch		#
    *		  address; fetch may fail				#
    *	#<data> - return address of immediate value; set immed_flg	#
    *		  in SPCOND_FLG						#
    *	(d16,PC) - fetch stacked PC value; read d16 using		#
    *		  _imem_read_word(); fetch may fail -> branch to	#
    *		  isp_iacc()						#
    *	everything else - read needed displacements as appropriate w/	#
    *		  _imem_read_{word,long}(); read may fail; if memory	#
    * 		  indirect, read indirect address using			#
    *		  _dmem_read_long() which may also fail			#
    *									#
    *########################################################################

    	global	_calc_ea
    _calc_ea:
    	move.l	d0,a0			* move # bytes to a0

    * MODE and REG are taken from the EXC_OPWORD.
    	move.w	EXC_OPWORD(a6),d0	* fetch opcode word
    	move.w	d0,d1			* make a copy

    	andi.w	#$3f,d0			* extract mode field
    	andi.l	#$7,d1			* extract reg  field

    * jump to the corresponding function for each {MODE,REG} pair.
    	move.w	(tbl_ea_mode.b,pc,d0.w*2),d0		* fetch jmp distance
    	jmp	(tbl_ea_mode.b,pc,d0.w*1)	* jmp to correct ea mode

    	.dc.w	$4AFC,64
    tbl_ea_mode:
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode

    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode

    	.dc.w	addr_ind_a0-tbl_ea_mode
    	.dc.w	addr_ind_a1-tbl_ea_mode
    	.dc.w	addr_ind_a2-tbl_ea_mode
    	.dc.w	addr_ind_a3-tbl_ea_mode
    	.dc.w	addr_ind_a4-tbl_ea_mode
    	.dc.w	addr_ind_a5-tbl_ea_mode
    	.dc.w	addr_ind_a6-tbl_ea_mode
    	.dc.w	addr_ind_a7-tbl_ea_mode

    	.dc.w	addr_ind_p_a0-tbl_ea_mode
    	.dc.w	addr_ind_p_a1-tbl_ea_mode
    	.dc.w	addr_ind_p_a2-tbl_ea_mode
    	.dc.w	addr_ind_p_a3-tbl_ea_mode
    	.dc.w	addr_ind_p_a4-tbl_ea_mode
    	.dc.w	addr_ind_p_a5-tbl_ea_mode
    	.dc.w	addr_ind_p_a6-tbl_ea_mode
    	.dc.w	addr_ind_p_a7-tbl_ea_mode

    	.dc.w	addr_ind_m_a0-tbl_ea_mode
    	.dc.w	addr_ind_m_a1-tbl_ea_mode
    	.dc.w	addr_ind_m_a2-tbl_ea_mode
    	.dc.w	addr_ind_m_a3-tbl_ea_mode
    	.dc.w	addr_ind_m_a4-tbl_ea_mode
    	.dc.w	addr_ind_m_a5-tbl_ea_mode
    	.dc.w	addr_ind_m_a6-tbl_ea_mode
    	.dc.w	addr_ind_m_a7-tbl_ea_mode

    	.dc.w	addr_ind_disp_a0-tbl_ea_mode
    	.dc.w	addr_ind_disp_a1-tbl_ea_mode
    	.dc.w	addr_ind_disp_a2-tbl_ea_mode
    	.dc.w	addr_ind_disp_a3-tbl_ea_mode
    	.dc.w	addr_ind_disp_a4-tbl_ea_mode
    	.dc.w	addr_ind_disp_a5-tbl_ea_mode
    	.dc.w	addr_ind_disp_a6-tbl_ea_mode
    	.dc.w	addr_ind_disp_a7-tbl_ea_mode

    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode
    	.dc.w	_addr_ind_ext-tbl_ea_mode

    	.dc.w	abs_short-tbl_ea_mode
    	.dc.w	abs_long-tbl_ea_mode
    	.dc.w	pc_ind-tbl_ea_mode
    	.dc.w	pc_ind_ext-tbl_ea_mode
    	.dc.w	immediate-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode
    	.dc.w	tbl_ea_mode-tbl_ea_mode

    *##################################
    * Address register indirect: (An) #
    *##################################
    addr_ind_a0:
    	move.l	EXC_A0(a6),a0		* Get current a0
    	rts

    addr_ind_a1:
    	move.l	EXC_A1(a6),a0		* Get current a1
    	rts

    addr_ind_a2:
    	move.l	EXC_A2(a6),a0		* Get current a2
    	rts

    addr_ind_a3:
    	move.l	EXC_A3(a6),a0		* Get current a3
    	rts

    addr_ind_a4:
    	move.l	EXC_A4(a6),a0		* Get current a4
    	rts

    addr_ind_a5:
    	move.l	EXC_A5(a6),a0		* Get current a5
    	rts

    addr_ind_a6:
    	move.l	EXC_A6(a6),a0		* Get current a6
    	rts

    addr_ind_a7:
    	move.l	EXC_A7(a6),a0		* Get current a7
    	rts

    *####################################################
    * Address register indirect w/ postincrement: (An)+ #
    *####################################################
    addr_ind_p_a0:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A0(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A0(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$0,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a1:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A1(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A1(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$1,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a2:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A2(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A2(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$2,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a3:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A3(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A3(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$3,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a4:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A4(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A4(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$4,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a5:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A5(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A5(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$5,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a6:
    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A6(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A6(a6)		* save incremented value

    	move.l	a0,EXC_SAVVAL(a6)	* save in case of access error
    	move.b	#$6,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_p_a7:
    	move.b	#mia7_flg,SPCOND_FLG(a6)	* set "special case" flag

    	move.l	a0,d0			* copy no. bytes
    	move.l	EXC_A7(a6),a0		* load current value
    	add.l	a0,d0			* increment
    	move.l	d0,EXC_A7(a6)		* save incremented value
    	rts

    *###################################################
    * Address register indirect w/ predecrement: -(An) #
    *###################################################
    addr_ind_m_a0:
    	move.l	EXC_A0(a6),d0		* Get current a0
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A0(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$0,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a1:
    	move.l	EXC_A1(a6),d0		* Get current a1
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A1(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$1,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a2:
    	move.l	EXC_A2(a6),d0		* Get current a2
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A2(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$2,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a3:
    	move.l	EXC_A3(a6),d0		* Get current a3
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A3(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$3,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a4:
    	move.l	EXC_A4(a6),d0		* Get current a4
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A4(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$4,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a5:
    	move.l	EXC_A5(a6),d0		* Get current a5
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A5(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$5,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a6:
    	move.l	EXC_A6(a6),d0		* Get current a6
    	move.l	d0,EXC_SAVVAL(a6)	* save in case of access error
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A6(a6)		* Save decr value
    	move.l	d0,a0

    	move.b	#$6,EXC_SAVREG(a6)	* save regno, too
    	move.b	#restore_flg,SPCOND_FLG(a6)	* set flag
    	rts

    addr_ind_m_a7:
    	move.b	#mda7_flg,SPCOND_FLG(a6)	* set "special case" flag

    	move.l	EXC_A7(a6),d0		* Get current a7
    	sub.l	a0,d0			* Decrement
    	move.l	d0,EXC_A7(a6)		* Save decr value
    	move.l	d0,a0
    	rts

    *#######################################################
    * Address register indirect w/ displacement: (d16, An) #
    *#######################################################
    addr_ind_disp_a0:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A0(a6),a0		* a0 + d16
    	rts

    addr_ind_disp_a1:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A1(a6),a0		* a1 + d16
    	rts

    addr_ind_disp_a2:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A2(a6),a0		* a2 + d16
    	rts

    addr_ind_disp_a3:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A3(a6),a0		* a3 + d16
    	rts

    addr_ind_disp_a4:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A4(a6),a0		* a4 + d16
    	rts

    addr_ind_disp_a5:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A5(a6),a0		* a5 + d16
    	rts

    addr_ind_disp_a6:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A6(a6),a0		* a6 + d16
    	rts

    addr_ind_disp_a7:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement
    	add.l	EXC_A7(a6),a0		* a7 + d16
    	rts

    *#######################################################################
    * Address register indirect w/ index(8-bit displacement): (dn, An, Xn) #
    *    "       "         "    w/   "  (base displacement): (bd, An, Xn)  #
    * Memory indirect postindexed: ([bd, An], Xn, od)		       #
    * Memory indirect preindexed: ([bd, An, Xn], od)		       #
    *#######################################################################
    _addr_ind_ext:
    	move.l	d1,-(sp)

    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word		* fetch extword in d0

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.l	(sp)+,d1

    	move.l	(EXC_AREGS,a6,d1.w*4),a0	* put base in a0

    	btst	#$8,d0
    	beq.b	addr_ind_index_8bit	* for ext word or not?

    	movem.l	d2-d5,-(sp)		* save d2-d5

    	move.l	d0,d5			* put extword in d5
    	move.l	a0,d3			* put base in d3

    	bra.l	calc_mem_ind		* calc memory indirect

    addr_ind_index_8bit:
    	move.l	d2,-(sp)		* save old d2

    	move.l	d0,d1
    	rol.w	#$4,d1
    	andi.w	#$f,d1			* extract index regno

    	move.l	(EXC_DREGS,a6,d1.w*4),d1	* fetch index reg value

    	btst	#$b,d0			* is it word or long?
    	bne.b	aii8_long
    	ext.l	d1			* sign extend word index
    aii8_long:
    	move.l	d0,d2
    	rol.w	#$7,d2
    	andi.l	#$3,d2			* extract scale value

    	lsl.l	d2,d1			* shift index by scale

    	extb.l	d0			* sign extend displacement
    	add.l	d1,d0			* index + disp
    	add.l	d0,a0			* An + (index + disp)

    	move.l	(sp)+,d2		* restore old d2
    	rts

    *#####################
    * Immediate: #<data> #
    *########################################################################
    * word, long: <ea> of the data is the current extension word		#
    * 	pointer value. new extension word pointer is simply the old	#
    * 	plus the number of bytes in the data type(2 or 4).		#
    *########################################################################
    immediate:
    	move.b	#immed_flg,SPCOND_FLG(a6)	* set immediate flag

    	move.l	EXC_EXTWPTR(a6),a0	* fetch extension word ptr
    	rts

    *##########################
    * Absolute short: (XXX).W #
    *##########################
    abs_short:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word		* fetch short address

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* return <ea> in a0
    	rts

    *#########################
    * Absolute long: (XXX).L #
    *#########################
    abs_long:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$4,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_long		* fetch long address

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.l	d0,a0			* return <ea> in a0
    	rts

    *######################################################
    * Program counter indirect w/ displacement: (d16, PC) #
    *######################################################
    pc_ind:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word		* fetch word displacement

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.w	d0,a0			* sign extend displacement

    	add.l	EXC_EXTWPTR(a6),a0	* pc + d16

    * _imem_read_word() increased the extwptr by 2. need to adjust here.
    	subq.l	#$2,a0			* adjust <ea>

    	rts

    *#########################################################
    * PC indirect w/ index(8-bit displacement): (d8, PC, An) #
    * "     "     w/   "  (base displacement): (bd, PC, An)  #
    * PC memory indirect postindexed: ([bd, PC], Xn, od)     #
    * PC memory indirect preindexed: ([bd, PC, Xn], od)      #
    *#########################################################
    pc_ind_ext:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word		* fetch ext word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.l	EXC_EXTWPTR(a6),a0	* put base in a0
    	subq.l	#$2,a0			* adjust base

    	btst	#$8,d0			* is disp only 8 bits?
    	beq.b	pc_ind_index_8bit	* yes

    * the indexed addressing mode uses a base displacement of size
    * word or long
    	movem.l	d2-d5,-(sp)		* save d2-d5

    	move.l	d0,d5			* put extword in d5
    	move.l	a0,d3			* put base in d3

    	bra.l	calc_mem_ind		* calc memory indirect

    pc_ind_index_8bit:
    	move.l	d2,-(sp)		* create a temp register

    	move.l	d0,d1			* make extword copy
    	rol.w	#$4,d1			* rotate reg num into place
    	andi.w	#$f,d1			* extract register number

    	move.l	(EXC_DREGS,a6,d1.w*4),d1	* fetch index reg value

    	btst	#$b,d0			* is index word or long?
    	bne.b	pii8_long		* long
    	ext.l	d1			* sign extend word index
    pii8_long:
    	move.l	d0,d2			* make extword copy
    	rol.w	#$7,d2			* rotate scale value into place
    	andi.l	#$3,d2			* extract scale value

    	lsl.l	d2,d1			* shift index by scale

    	extb.l	d0			* sign extend displacement
    	add.l	d1,d0			* index + disp
    	add.l	d0,a0			* An + (index + disp)

    	move.l	(sp)+,d2		* restore temp register

    	rts

    * a5 = exc_extwptr	(global to uaeh)
    * a4 = exc_opword	(global to uaeh)
    * a3 = exc_dregs	(global to uaeh)

    * d2 = index		(internal "     "    )
    * d3 = base		(internal "     "    )
    * d4 = od		(internal "     "    )
    * d5 = extword		(internal "     "    )
    calc_mem_ind:
    	btst	#$6,d5			* is the index suppressed?
    	beq.b	calc_index
    	clr.l	d2			* yes, so index = 0
    	bra.b	base_supp_ck
    calc_index:
    	bfextu	d5{#16:#4},d2
    	move.l	(EXC_DREGS,a6,d2.w*4),d2
    	btst	#$b,d5			* is index word or long?
    	bne.b	no_ext
    	ext.l	d2
    no_ext:
    	bfextu	d5{#21:#2},d0
    	lsl.l	d0,d2
    base_supp_ck:
    	btst	#$7,d5			* is the bd suppressed?
    	beq.b	no_base_sup
    	clr.l	d3
    no_base_sup:
    	bfextu	d5{#26:#2},d0	* get bd size
    *	beq.l		_error			# if (size == 0) it's reserved
    	cmpi.b	#2,d0
    	blt.b	no_bd
    	beq.b	get_word_bd

    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$4,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_long

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	bra.b	chk_ind
    get_word_bd:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	ext.l	d0			* sign extend bd

    chk_ind:
    	add.l	d0,d3			* base += bd
    no_bd:
    	bfextu	d5{#30:#2},d0		* is od suppressed?
    	beq.w	aii_bd
    	cmpi.b	#$2,d0
    	blt.b	null_od
    	beq.b	word_od

    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$4,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_long

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	bra.b	add_them

    word_od:
    	move.l	EXC_EXTWPTR(a6),a0	* fetch instruction addr
    	addq.l	#$2,EXC_EXTWPTR(a6)	* incr instruction ptr
    	bsr.l	_imem_read_word

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	ext.l	d0			* sign extend od
    	bra.b	add_them

    null_od:
    	clr.l	d0
    add_them:
    	move.l	d0,d4
    	btst	#$2,d5			* pre or post indexing?
    	beq.b	pre_indexed

    	move.l	d3,a0
    	bsr.l	_dmem_read_long

    	tst.l	d1			* dfetch error?
    	bne.b	calc_ea_err		* yes

    	add.l	d2,d0			* <ea> += index
    	add.l	d4,d0			* <ea> += od
    	bra.b	done_ea

    pre_indexed:
    	add.l	d2,d3			* preindexing
    	move.l	d3,a0
    	bsr.l	_dmem_read_long

    	tst.l	d1			* ifetch error?
    	bne.b	calc_ea_err		* yes

    	add.l	d4,d0			* ea += od
    	bra.b	done_ea

    aii_bd:
    	add.l	d2,d3			* ea = (base + bd) + index
    	move.l	d3,d0
    done_ea:
    	move.l	d0,a0

    	movem.l	(sp)+,d2-d5		* restore d2-d5
    	rts

    * if dmem_read_long() returns a fail message in d1, the package
    * must create an access error frame. here, we pass a skeleton fslw
    * and the failing address to the routine that creates the new frame.
    * FSLW:
    * 	read = true
    * 	size = longword
    *	TM = data
    * 	software emulation error = true
    calc_ea_err:
    	move.l	d3,a0			* pass failing address
    	move.l	#$01010001,d0		* pass fslw
    	bra.l	isp_dacc

    *########################################################################
    * XDEF **************************************************************** #
    * 	_moveperipheral(): routine to emulate movep instruction		#
    *									#
    * XREF **************************************************************** #
    *	_dmem_read_byte() - read byte from memory			#
    *	_dmem_write_byte() - write byte to memory			#
    *	isp_dacc() - handle data access error exception			#
    *									#
    * INPUT *************************************************************** #
    *	none								#
    *									#
    * OUTPUT ************************************************************** #
    *	If exiting through isp_dacc...					#
    *		a0 = failing address					#
    *		d0 = FSLW						#
    *	else								#
    *		none							#
    *									#
    * ALGORITHM ***********************************************************	#
    *	Decode the movep instruction words stored at EXC_OPWORD and	#
    * either read or write the required bytes from/to memory. Use the	#
    * _dmem_{read,write}_byte() routines. If one of the memory routines	#
    * returns a failing value, we must pass the failing address and	a FSLW	#
    * to the _isp_dacc() routine.						#
    *	Since this instruction is used to access peripherals, make sure	#
    * to only access the required bytes.					#
    *									#
    *########################################################################

    *##########################
    * movep.(w,l)	Dx,(d,Ay) #
    * movep.(w,l)	(d,Ay),Dx #
    *##########################
    	global	_moveperipheral
    _moveperipheral:
    	move.w	EXC_OPWORD(a6),d1	* fetch the opcode word

    	move.b	d1,d0
    	andi.w	#$7,d0			* extract Ay from opcode word

    	move.l	(EXC_AREGS,a6,d0.w*4),a0	* fetch ay

    	add.w	EXC_EXTWORD(a6),a0	* add: an + sgn_ext(disp)

    	btst	#$7,d1			* (reg 2 mem) or (mem 2 reg)
    	beq.w	mem2reg

    * reg2mem: fetch dx, then write it to memory
    reg2mem:
    	move.w	d1,d0
    	rol.w	#$7,d0
    	andi.w	#$7,d0			* extract Dx from opcode word

    	move.l	(EXC_DREGS,a6,d0.w*4),d0	* fetch dx

    	btst	#$6,d1			* word or long operation?
    	beq.b	r2mwtrans

    * a0 = dst addr
    * d0 = Dx
    r2mltrans:
    	move.l	d0,d2			* store data
    	move.l	a0,a2			* store addr
    	rol.l	#$8,d2
    	move.l	d2,d0

    	bsr.l	_dmem_write_byte	* os  : write hi

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	add.w	#$2,a2			* incr addr
    	move.l	a2,a0
    	rol.l	#$8,d2
    	move.l	d2,d0

    	bsr.l	_dmem_write_byte	* os  : write lo

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	add.w	#$2,a2			* incr addr
    	move.l	a2,a0
    	rol.l	#$8,d2
    	move.l	d2,d0

    	bsr.l	_dmem_write_byte	* os  : write lo

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	add.w	#$2,a2			* incr addr
    	move.l	a2,a0
    	rol.l	#$8,d2
    	move.l	d2,d0

    	bsr.l	_dmem_write_byte	* os  : write lo

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	rts

    * a0 = dst addr
    * d0 = Dx
    r2mwtrans:
    	move.l	d0,d2			* store data
    	move.l	a0,a2			* store addr
    	lsr.w	#$8,d0

    	bsr.l	_dmem_write_byte	* os  : write hi

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	add.w	#$2,a2
    	move.l	a2,a0
    	move.l	d2,d0

    	bsr.l	_dmem_write_byte	* os  : write lo

    	tst.l	d1			* dfetch error?
    	bne.w	movp_write_err		* yes

    	rts

    * mem2reg: read bytes from memory.
    * determines the dest register, and then writes the bytes into it.
    mem2reg:
    	btst	#$6,d1			* word or long operation?
    	beq.b	m2rwtrans

    * a0 = dst addr
    m2rltrans:
    	move.l	a0,a2			* store addr

    	bsr.l	_dmem_read_byte		* read first byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	move.l	d0,d2

    	add.w	#$2,a2			* incr addr by 2 bytes
    	move.l	a2,a0

    	bsr.l	_dmem_read_byte		* read second byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	lsl.w	#$8,d2
    	move.b	d0,d2			* append bytes

    	add.w	#$2,a2			* incr addr by 2 bytes
    	move.l	a2,a0

    	bsr.l	_dmem_read_byte		* read second byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	lsl.l	#$8,d2
    	move.b	d0,d2			* append bytes

    	add.w	#$2,a2			* incr addr by 2 bytes
    	move.l	a2,a0

    	bsr.l	_dmem_read_byte		* read second byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	lsl.l	#$8,d2
    	move.b	d0,d2			* append bytes

    	move.b	EXC_OPWORD(a6),d1
    	lsr.b	#$1,d1
    	andi.w	#$7,d1			* extract Dx from opcode word

    	move.l	d2,(EXC_DREGS,a6,d1.w*4)	* store dx

    	rts

    * a0 = dst addr
    m2rwtrans:
    	move.l	a0,a2			* store addr

    	bsr.l	_dmem_read_byte		* read first byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	move.l	d0,d2

    	add.w	#$2,a2			* incr addr by 2 bytes
    	move.l	a2,a0

    	bsr.l	_dmem_read_byte		* read second byte

    	tst.l	d1			* dfetch error?
    	bne.w	movp_read_err		* yes

    	lsl.w	#$8,d2
    	move.b	d0,d2			* append bytes

    	move.b	EXC_OPWORD(a6),d1
    	lsr.b	#$1,d1
    	andi.w	#$7,d1			* extract Dx from opcode word

    	move.w	d2,(EXC_DREGS+2,a6,d1.w*4)	* store dx

    	rts

    * if dmem_{read,write}_byte() returns a fail message in d1, the package
    * must create an access error frame. here, we pass a skeleton fslw
    * and the failing address to the routine that creates the new frame.
    * FSLW:
    * 	write = true
    *	size = byte
    *	TM = data
    *	software emulation error = true
    movp_write_err:
    	move.l	a2,a0			* pass failing address
    	move.l	#$00a10001,d0		* pass fslw
    	bra.l	isp_dacc

    * FSLW:
    * 	read = true
    *	size = byte
    *	TM = data
    *	software emulation error = true
    movp_read_err:
    	move.l	a2,a0			* pass failing address
    	move.l	#$01210001,d0		* pass fslw
    	bra.l	isp_dacc

    *########################################################################
    * XDEF ****************************************************************	#
    * 	_chk2_cmp2(): routine to emulate chk2/cmp2 instructions		#
    *									#
    * XREF ****************************************************************	#
    *	_calc_ea(): calculate effective address				#
    *	_dmem_read_long(): read operands				#
    * 	_dmem_read_word(): read operands				#
    *	isp_dacc(): handle data access error exception			#
    *									#
    * INPUT ***************************************************************	#
    *	none								#
    *									#
    * OUTPUT **************************************************************	#
    *	If exiting through isp_dacc...					#
    *		a0 = failing address					#
    *		d0 = FSLW						#
    *	else								#
    * 		none							#
    *									#
    * ALGORITHM ***********************************************************	#
    *	First, calculate the effective address, then fetch the byte,	#
    * word, or longword sized operands. Then, in the interest of 		#
    * simplicity, all operands are converted to longword size whether the 	#
    * operation is byte, word, or long. The bounds are sign extended 	#
    * accordingly. If Rn is a data regsiter, Rn is also sign extended. If 	#
    * Rn is an address register, it need not be sign extended since the 	#
    * full register is always used.						#
    *	The comparisons are made and the condition codes calculated.	#
    * If the instruction is chk2 and the Rn value is out-of-bounds, set	#
    * the ichk_flg in SPCOND_FLG.						#
    *	If the memory fetch returns a failing value, pass the failing 	#
    * address and FSLW to the isp_dacc() routine.				#
    *									#
    *########################################################################

    	global	_chk2_cmp2
    _chk2_cmp2:

    * passing size parameter doesn't matter since chk2 & cmp2 can't do
    * either predecrement, postincrement, or immediate.
    	bsr.l	_calc_ea		* calculate <ea>

    	move.b	EXC_EXTWORD(a6),d0	* fetch hi extension word
    	rol.b	#$4,d0			* rotate reg bits into lo
    	andi.w	#$f,d0			* extract reg bits

    	move.l	(EXC_DREGS,a6,d0.w*4),d2	* get regval

    	cmpi.b	#$2,EXC_OPWORD(a6)	* what size is operation?
    	blt.b	chk2_cmp2_byte		* size == byte
    	beq.b	chk2_cmp2_word		* size == word

    * the bounds are longword size. call routine to read the lower
    * bound into d0 and the higher bound into d1.
    chk2_cmp2_long:
    	move.l	a0,a2			* save copy of <ea>
    	bsr.l	_dmem_read_long		* fetch long lower bound

    	tst.l	d1			* dfetch error?
    	bne.w	chk2_cmp2_err_l		* yes

    	move.l	d0,d3			* save long lower bound
    	addq.l	#$4,a2
    	move.l	a2,a0			* pass <ea> of long upper bound
    	bsr.l	_dmem_read_long		* fetch long upper bound

    	tst.l	d1			* dfetch error?
    	bne.w	chk2_cmp2_err_l		* yes

    	move.l	d0,d1			* long upper bound in d1
    	move.l	d3,d0			* long lower bound in d0
    	bra.w	chk2_cmp2_compare	* go do the compare emulation

    * the bounds are word size. fetch them in one subroutine call by
    * reading a longword. sign extend both. if it's a data operation,
    * sign extend Rn to long, also.
    chk2_cmp2_word:
    	move.l	a0,a2
    	bsr.l	_dmem_read_long		* fetch 2 word bounds

    	tst.l	d1			* dfetch error?
    	bne.w	chk2_cmp2_err_l		* yes

    	move.w	d0,d1			* place hi in %d1
    	swap	d0			* place lo in %d0

    	ext.l	d0			* sign extend lo bnd
    	ext.l	d1			* sign extend hi bnd

    	btst	#$7,EXC_EXTWORD(a6)	* address compare?
    	bne.w	chk2_cmp2_compare	* yes; don't sign extend

    * operation is a data register compare.
    * sign extend word to long so we can do simple longword compares.
    	ext.l	d2			* sign extend data word
    	bra.w	chk2_cmp2_compare	* go emulate compare

    * the bounds are byte size. fetch them in one subroutine call by
    * reading a word. sign extend both. if it's a data operation,
    * sign extend Rn to long, also.
    chk2_cmp2_byte:
    	move.l	a0,a2
    	bsr.l	_dmem_read_word		* fetch 2 byte bounds

    	tst.l	d1			* dfetch error?
    	bne.w	chk2_cmp2_err_w		* yes

    	move.b	d0,d1			* place hi in %d1
    	lsr.w	#$8,d0			* place lo in %d0

    	extb.l	d0			* sign extend lo bnd
    	extb.l	d1			* sign extend hi bnd

    	btst	#$7,EXC_EXTWORD(a6)	* address compare?
    	bne.b	chk2_cmp2_compare	* yes; don't sign extend

    * operation is a data register compare.
    * sign extend byte to long so we can do simple longword compares.
    	extb.l	d2			* sign extend data byte

    *
    * To set the ccodes correctly:
    * 	(1) save 'Z' bit from (Rn - lo)
    *	(2) save 'Z' and 'N' bits from ((hi - lo) - (Rn - hi))
    *	(3) keep 'X', 'N', and 'V' from before instruction
    *	(4) combine ccodes
    *
    chk2_cmp2_compare:
    	sub.l	d0,d2			* (Rn - lo)
    	move.w	ccr,d3			* fetch resulting ccodes
    	andi.b	#$4,d3			* keep 'Z' bit
    	sub.l	d0,d1			* (hi - lo)
    	cmp.l	d2,d1			* ((hi - lo) - (Rn - hi))

    	move.w	ccr,d4			* fetch resulting ccodes
    	or.b	d4,d3			* combine w/ earlier ccodes
    	andi.b	#$5,d3			* keep 'Z' and 'N'

    	move.w	EXC_CC(a6),d4		* fetch old ccodes
    	andi.b	#$1a,d4			* keep 'X','N','V' bits
    	or.b	d3,d4			* insert new ccodes
    	move.w	d4,EXC_CC(a6)		* save new ccodes

    	btst	#$3,EXC_EXTWORD(a6)	* separate chk2,cmp2
    	bne.b	chk2_finish		* it's a chk2

    	rts

    * this code handles the only difference between chk2 and cmp2. chk2 would
    * have trapped out if the value was out of bounds. we check this by seeing
    * if the 'N' bit was set by the operation.
    chk2_finish:
    	btst	#$0,d4			* is 'N' bit set?
    	bne.b	chk2_trap		* yes;chk2 should trap
    	rts
    chk2_trap:
    	move.b	#ichk_flg,SPCOND_FLG(a6)	* set "special case" flag
    	rts

    * if dmem_read_{long,word}() returns a fail message in d1, the package
    * must create an access error frame. here, we pass a skeleton fslw
    * and the failing address to the routine that creates the new frame.
    * FSLW:
    *	read = true
    *	size = longword
    *	TM = data
    * 	software emulation error = true
    chk2_cmp2_err_l:
    	move.l	a2,a0			* pass failing address
    	move.l	#$01010001,d0		* pass fslw
    	bra.l	isp_dacc

    * FSLW:
    *	read = true
    *	size = word
    *	TM = data
    * 	software emulation error = true
    chk2_cmp2_err_w:
    	move.l	a2,a0			* pass failing address
    	move.l	#$01410001,d0		* pass fslw
    	bra.l	isp_dacc

    *########################################################################
    * XDEF ****************************************************************	#
    * 	_div64(): routine to emulate div{u,s}.l <ea>,Dr:Dq		#
    *							64/32->32r:32q	#
    *									#
    * XREF ****************************************************************	#
    *	_calc_ea() - calculate effective address			#
    * 	isp_iacc() - handle instruction access error exception		#
    *	isp_dacc() - handle data access error exception			#
    *	isp_restore() - restore An on access error w/ -() or ()+	#
    *									#
    * INPUT ***************************************************************	#
    *	none								#
    *									#
    * OUTPUT **************************************************************	#
    * 	If exiting through isp_dacc...					#
    *		a0 = failing address					#
    * 		d0 = FSLW						#
    *	else								#
    *		none							#
    *									#
    * ALGORITHM ***********************************************************	#
    * 	First, decode the operand location. If it's in Dn, fetch from	#
    * the stack. If it's in memory, use _calc_ea() to calculate the 	#
    * effective address. Use _dmem_read_long() to fetch at that address.	#
    * Unless the operand is immediate data. Then use _imem_read_long().	#
    * Send failures to isp_dacc() or isp_iacc() as appropriate.		#
    *	If the operands are signed, make them unsigned and save	the 	#
    * sign info for later. Separate out special cases like divide-by-zero	#
    * or 32-bit divides if possible. Else, use a special math algorithm	#
    * to calculate the result. 						#
    *	Restore sign info if signed instruction. Set the condition 	#
    * codes. Set idbyz_flg in SPCOND_FLG if divisor was zero. Store the 	#
    * quotient and remainder in the appropriate data registers on the stack.#
    *									#
    *########################################################################

    NDIVISOR	set	EXC_TEMP+$0
    NDIVIDEND	set	EXC_TEMP+$1
    NDRSAVE	set	EXC_TEMP+$2
    NDQSAVE	set	EXC_TEMP+$4
    DDSECOND	set	EXC_TEMP+$6
    DDQUOTIENT	set	EXC_TEMP+$8
    DDNORMAL	set	EXC_TEMP+$c

    	global	_div64
    *############
    * div(u,s)l #
    *############
    _div64:
    	move.b	EXC_OPWORD+1(a6),d0
    	andi.b	#$38,d0			* extract src mode

    	bne.w	dcontrolmodel_s		* %dn dest or control mode?

    	move.b	EXC_OPWORD+1(a6),d0	* extract Dn from opcode
    	andi.w	#$7,d0
    	move.l	(EXC_DREGS,a6,d0.w*4),d7	* fetch divisor from register

    dgotsrcl:
    	beq.w	div64eq0		* divisor is = 0!!!

    	move.b	EXC_EXTWORD+1(a6),d0	* extract Dr from extword
    	move.b	EXC_EXTWORD(a6),d1	* extract Dq from extword
    	andi.w	#$7,d0
    	lsr.b	#$4,d1
    	andi.w	#$7,d1
    	move.w	d0,NDRSAVE(a6)		* save Dr for later
    	move.w	d1,NDQSAVE(a6)		* save Dq for later

    * fetch %dr and %dq directly off stack since all regs are saved there
    	move.l	(EXC_DREGS,a6,d0.w*4),d5	* get dividend hi
    	move.l	(EXC_DREGS,a6,d1.w*4),d6	* get dividend lo

    * separate signed and unsigned divide
    	btst	#$3,EXC_EXTWORD(a6)	* signed or unsigned?
    	beq.b	dspecialcases		* use positive divide

    * save the sign of the divisor
    * make divisor unsigned if it's negative
    	tst.l	d7			* chk sign of divisor
    	slt	NDIVISOR(a6)		* save sign of divisor
    	bpl.b	dsgndividend
    	neg.l	d7			* complement negative divisor

    * save the sign of the dividend
    * make dividend unsigned if it's negative
    dsgndividend:
    	tst.l	d5			* chk sign of hi(dividend)
    	slt	NDIVIDEND(a6)		* save sign of dividend
    	bpl.b	dspecialcases

    	move.w	#$0,ccr			* clear 'X' cc bit
    	negx.l	d6			* complement signed dividend
    	negx.l	d5

    * extract some special cases:
    * 	- is (dividend == 0) ?
    *	- is (hi(dividend) == 0 && (divisor <= lo(dividend))) ? (32-bit div)
    dspecialcases:
    	tst.l	d5			* is (hi(dividend) == 0)
    	bne.b	dnormaldivide		* no, so try it the long way

    	tst.l	d6			* is (lo(dividend) == 0), too
    	beq.w	ddone			* yes, so (dividend == 0)

    	cmp.l	d6,d7			* is (divisor <= lo(dividend))
    	bls.b	d32bitdivide		* yes, so use 32 bit divide

    	exg	d5,d6			* q = 0, r = dividend
    	bra.w	divfinish		* can't divide, we're done.

    d32bitdivide:
    	divul.l	d7,d5:d6		* it's only a 32/32 bit div!

    	bra.b	divfinish

    dnormaldivide:
    * last special case:
    * 	- is hi(dividend) >= divisor ? if yes, then overflow
    	cmp.l	d5,d7
    	bls.b	ddovf			* answer won't fit in 32 bits

    * perform the divide algorithm:
    	bsr.l	dclassical		* do int divide

    * separate into signed and unsigned finishes.
    divfinish:
    	btst	#$3,EXC_EXTWORD(a6)	* do divs, divu separately
    	beq.b	ddone			* divu has no processing!!!

    * it was a divs.l, so ccode setting is a little more complicated...
    	tst.b	NDIVIDEND(a6)		* remainder has same sign
    	beq.b	dcc			* as dividend.
    	neg.l	d5			* sgn(rem) = sgn(dividend)
    dcc:
    	move.b	NDIVISOR(a6),d0
    	eor.b	d0,NDIVIDEND(a6)	* chk if quotient is negative
    	beq.b	dqpos			* branch to quot positive

    * 0x80000000 is the largest number representable as a 32-bit negative
    * number. the negative of 0x80000000 is 0x80000000.
    	cmpi.l	#$80000000,d6		* will (-quot) fit in 32 bits?
    	bhi.b	ddovf

    	neg.l	d6			* make (-quot) 2's comp

    	bra.b	ddone

    dqpos:
    	btst	#$1f,d6			* will (+quot) fit in 32 bits?
    	bne.b	ddovf

    ddone:
    * at this point, result is normal so ccodes are set based on result.
    	move.w	EXC_CC(a6),ccr
    	tst.l	d6			* set %ccode bits
    	move.w	ccr,EXC_CC(a6)

    	move.w	NDRSAVE(a6),d0		* get Dr off stack
    	move.w	NDQSAVE(a6),d1		* get Dq off stack

    * if the register numbers are the same, only the quotient gets saved.
    * so, if we always save the quotient second, we save ourselves a cmp&beq
    	move.l	d5,(EXC_DREGS,a6,d0.w*4)	* save remainder
    	move.l	d6,(EXC_DREGS,a6,d1.w*4)	* save quotient

    	rts

    ddovf:
    	bset	#$1,EXC_CC+1(a6)	* 'V' set on overflow
    	bclr	#$0,EXC_CC+1(a6)	* 'C' cleared on overflow

    	rts

    div64eq0:
    	andi.b	#$1e,EXC_CC+1(a6)	* clear 'C' bit on divbyzero
    	ori.b	#idbyz_flg,SPCOND_FLG(a6)	* set "special case" flag
    	rts

    *##########################################################################
    *########################################################################
    * This routine uses the 'classical' Algorithm D from Donald Knuth's	#
    * Art of Computer Programming, vol II, Seminumerical Algorithms.	#
    * For this implementation b=2**16, and the target is U1U2U3U4/V1V2,	#
    * where U,V are words of the quadword dividend and longword divisor,	#
    * and U1, V1 are the most significant words.				#
    * 									#
    * The most sig. longword of the 64 bit dividend must be in %d5, least 	#
    * in %d6. The divisor must be in the variable ddivisor, and the		#
    * signed/unsigned flag ddusign must be set (0=unsigned,1=signed).	#
    * The quotient is returned in %d6, remainder in %d5, unless the		#
    * v (overflow) bit is set in the saved %ccr. If overflow, the dividend	#
    * is unchanged.								#
    *########################################################################
    dclassical:
    * if the divisor msw is 0, use simpler algorithm then the full blown
    * one at ddknuth:

    	cmpi.l	#$ffff,d7
    	bhi.b	ddknuth			* go use D. Knuth algorithm

    * Since the divisor is only a word (and larger than the mslw of the dividend),
    * a simpler algorithm may be used :
    * In the general case, four quotient words would be created by
    * dividing the divisor word into each dividend word. In this case,
    * the first two quotient words must be zero, or overflow would occur.
    * Since we already checked this case above, we can treat the most significant
    * longword of the dividend as (0) remainder (see Knuth) and merely complete
    * the last two divisions to get a quotient longword and word remainder:

    	clr.l	d1
    	swap	d5			* same as r*b if previous step rqd
    	swap	d6			* get u3 to lsw position
    	move.w	d6,d5			* rb + u3

    	divu.w	d7,d5

    	move.w	d5,d1			* first quotient word
    	swap	d6			* get u4
    	move.w	d6,d5			* rb + u4

    	divu.w	d7,d5

    	swap	d1
    	move.w	d5,d1			* 2nd quotient 'digit'
    	clr.w	d5
    	swap	d5			* now remainder
    	move.l	d1,d6			* and quotient

    	rts

    ddknuth:
    * In this algorithm, the divisor is treated as a 2 digit (word) number
    * which is divided into a 3 digit (word) dividend to get one quotient
    * digit (word). After subtraction, the dividend is shifted and the
    * process repeated. Before beginning, the divisor and quotient are
    * 'normalized' so that the process of estimating the quotient digit
    * will yield verifiably correct results..

    	clr.l	DDNORMAL(a6)		* count of shifts for normalization
    	clr.b	DDSECOND(a6)		* clear flag for quotient digits
    	clr.l	d1			* %d1 will hold trial quotient
    ddnchk:
    	btst	#31,d7			* must we normalize? first word of
    	bne.b	ddnormalized		* divisor (V1) must be >= 65536/2
    	addq.l	#$1,DDNORMAL(a6)	* count normalization shifts
    	lsl.l	#$1,d7			* shift the divisor
    	lsl.l	#$1,d6			* shift u4,u3 with overflow to u2
    	roxl.l	#$1,d5			* shift u1,u2
    	bra.w	ddnchk
    ddnormalized:

    * Now calculate an estimate of the quotient words (msw first, then lsw).
    * The comments use subscripts for the first quotient digit determination.
    	move.l	d7,d3			* divisor
    	move.l	d5,d2			* dividend mslw
    	swap	d2
    	swap	d3
    	cmp.w	d3,d2			* V1 = U1 ?
    	bne.b	ddqcalc1
    	move.w	#$ffff,d1		* use max trial quotient word
    	bra.b	ddadj0
    ddqcalc1:
    	move.l	d5,d1

    	divu.w	d3,d1			* use quotient of mslw/msw

    	andi.l	#$0000ffff,d1		* zero any remainder
    ddadj0:

    * now test the trial quotient and adjust. This step plus the
    * normalization assures (according to Knuth) that the trial
    * quotient will be at worst 1 too large.
    	move.l	d6,-(sp)
    	clr.w	d6			* word u3 left
    	swap	d6			* in lsw position
    ddadj1:	move.l	d7,d3
    	move.l	d1,d2
    	mulu.w	d7,d2			* V2q
    	swap	d3
    	mulu.w	d1,d3			* V1q
    	move.l	d5,d4			* U1U2
    	sub.l	d3,d4			* U1U2 - V1q

    	swap	d4

    	move.w	d4,d0
    	move.w	d6,d4			* insert lower word (U3)

    	tst.w	d0			* is upper word set?
    	bne.w	ddadjd1

    *	add.l		%d6, %d4		# (U1U2 - V1q) + U3

    	cmp.l	d4,d2
    	bls.b	ddadjd1			* is V2q > (U1U2-V1q) + U3 ?
    	subq.l	#$1,d1			* yes, decrement and recheck
    	bra.b	ddadj1
    ddadjd1:
    * now test the word by multiplying it by the divisor (V1V2) and comparing
    * the 3 digit (word) result with the current dividend words
    	move.l	d5,-(sp)		* save %d5 (%d6 already saved)
    	move.l	d1,d6
    	swap	d6			* shift answer to ms 3 words
    	move.l	d7,d5
    	bsr.l	dmm2
    	move.l	d5,d2			* now %d2,%d3 are trial*divisor
    	move.l	d6,d3
    	move.l	(sp)+,d5		* restore dividend
    	move.l	(sp)+,d6
    	sub.l	d3,d6
    	subx.l	d2,d5			* subtract double precision
    	bcc.l	dd2nd			* no carry, do next quotient digit
    	subq.l	#$1,d1			* q is one too large
    * need to add back divisor longword to current ms 3 digits of dividend
    * - according to Knuth, this is done only 2 out of 65536 times for random
    * divisor, dividend selection.
    	clr.l	d2
    	move.l	d7,d3
    	swap	d3
    	clr.w	d3			* %d3 now ls word of divisor
    	add.l	d3,d6			* aligned with 3rd word of dividend
    	addx.l	d2,d5
    	move.l	d7,d3
    	clr.w	d3			* %d3 now ms word of divisor
    	swap	d3			* aligned with 2nd word of dividend
    	add.l	d3,d5
    dd2nd:
    	tst.b	DDSECOND(a6)		* both q words done?
    	bne.b	ddremain
    * first quotient digit now correct. store digit and shift the
    * (subtracted) dividend
    	move.w	d1,DDQUOTIENT(a6)
    	clr.l	d1
    	swap	d5
    	swap	d6
    	move.w	d6,d5
    	clr.w	d6
    	st	DDSECOND(a6)		* second digit
    	bra.w	ddnormalized
    ddremain:
    * add 2nd word to quotient, get the remainder.
    	move.w	d1,DDQUOTIENT+2(a6)
    * shift down one word/digit to renormalize remainder.
    	move.w	d5,d6
    	swap	d6
    	swap	d5
    	move.l	DDNORMAL(a6),d7		* get norm shift count
    	beq.b	ddrn
    	subq.l	#$1,d7			* set for loop count
    ddnlp:
    	lsr.l	#$1,d5			* shift into %d6
    	roxr.l	#$1,d6
    	dbf	d7,ddnlp
    ddrn:
    	move.l	d6,d5			* remainder
    	move.l	DDQUOTIENT(a6),d6	* quotient

    	rts
    dmm2:
    * factors for the 32X32->64 multiplication are in %d5 and %d6.
    * returns 64 bit result in %d5 (hi) %d6(lo).
    * destroys %d2,%d3,%d4.

    * multiply hi,lo words of each factor to get 4 intermediate products
    	move.l	d6,d2
    	move.l	d6,d3
    	move.l	d5,d4
    	swap	d3
    	swap	d4
    	mulu.w	d5,d6			* %d6 <- lsw*lsw
    	mulu.w	d3,d5			* %d5 <- msw-dest*lsw-source
    	mulu.w	d4,d2			* %d2 <- msw-source*lsw-dest
    	mulu.w	d4,d3			* %d3 <- msw*msw
    * now use swap and addx to consolidate to two longwords
    	clr.l	d4
    	swap	d6
    	add.w	d5,d6			* add msw of l*l to lsw of m*l product
    	addx.w	d4,d3			* add any carry to m*m product
    	add.w	d2,d6			* add in lsw of other m*l product
    	addx.w	d4,d3			* add any carry to m*m product
    	swap	d6			* %d6 is low 32 bits of final product
    	clr.w	d5
    	clr.w	d2			* lsw of two mixed products used,
    	swap	d5			* now use msws of longwords
    	swap	d2
    	add.l	d2,d5
    	add.l	d3,d5			* %d5 now ms 32 bits of final product
    	rts

    *#########
    dcontrolmodel_s:
    	moveq.l	#LONG,d0
    	bsr.l	_calc_ea		* calc <ea>

    	cmpi.b	#immed_flg,SPCOND_FLG(a6)	* immediate addressing mode?
    	beq.b	dimmed			* yes

    	move.l	a0,a2
    	bsr.l	_dmem_read_long		* fetch divisor from <ea>

    	tst.l	d1			* dfetch error?
    	bne.b	div64_err		* yes

    	move.l	d0,d7
    	bra.w	dgotsrcl

    * we have to split out immediate data here because it must be read using
    * imem_read() instead of dmem_read(). this becomes especially important
    * if the fetch runs into some deadly fault.
    dimmed:
    	addq.l	#$4,EXC_EXTWPTR(a6)
    	bsr.l	_imem_read_long		* read immediate value

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.l	d0,d7
    	bra.w	dgotsrcl

    *#########

    * if dmem_read_long() returns a fail message in d1, the package
    * must create an access error frame. here, we pass a skeleton fslw
    * and the failing address to the routine that creates the new frame.
    * also, we call isp_restore in case the effective addressing mode was
    * (an)+ or -(an) in which case the previous "an" value must be restored.
    * FSLW:
    * 	read = true
    * 	size = longword
    *	TM = data
    * 	software emulation error = true
    div64_err:
    	bsr.l	isp_restore		* restore addr reg
    	move.l	a2,a0			* pass failing address
    	move.l	#$01010001,d0		* pass fslw
    	bra.l	isp_dacc

    *########################################################################
    * XDEF ****************************************************************	#
    *	_mul64(): routine to emulate mul{u,s}.l <ea>,Dh:Dl 32x32->64	#
    *									#
    * XREF ****************************************************************	#
    *	_calc_ea() - calculate effective address			#
    *	isp_iacc() - handle instruction access error exception		#
    * 	isp_dacc() - handle data access error exception			#
    *	isp_restore() - restore An on access error w/ -() or ()+	#
    *									#
    * INPUT ***************************************************************	#
    *	none								#
    *									#
    * OUTPUT **************************************************************	#
    * 	If exiting through isp_dacc...					#
    *		a0 = failing address					#
    *		d0 = FSLW						#
    * 	else								#
    *		none							#
    *									#
    * ALGORITHM ***********************************************************	#
    *	First, decode the operand location. If it's in Dn, fetch from	#
    * the stack. If it's in memory, use _calc_ea() to calculate the		#
    * effective address. Use _dmem_read_long() to fetch at that address.	#
    * Unless the operand is immediate data. Then use _imem_read_long().	#
    * Send failures to isp_dacc() or isp_iacc() as appropriate.		#
    *	If the operands are signed, make them unsigned and save the 	#
    * sign info for later. Perform the multiplication using 16x16->32	#
    * unsigned multiplies and "add" instructions. Store the high and low 	#
    * portions of the result in the appropriate data registers on the	#
    * stack. Calculate the condition codes, also.				#
    *									#
    *########################################################################

    *############
    * mul(u,s)l #
    *############
    	global	_mul64
    _mul64:
    	move.b	EXC_OPWORD+1(a6),d0	* extract src {mode,reg}
    	cmpi.b	#$7,d0			* is src mode Dn or other?
    	bgt.w	mul64_memop		* src is in memory

    * multiplier operand in the the data register file.
    * must extract the register number and fetch the operand from the stack.
    mul64_regop:
    	andi.w	#$7,d0			* extract Dn
    	move.l	(EXC_DREGS,a6,d0.w*4),d3	* fetch multiplier

    * multiplier is in %d3. now, extract Dl and Dh fields and fetch the
    * multiplicand from the data register specified by Dl.
    mul64_multiplicand:
    	move.w	EXC_EXTWORD(a6),d2	* fetch ext word
    	clr.w	d1			* clear Dh reg
    	move.b	d2,d1			* grab Dh
    	rol.w	#$4,d2			* align Dl byte
    	andi.w	#$7,d2			* extract Dl

    	move.l	(EXC_DREGS,a6,d2.w*4),d4	* get multiplicand

    * check for the case of "zero" result early
    	tst.l	d4			* test multiplicand
    	beq.w	mul64_zero		* handle zero separately
    	tst.l	d3			* test multiplier
    	beq.w	mul64_zero		* handle zero separately

    * multiplier is in %d3 and multiplicand is in %d4.
    * if the operation is to be signed, then the operands are converted
    * to unsigned and the result sign is saved for the end.
    	clr.b	EXC_TEMP(a6)		* clear temp space
    	btst	#$3,EXC_EXTWORD(a6)	* signed or unsigned?
    	beq.b	mul64_alg		* unsigned; skip sgn calc

    	tst.l	d3			* is multiplier negative?
    	bge.b	mul64_chk_md_sgn	* no
    	neg.l	d3			* make multiplier positive
    	ori.b	#$1,EXC_TEMP(a6)	* save multiplier sgn

    * the result sign is the exclusive or of the operand sign bits.
    mul64_chk_md_sgn:
    	tst.l	d4			* is multiplicand negative?
    	bge.b	mul64_alg		* no
    	neg.l	d4			* make multiplicand positive
    	eori.b	#$1,EXC_TEMP(a6)	* calculate correct sign

    *########################################################################
    *	63			   32				0	#
    * 	----------------------------					#
    * 	| hi(mplier) * hi(mplicand)|					#
    * 	----------------------------					#
    *		     -----------------------------			#
    *		     | hi(mplier) * lo(mplicand) |			#
    *		     -----------------------------			#
    *		     -----------------------------			#
    *		     | lo(mplier) * hi(mplicand) |			#
    *		     -----------------------------			#
    *	  |			   -----------------------------	#
    *	--|--			   | lo(mplier) * lo(mplicand) |	#
    *	  |			   -----------------------------	#
    *	========================================================	#
    *	--------------------------------------------------------	#
    *	|	hi(result)	   |	    lo(result)         |	#
    *	--------------------------------------------------------	#
    *########################################################################
    mul64_alg:
    * load temp registers with operands
    	move.l	d3,d5			* mr in %d5
    	move.l	d3,d6			* mr in %d6
    	move.l	d4,d7			* md in %d7
    	swap	d6			* hi(mr) in lo %d6
    	swap	d7			* hi(md) in lo %d7

    * complete necessary multiplies:
    	mulu.w	d4,d3			* [1] lo(mr) * lo(md)
    	mulu.w	d6,d4			* [2] hi(mr) * lo(md)
    	mulu.w	d7,d5			* [3] lo(mr) * hi(md)
    	mulu.w	d7,d6			* [4] hi(mr) * hi(md)

    * add lo portions of [2],[3] to hi portion of [1].
    * add carries produced from these adds to [4].
    * lo([1]) is the final lo 16 bits of the result.
    	clr.l	d7			* load %d7 w/ zero value
    	swap	d3			* hi([1]) <==> lo([1])
    	add.w	d4,d3			* hi([1]) + lo([2])
    	addx.l	d7,d6			*    [4]  + carry
    	add.w	d5,d3			* hi([1]) + lo([3])
    	addx.l	d7,d6			*    [4]  + carry
    	swap	d3			* lo([1]) <==> hi([1])

    * lo portions of [2],[3] have been added in to final result.
    * now, clear lo, put hi in lo reg, and add to [4]
    	clr.w	d4			* clear lo([2])
    	clr.w	d5			* clear hi([3])
    	swap	d4			* hi([2]) in lo %d4
    	swap	d5			* hi([3]) in lo %d5
    	add.l	d5,d4			*    [4]  + hi([2])
    	add.l	d6,d4			*    [4]  + hi([3])

    * unsigned result is now in {%d4,%d3}
    	tst.b	EXC_TEMP(a6)		* should result be signed?
    	beq.b	mul64_done		* no

    * result should be a signed negative number.
    * compute 2's complement of the unsigned number:
    *   -negate all bits and add 1
    mul64_neg:
    	not.l	d3			* negate lo(result) bits
    	not.l	d4			* negate hi(result) bits
    	addq.l	#1,d3			* add 1 to lo(result)
    	addx.l	d7,d4			* add carry to hi(result)

    * the result is saved to the register file.
    * for '040 compatability, if Dl == Dh then only the hi(result) is
    * saved. so, saving hi after lo accomplishes this without need to
    * check Dl,Dh equality.
    mul64_done:
    	move.l	d3,(EXC_DREGS,a6,d2.w*4)	* save lo(result)
    	move.w	#$0,ccr
    	move.l	d4,(EXC_DREGS,a6,d1.w*4)	* save hi(result)

    * now, grab the condition codes. only one that can be set is 'N'.
    * 'N' CAN be set if the operation is unsigned if bit 63 is set.
    	move.w	ccr,d7			* fetch %ccr to see if 'N' set
    	andi.b	#$8,d7			* extract 'N' bit

    mul64_ccode_set:
    	move.b	EXC_CC+1(a6),d6		* fetch previous %ccr
    	andi.b	#$10,d6			* all but 'X' bit changes

    	or.b	d7,d6			* group 'X' and 'N'
    	move.b	d6,EXC_CC+1(a6)		* save new %ccr

    	rts

    * one or both of the operands is zero so the result is also zero.
    * save the zero result to the register file and set the 'Z' ccode bit.
    mul64_zero:
    	clr.l	(EXC_DREGS,a6,d2.w*4)	* save lo(result)
    	clr.l	(EXC_DREGS,a6,d1.w*4)	* save hi(result)

    	moveq.l	#$4,d7			* set 'Z' ccode bit
    	bra.b	mul64_ccode_set		* finish ccode set

    *#########

    * multiplier operand is in memory at the effective address.
    * must calculate the <ea> and go fetch the 32-bit operand.
    mul64_memop:
    	moveq.l	#LONG,d0		* pass # of bytes
    	bsr.l	_calc_ea		* calculate <ea>

    	cmpi.b	#immed_flg,SPCOND_FLG(a6)	* immediate addressing mode?
    	beq.b	mul64_immed		* yes

    	move.l	a0,a2
    	bsr.l	_dmem_read_long		* fetch src from addr (%a0)

    	tst.l	d1			* dfetch error?
    	bne.w	mul64_err		* yes

    	move.l	d0,d3			* store multiplier in %d3

    	bra.w	mul64_multiplicand

    * we have to split out immediate data here because it must be read using
    * imem_read() instead of dmem_read(). this becomes especially important
    * if the fetch runs into some deadly fault.
    mul64_immed:
    	addq.l	#$4,EXC_EXTWPTR(a6)
    	bsr.l	_imem_read_long		* read immediate value

    	tst.l	d1			* ifetch error?
    	bne.l	isp_iacc		* yes

    	move.l	d0,d3
    	bra.w	mul64_multiplicand

    *#########

    * if dmem_read_long() returns a fail message in d1, the package
    * must create an access error frame. here, we pass a skeleton fslw
    * and the failing address to the routine that creates the new frame.
    * also, we call isp_restore in case the effective addressing mode was
    * (an)+ or -(an) in which case the previous "an" value must be restored.
    * FSLW:
    * 	read = true
    * 	size = longword
    *	TM = data
    * 	software emulation error = true
    mul64_err:
    	bsr.l	isp_restore		* restore addr reg
    	move.l	a2,a0			* pass failing address
    	move.l	#$01010001,d0		* pass fslw
    	bra.l	isp_dacc

    *########################################################################
    * XDEF ****************************************************************	#
    *	_compandset2(): routine to emulate cas2()			#
    *			(internal to package)				#
    *									#
    *	_isp_cas2_finish(): store ccodes, store compare regs		#
    *			    (external to package)			#
    *									#
    * XREF ****************************************************************	#
    *	_real_lock_page() - "callout" to lock op's page from page-outs	#
    *	_cas_terminate2() - access error exit				#
    *	_real_cas2() - "callout" to core cas2 emulation code		#
    *	_real_unlock_page() - "callout" to unlock page			#
    *									#
    * INPUT ***************************************************************	#
    * _compandset2():							#
    *	d0 = instruction extension word					#
    *									#
    * _isp_cas2_finish():							#
    *	see cas2 core emulation code					#
    * 									#
    * OUTPUT **************************************************************	#
    * _compandset2():							#
    *	see cas2 core emulation code					#
    *									#
    * _isp_cas_finish():							#
    *	None (register file or memroy changed as appropriate)		#
    *									#
    * ALGORITHM ***********************************************************	#
    * compandset2():							#
    *	Decode the instruction and fetch the appropriate Update and	#
    * Compare operands. Then call the "callout" _real_lock_page() for each	#
    * memory operand address so that the operating system can keep these	#
    * pages from being paged out. If either _real_lock_page() fails, exit	#
    * through _cas_terminate2(). Don't forget to unlock the 1st locked page	#
    * using _real_unlock_paged() if the 2nd lock-page fails.		#
    * Finally, branch to the core cas2 emulation code by calling the 	#
    * "callout" _real_cas2().						#
    *									#
    * _isp_cas2_finish():							#
    *	Re-perform the comparison so we can determine the condition	#
    * codes which were too much trouble to keep around during the locked	#
    * emulation. Then unlock each operands page by calling the "callout"	#
    * _real_unlock_page().							#
    *									#
    *########################################################################

    ADDR1	set	EXC_TEMP+$c
    ADDR2	set	EXC_TEMP+$0
    DC2	set	EXC_TEMP+$a
    DC1	set	EXC_TEMP+$8

    	global	_compandset2
    _compandset2:
    	move.l	d0,EXC_TEMP+$4(a6)		* store for possible restart
    	move.l	d0,d1			* extension word in d0

    	rol.w	#$4,d0
    	andi.w	#$f,d0			* extract Rn2
    	move.l	(EXC_DREGS,a6,d0.w*4),a1	* fetch ADDR2
    	move.l	a1,ADDR2(a6)

    	move.l	d1,d0

    	lsr.w	#$6,d1
    	andi.w	#$7,d1			* extract Du2
    	move.l	(EXC_DREGS,a6,d1.w*4),d5	* fetch Update2 Op

    	andi.w	#$7,d0			* extract Dc2
    	move.l	(EXC_DREGS,a6,d0.w*4),d3	* fetch Compare2 Op
    	move.w	d0,DC2(a6)

    	move.w	EXC_EXTWORD(a6),d0
    	move.l	d0,d1

    	rol.w	#$4,d0
    	andi.w	#$f,d0			* extract Rn1
    	move.l	(EXC_DREGS,a6,d0.w*4),a0	* fetch ADDR1
    	move.l	a0,ADDR1(a6)

    	move.l	d1,d0

    	lsr.w	#$6,d1
    	andi.w	#$7,d1			* extract Du1
    	move.l	(EXC_DREGS,a6,d1.w*4),d4	* fetch Update1 Op

    	andi.w	#$7,d0			* extract Dc1
    	move.l	(EXC_DREGS,a6,d0.w*4),d2	* fetch Compare1 Op
    	move.w	d0,DC1(a6)

    	btst	#$1,EXC_OPWORD(a6)	* word or long?
    	sne	d7

    	btst	#$5,EXC_ISR(a6)		* user or supervisor?
    	sne	d6

    	move.l	a0,a2
    	move.l	a1,a3

    	move.l	d7,d1			* pass size
    	move.l	d6,d0			* pass mode
    	bsr.l	_real_lock_page		* lock page
    	move.l	a2,a0
    	tst.l	d0			* error?
    	bne.l	_cas_terminate2		* yes

    	move.l	d7,d1			* pass size
    	move.l	d6,d0			* pass mode
    	move.l	a3,a0			* pass addr
    	bsr.l	_real_lock_page		* lock page
    	move.l	a3,a0
    	tst.l	d0			* error?
    	bne.b	cas_preterm		* yes

    	move.l	a2,a0
    	move.l	a3,a1

    	bra.l	_real_cas2

    * if the 2nd lock attempt fails, then we must still unlock the
    * first page(s).
    cas_preterm:
    	move.l	d0,-(sp)		* save FSLW
    	move.l	d7,d1			* pass size
    	move.l	d6,d0			* pass mode
    	move.l	a2,a0			* pass ADDR1
    	bsr.l	_real_unlock_page	* unlock first page(s)
    	move.l	(sp)+,d0		* restore FSLW
    	move.l	a3,a0			* pass failing addr
    	bra.l	_cas_terminate2

    *############################################################

    	global	_isp_cas2_finish
    _isp_cas2_finish:
    	btst	#$1,EXC_OPWORD(a6)
    	bne.b	cas2_finish_l

    	move.w	EXC_CC(a6),ccr		* load old ccodes
    	cmp.w	d2,d0
    	bne.b	cas2_finish_w_save
    	cmp.w	d3,d1
    cas2_finish_w_save:
    	move.w	ccr,EXC_CC(a6)		* save new ccodes

    	tst.b	d4			* update compare reg?
    	bne.b	cas2_finish_w_done	* no

    	move.w	DC2(a6),d3		* fetch Dc2
    	move.w	d1,(2+EXC_DREGS,a6,d3.w*4)	* store new Compare2 Op

    	move.w	DC1(a6),d2		* fetch Dc1
    	move.w	d0,(2+EXC_DREGS,a6,d2.w*4)	* store new Compare1 Op

    cas2_finish_w_done:
    	btst	#$5,EXC_ISR(a6)
    	sne	d2
    	move.l	d2,d0			* pass mode
    	sf	d1			* pass size
    	move.l	ADDR1(a6),a0		* pass ADDR1
    	bsr.l	_real_unlock_page	* unlock page

    	move.l	d2,d0			* pass mode
    	sf	d1			* pass size
    	move.l	ADDR2(a6),a0		* pass ADDR2
    	bsr.l	_real_unlock_page	* unlock page
    	rts

    cas2_finish_l:
    	move.w	EXC_CC(a6),ccr		* load old ccodes
    	cmp.l	d2,d0
    	bne.b	cas2_finish_l_save
    	cmp.l	d3,d1
    cas2_finish_l_save:
    	move.w	ccr,EXC_CC(a6)		* save new ccodes

    	tst.b	d4			* update compare reg?
    	bne.b	cas2_finish_l_done	* no

    	move.w	DC2(a6),d3		* fetch Dc2
    	move.l	d1,(EXC_DREGS,a6,d3.w*4)	* store new Compare2 Op

    	move.w	DC1(a6),d2		* fetch Dc1
    	move.l	d0,(EXC_DREGS,a6,d2.w*4)	* store new Compare1 Op

    cas2_finish_l_done:
    	btst	#$5,EXC_ISR(a6)
    	sne	d2
    	move.l	d2,d0			* pass mode
    	st	d1			* pass size
    	move.l	ADDR1(a6),a0		* pass ADDR1
    	bsr.l	_real_unlock_page	* unlock page

    	move.l	d2,d0			* pass mode
    	st	d1			* pass size
    	move.l	ADDR2(a6),a0		* pass ADDR2
    	bsr.l	_real_unlock_page	* unlock page
    	rts

    *#######
    	global	cr_cas2
    cr_cas2:
    	move.l	EXC_TEMP+$4(a6),d0
    	bra.w	_compandset2

    *########################################################################
    * XDEF ****************************************************************	#
    *	_compandset(): routine to emulate cas w/ misaligned <ea>	#
    *		       (internal to package)				#
    *	_isp_cas_finish(): routine called when cas emulation completes	#
    *			   (external and internal to package)		#
    *	_isp_cas_restart(): restart cas emulation after a fault		#
    *			    (external to package)			#
    *	_isp_cas_terminate(): create access error stack frame on fault	#
    *			      (external and internal to package)	#
    *	_isp_cas_inrange(): checks whether instr addess is within range	#
    *			    of core cas/cas2emulation code		#
    *			    (external to package)			#
    *									#
    * XREF ****************************************************************	#
    * 	_calc_ea(): calculate effective address				#
    *									#
    * INPUT ***************************************************************	#
    * compandset():								#
    * 	none								#
    * _isp_cas_restart():							#
    *	d6 = previous sfc/dfc						#
    * _isp_cas_finish():							#
    * _isp_cas_terminate():							#
    *	a0 = failing address						#
    *	d0 = FSLW							#
    *	d6 = previous sfc/dfc						#
    * _isp_cas_inrange():							#
    *	a0 = instruction address to be checked				#
    *									#
    * OUTPUT **************************************************************	#
    * compandset():								#
    *		none							#
    * _isp_cas_restart():							#
    *	a0 = effective address						#
    *	d7 = word or longword flag					#
    * _isp_cas_finish():							#
    *	a0 = effective address						#
    * _isp_cas_terminate():							#
    *	initial register set before emulation exception			#
    * _isp_cas_inrange():							#
    *	d0 = 0 => in range; -1 => out of range				#
    *									#
    * ALGORITHM ***********************************************************	#
    *									#
    * compandset():								#
    *	First, calculate the effective address. Then, decode the 	#
    * instruction word and fetch the "compare" (DC) and "update" (Du)	#
    * operands.								#
    * 	Next, call the external routine _real_lock_page() so that the	#
    * operating system can keep this page from being paged out while we're	#
    * in this routine. If this call fails, jump to _cas_terminate2().	#
    *	The routine then branches to _real_cas(). This external routine	#
    * that actually emulates cas can be supplied by the external os or	#
    * made to point directly back into the 060ISP which has a routine for	#
    * this purpose.								#
    *									#
    * _isp_cas_finish():							#
    * 	Either way, after emulation, the package is re-entered at	#
    * _isp_cas_finish(). This routine re-compares the operands in order to	#
    * set the condition codes. Finally, these routines will call		#
    * _real_unlock_page() in order to unlock the pages that were previously	#
    * locked.								#
    *									#
    * _isp_cas_restart():							#
    *	This routine can be entered from an access error handler where	#
    * the emulation sequence should be re-started from the beginning.	#
    *									#
    * _isp_cas_terminate():							#
    *	This routine can be entered from an access error handler where	#
    * an emulation operand access failed and the operating system would	#
    * like an access error stack frame created instead of the current 	#
    * unimplemented integer instruction frame.				#
    * 	Also, the package enters here if a call to _real_lock_page()	#
    * fails.								#
    *									#
    * _isp_cas_inrange():							#
    * 	Checks to see whether the instruction address passed to it in	#
    * a0 is within the software package cas/cas2 emulation routines. This	#
    * can be helpful for an operating system to determine whether an access	#
    * error during emulation was due to a cas/cas2 emulation access.	#
    *									#
    *########################################################################

    __DC__	set	EXC_TEMP+$8
    ADDR	set	EXC_TEMP+$4

    	global	_compandset
    _compandset:
    	btst	#$1,EXC_OPWORD(a6)	* word or long operation?
    	bne.b	compandsetl		* long

    compandsetw:
    	moveq.l	#$2,d0			* size = 2 bytes
    	bsr.l	_calc_ea		* a0 = calculated <ea>
    	move.l	a0,ADDR(a6)		* save <ea> for possible restart
    	sf	d7			* clear d7 for word size
    	bra.b	compandsetfetch

    compandsetl:
    	moveq.l	#$4,d0			* size = 4 bytes
    	bsr.l	_calc_ea		* a0 = calculated <ea>
    	move.l	a0,ADDR(a6)		* save <ea> for possible restart
    	st	d7			* set d7 for longword size

    compandsetfetch:
    	move.w	EXC_EXTWORD(a6),d0	* fetch cas extension word
    	move.l	d0,d1			* make a copy

    	lsr.w	#$6,d0
    	andi.w	#$7,d0			* extract Du
    	move.l	(EXC_DREGS,a6,d0.w*4),d2	* get update operand

    	andi.w	#$7,d1			* extract Dc
    	move.l	(EXC_DREGS,a6,d1.w*4),d4	* get compare operand
    	move.w	d1,__DC__(a6)		* save Dc

    	btst	#$5,EXC_ISR(a6)		* which mode for exception?
    	sne	d6			* set on supervisor mode

    	move.l	a0,a2			* save temporarily
    	move.l	d7,d1			* pass size
    	move.l	d6,d0			* pass mode
    	bsr.l	_real_lock_page		* lock page
    	tst.l	d0			* did error occur?
    	bne.w	_cas_terminate2		* yes, clean up the mess
    	move.l	a2,a0			* pass addr in a0

    	bra.l	_real_cas

    *#######
    	global	_isp_cas_finish
    _isp_cas_finish:
    	btst	#$1,EXC_OPWORD(a6)
    	bne.b	cas_finish_l

    * just do the compare again since it's faster than saving the ccodes
    * from the locked routine...
    cas_finish_w:
    	move.w	EXC_CC(a6),ccr		* restore cc
    	cmp.w	d4,d0			* do word compare
    	move.w	ccr,EXC_CC(a6)		* save cc

    	tst.b	d1			* update compare reg?
    	bne.b	cas_finish_w_done	* no

    	move.w	__DC__(a6),d3
    	move.w	d0,(EXC_DREGS+2,a6,d3.w*4)	* Dc = destination

    cas_finish_w_done:
    	move.l	ADDR(a6),a0		* pass addr
    	sf	d1			* pass size
    	btst	#$5,EXC_ISR(a6)
    	sne	d0			* pass mode
    	bsr.l	_real_unlock_page	* unlock page
    	rts

    * just do the compare again since it's faster than saving the ccodes
    * from the locked routine...
    cas_finish_l:
    	move.w	EXC_CC(a6),ccr		* restore cc
    	cmp.l	d4,d0			* do longword compare
    	move.w	ccr,EXC_CC(a6)		* save cc

    	tst.b	d1			* update compare reg?
    	bne.b	cas_finish_l_done	* no

    	move.w	__DC__(a6),d3
    	move.l	d0,(EXC_DREGS,a6,d3.w*4)	* Dc = destination

    cas_finish_l_done:
    	move.l	ADDR(a6),a0		* pass addr
    	st	d1			* pass size
    	btst	#$5,EXC_ISR(a6)
    	sne	d0			* pass mode
    	bsr.l	_real_unlock_page	* unlock page
    	rts

    *#######

    	global	_isp_cas_restart
    _isp_cas_restart:
    	movec.l	d6,sfc			* restore previous sfc
    	movec.l	d6,dfc			* restore previous dfc

    	cmpi.b	#$fc,EXC_OPWORD+1(a6)	* cas or cas2?
    	beq.l	cr_cas2			* cas2
    cr_cas:
    	move.l	ADDR(a6),a0		* load <ea>
    	btst	#$1,EXC_OPWORD(a6)	* word or long operation?
    	sne	d7			* set d7 accordingly
    	bra.w	compandsetfetch

    *#######

    * At this stage, it would be nice if d0 held the FSLW.
    	global	_isp_cas_terminate
    _isp_cas_terminate:
    	movec.l	d6,sfc			* restore previous sfc
    	movec.l	d6,dfc			* restore previous dfc

    	global	_cas_terminate2
    _cas_terminate2:
    	move.l	a0,a2			* copy failing addr to a2

    	move.l	d0,-(sp)
    	bsr.l	isp_restore		* restore An (if ()+ or -())
    	move.l	(sp)+,d0

    	addq.l	#$4,sp			* remove sub return addr
    	subq.l	#$8,sp			* make room for bigger stack
    	subq.l	#$8,a6			* shift frame ptr down, too
    	move.l	#26,d1			* want to move 51 longwords
    	lea	$8(sp),a0		* get address of old stack
    	lea	$0.w(sp),a1		* get address of new stack
    cas_term_cont:
    	move.l	(a0)+,(a1)+		* move a longword
    	dbra.w	d1,cas_term_cont	* keep going

    	move.w	#$4008,EXC_IVOFF(a6)	* put new stk fmt, voff
    	move.l	a2,EXC_IVOFF+$2(a6)	* put faulting addr on stack
    	move.l	d0,EXC_IVOFF+$6(a6)	* put FSLW on stack
    	movem.l	EXC_DREGS(a6),d0-d7/a0-a5	* restore user regs
    	unlk	a6			* unlink stack frame
    	bra.l	_real_access

    *#######

    	global	_isp_cas_inrange
    _isp_cas_inrange:
    	clr.l	d0			* clear return result
    	lea	_CASHI.l(pc),a1		* load end of CAS core code
    	cmp.l	a0,a1			* is PC in range?
    	blt.b	cin_no			* no
    	lea	_CASLO.l(pc),a1		* load begin of CAS core code
    	cmp.l	a1,a0			* is PC in range?
    	blt.b	cin_no			* no
    	rts				* yes; return d0 = 0
    cin_no:
    	move.l	#-$1,d0			* out of range; return d0 = -1
    	rts

    *################################################################
    *################################################################
    *################################################################
    * This is the start of the cas and cas2 "core" emulation code.	#
    * This is the section that may need to be replaced by the host	#
    * OS if it is too operating system-specific.			#
    * Please refer to the package documentation to see how to	#
    * "replace" this section, if necessary.				#
    *################################################################
    *################################################################
    *################################################################

    *       ######      ##      ######     ####
    *       #	   #  #     #         #    #
    *	#	  ######    ######        #
    *	#	  #    #         #      #
    *       ######    #    #    ######    ######

    *########################################################################
    * XDEF ****************************************************************	#
    *	_isp_cas2(): "core" emulation code for the cas2 instruction	#
    *									#
    * XREF ****************************************************************	#
    *	_isp_cas2_finish() - only exit point for this emulation code;	#
    *			     do clean-up; calculate ccodes; store 	#
    *			     Compare Ops if appropriate.		#
    *									#
    * INPUT ***************************************************************	#
    *	*see chart below*						#
    * 									#
    * OUTPUT **************************************************************	#
    *	*see chart below*						#
    *									#
    * ALGORITHM ***********************************************************	#
    *	(1) Make several copies of the effective address.		#
    *	(2) Save current SR; Then mask off all maskable interrupts.	#
    *	(3) Save current SFC/DFC (ASSUMED TO BE EQUAL!!!); Then set 	#
    *	    according to whether exception occurred in user or 		#
    *	    supervisor mode.						#
    *	(4) Use "plpaw" instruction to pre-load ATC with effective	#
    *	    address pages(s). THIS SHOULD NOT FAULT!!! The relevant	#
    *	    page(s) should have already been made resident prior to	#
    * 	    entering this routine.					#
    *	(5) Push the operand lines from the cache w/ "cpushl". 		#
    *	    In the 68040, this was done within the locked region. In	#
    * 	    the 68060, it is done outside of the locked region.		#
    *	(6) Use "plpar" instruction to do a re-load of ATC entries for	#
    *	    ADDR1 since ADDR2 entries may have pushed ADDR1 out of the	#
    *	    ATC.							#
    *	(7) Pre-fetch the core emulation instructions by executing	#
    *	    one branch within each physical line (16 bytes) of the code	#
    *	    before actually executing the code.				#
    *	(8) Load the BUSCR w/ the bus lock value.			#
    *	(9) Fetch the source operands using "moves".			#
    *	(10)Do the compares. If both equal, go to step (13).		#
    *	(11)Unequal. No update occurs. But, we do write the DST1 op	#
    *	    back to itself (as w/ the '040) so we can gracefully unlock	#
    *	    the bus (and assert LOCKE*) using BUSCR and the final move.	#
    *	(12)Exit.							#
    *	(13)Write update operand to the DST locations. Use BUSCR to 	#
    *	    assert LOCKE* for the final write operation.		#
    *	(14)Exit.							#
    *									#
    * 	The algorithm is actually implemented slightly differently	#
    * depending on the size of the operation and the misalignment of the 	#
    * operands. A misaligned operand must be written in aligned chunks or	#
    * else the BUSCR register control gets confused.			#
    *									#
    *########################################################################

    *################################################################
    * THIS IS THE STATE OF THE INTEGER REGISTER FILE UPON		#
    * ENTERING _isp_cas2().						#
    *								#
    * D0 = xxxxxxxx							#
    * D1 = xxxxxxxx							#
    * D2 = cmp operand 1						#
    * D3 = cmp operand 2						#
    * D4 = update oper 1						#
    * D5 = update oper 2						#
    * D6 = 'xxxxxxff if supervisor mode; 'xxxxxx00 if user mode	#
    * D7 = 'xxxxxxff if longword operation; 'xxxxxx00 if word 	#
    * A0 = ADDR1							#
    * A1 = ADDR2							#
    * A2 = xxxxxxxx							#
    * A3 = xxxxxxxx							#
    * A4 = xxxxxxxx							#
    * A5 = xxxxxxxx							#
    * A6 = frame pointer						#
    * A7 = stack pointer						#
    *################################################################

    *	align		0x1000
    * beginning label used by _isp_cas_inrange()
    	global	_CASLO
    _CASLO:

    	global	_isp_cas2
    _isp_cas2:
    	tst.b	d6			* user or supervisor mode?
    	bne.b	cas2_supervisor		* supervisor
    cas2_user:
    	moveq.l	#$1,d0			* load user data fc
    	bra.b	cas2_cont
    cas2_supervisor:
    	moveq.l	#$5,d0			* load supervisor data fc
    cas2_cont:
    	tst.b	d7			* word or longword?
    	beq.w	cas2w			* word

    *###
    cas2l:
    	move.l	a0,a2			* copy ADDR1
    	move.l	a1,a3			* copy ADDR2
    	move.l	a0,a4			* copy ADDR1
    	move.l	a1,a5			* copy ADDR2

    	addq.l	#$3,a4			* ADDR1+3
    	addq.l	#$3,a5			* ADDR2+3
    	move.l	a2,d1			* ADDR1

    * mask interrupts levels 0-6. save old mask value.
    	move.w	sr,d7			* save current SR
    	ori.w	#$0700,sr		* inhibit interrupts

    * load the SFC and DFC with the appropriate mode.
    	movec	sfc,d6			* save old SFC/DFC
    	movec	d0,sfc			* store new SFC
    	movec	d0,dfc			* store new DFC

    * pre-load the operand ATC. no page faults should occur here because
    * _real_lock_page() should have taken care of this.
    	plpaw	(a2)			* load atc for ADDR1
    	plpaw	(a4)			* load atc for ADDR1+3
    	plpaw	(a3)			* load atc for ADDR2
    	plpaw	(a5)			* load atc for ADDR2+3

    * push the operand lines from the cache if they exist.
    	cpushl	dc,(a2)			* push line for ADDR1
    	cpushl	dc,(a4)			* push line for ADDR1+3
    	cpushl	dc,(a3)			* push line for ADDR2
    	cpushl	dc,(a5)			* push line for ADDR2+2

    	move.l	d1,a2			* ADDR1
    	addq.l	#$3,d1
    	move.l	d1,a4			* ADDR1+3
    * if ADDR1 was ATC resident before the above "plpaw" and was executed
    * and it was the next entry scheduled for replacement and ADDR2
    * shares the same set, then the "plpaw" for ADDR2 can push the ADDR1
    * entries from the ATC. so, we do a second set of "plpa"s.
    	plpar	(a2)			* load atc for ADDR1
    	plpar	(a4)			* load atc for ADDR1+3

    * load the BUSCR values.
    	move.l	#$80000000,a2		* assert LOCK* buscr value
    	move.l	#$a0000000,a3		* assert LOCKE* buscr value
    	move.l	#$00000000,a4		* buscr unlock value

    * there are three possible mis-aligned cases for longword cas. they
    * are separated because the final write which asserts LOCKE* must
    * be aligned.
    	move.l	a0,d0			* is ADDR1 misaligned?
    	andi.b	#$3,d0
    	beq.b	CAS2L_ENTER		* no
    	cmpi.b	#$2,d0
    	beq.w	CAS2L2_ENTER		* yes; word misaligned
    	bra.w	CAS2L3_ENTER		* yes; byte misaligned

    *
    * D0 = dst operand 1 <-
    * D1 = dst operand 2 <-
    * D2 = cmp operand 1
    * D3 = cmp operand 2
    * D4 = update oper 1
    * D5 = update oper 2
    * D6 = old SFC/DFC
    * D7 = old SR
    * A0 = ADDR1
    * A1 = ADDR2
    * A2 = bus LOCK*  value
    * A3 = bus LOCKE* value
    * A4 = bus unlock value
    * A5 = xxxxxxxx
    *
    	align	$10,$51FC
    CAS2L_START:
    	movec	a2,buscr		* assert LOCK*
    	moves.l	(a1),d1			* fetch Dest2[31:0]
    	moves.l	(a0),d0			* fetch Dest1[31:0]
    	bra.b	CAS2L_CONT
    CAS2L_ENTER:
    	bra.b	(*)+16

    CAS2L_CONT:
    	cmp.l	d2,d0			* Dest1 - Compare1
    	bne.b	CAS2L_NOUPDATE
    	cmp.l	d3,d1			* Dest2 - Compare2
    	bne.b	CAS2L_NOUPDATE
    	moves.l	d5,(a1)			* Update2[31:0] -> DEST2
    	bra.b	CAS2L_UPDATE
    	bra.b	(*)+16

    CAS2L_UPDATE:
    	movec	a3,buscr		* assert LOCKE*
    	moves.l	d4,(a0)			* Update1[31:0] -> DEST1
    	movec	a4,buscr		* unlock the bus
    	bra.b	cas2l_update_done
    	bra.b	(*)+16

    CAS2L_NOUPDATE:
    	movec	a3,buscr		* assert LOCKE*
    	moves.l	d0,(a0)			* Dest1[31:0] -> DEST1
    	movec	a4,buscr		* unlock the bus
    	bra.b	cas2l_noupdate_done
    	bra.b	(*)+16

    CAS2L_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CAS2L_START

    *###

    *################################################################
    * THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON	#
    * ENTERING _isp_cas2().						#
    *								#
    * D0 = destination[31:0] operand 1				#
    * D1 = destination[31:0] operand 2				#
    * D2 = cmp[31:0] operand 1					#
    * D3 = cmp[31:0] operand 2					#
    * D4 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required	#
    * D5 = xxxxxxxx							#
    * D6 = xxxxxxxx							#
    * D7 = xxxxxxxx							#
    * A0 = xxxxxxxx							#
    * A1 = xxxxxxxx							#
    * A2 = xxxxxxxx							#
    * A3 = xxxxxxxx							#
    * A4 = xxxxxxxx							#
    * A5 = xxxxxxxx							#
    * A6 = frame pointer						#
    * A7 = stack pointer						#
    *################################################################

    cas2l_noupdate_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	sf	d4			* indicate no update was done
    	bra.l	_isp_cas2_finish

    cas2l_update_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	st	d4			* indicate update was done
    	bra.l	_isp_cas2_finish
    *###

    	align	$10,$51FC
    CAS2L2_START:
    	movec	a2,buscr		* assert LOCK*
    	moves.l	(a1),d1			* fetch Dest2[31:0]
    	moves.l	(a0),d0			* fetch Dest1[31:0]
    	bra.b	CAS2L2_CONT
    CAS2L2_ENTER:
    	bra.b	(*)+16

    CAS2L2_CONT:
    	cmp.l	d2,d0			* Dest1 - Compare1
    	bne.b	CAS2L2_NOUPDATE
    	cmp.l	d3,d1			* Dest2 - Compare2
    	bne.b	CAS2L2_NOUPDATE
    	moves.l	d5,(a1)			* Update2[31:0] -> Dest2
    	bra.b	CAS2L2_UPDATE
    	bra.b	(*)+16

    CAS2L2_UPDATE:
    	swap	d4			* get Update1[31:16]
    	moves.w	d4,(a0)+		* Update1[31:16] -> DEST1
    	movec	a3,buscr		* assert LOCKE*
    	swap	d4			* get Update1[15:0]
    	bra.b	CAS2L2_UPDATE2
    	bra.b	(*)+16

    CAS2L2_UPDATE2:
    	moves.w	d4,(a0)			* Update1[15:0] -> DEST1+0x2
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2l_update_done
    	nop
    	bra.b	(*)+16

    CAS2L2_NOUPDATE:
    	swap	d0			* get Dest1[31:16]
    	moves.w	d0,(a0)+		* Dest1[31:16] -> DEST1
    	movec	a3,buscr		* assert LOCKE*
    	swap	d0			* get Dest1[15:0]
    	bra.b	CAS2L2_NOUPDATE2
    	bra.b	(*)+16

    CAS2L2_NOUPDATE2:
    	moves.w	d0,(a0)			* Dest1[15:0] -> DEST1+0x2
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2l_noupdate_done
    	nop
    	bra.b	(*)+16

    CAS2L2_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CAS2L2_START

    *################################

    	align	$10,$51FC
    CAS2L3_START:
    	movec	a2,buscr		* assert LOCK*
    	moves.l	(a1),d1			* fetch Dest2[31:0]
    	moves.l	(a0),d0			* fetch Dest1[31:0]
    	bra.b	CAS2L3_CONT
    CAS2L3_ENTER:
    	bra.b	(*)+16

    CAS2L3_CONT:
    	cmp.l	d2,d0			* Dest1 - Compare1
    	bne.b	CAS2L3_NOUPDATE
    	cmp.l	d3,d1			* Dest2 - Compare2
    	bne.b	CAS2L3_NOUPDATE
    	moves.l	d5,(a1)			* Update2[31:0] -> DEST2
    	bra.b	CAS2L3_UPDATE
    	bra.b	(*)+16

    CAS2L3_UPDATE:
    	rol.l	#$8,d4			* get Update1[31:24]
    	moves.b	d4,(a0)+		* Update1[31:24] -> DEST1
    	swap	d4			* get Update1[23:8]
    	moves.w	d4,(a0)+		* Update1[23:8] -> DEST1+0x1
    	bra.b	CAS2L3_UPDATE2
    	bra.b	(*)+16

    CAS2L3_UPDATE2:
    	rol.l	#$8,d4			* get Update1[7:0]
    	movec	a3,buscr		* assert LOCKE*
    	moves.b	d4,(a0)			* Update1[7:0] -> DEST1+0x3
    	bra.b	CAS2L3_UPDATE3
    	nop
    	bra.b	(*)+16

    CAS2L3_UPDATE3:
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2l_update_done
    	nop
    	nop
    	nop
    	bra.b	(*)+16

    CAS2L3_NOUPDATE:
    	rol.l	#$8,d0			* get Dest1[31:24]
    	moves.b	d0,(a0)+		* Dest1[31:24] -> DEST1
    	swap	d0			* get Dest1[23:8]
    	moves.w	d0,(a0)+		* Dest1[23:8] -> DEST1+0x1
    	bra.b	CAS2L3_NOUPDATE2
    	bra.b	(*)+16

    CAS2L3_NOUPDATE2:
    	rol.l	#$8,d0			* get Dest1[7:0]
    	movec	a3,buscr		* assert LOCKE*
    	moves.b	d0,(a0)			* Update1[7:0] -> DEST1+0x3
    	bra.b	CAS2L3_NOUPDATE3
    	nop
    	bra.b	(*)+16

    CAS2L3_NOUPDATE3:
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2l_noupdate_done
    	nop
    	nop
    	nop
    	bra.b	(*)+14

    CAS2L3_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.w	CAS2L3_START

    *############################################################
    *############################################################

    cas2w:
    	move.l	a0,a2			* copy ADDR1
    	move.l	a1,a3			* copy ADDR2
    	move.l	a0,a4			* copy ADDR1
    	move.l	a1,a5			* copy ADDR2

    	addq.l	#$1,a4			* ADDR1+1
    	addq.l	#$1,a5			* ADDR2+1
    	move.l	a2,d1			* ADDR1

    * mask interrupt levels 0-6. save old mask value.
    	move.w	sr,d7			* save current SR
    	ori.w	#$0700,sr		* inhibit interrupts

    * load the SFC and DFC with the appropriate mode.
    	movec	sfc,d6			* save old SFC/DFC
    	movec	d0,sfc			* store new SFC
    	movec	d0,dfc			* store new DFC

    * pre-load the operand ATC. no page faults should occur because
    * _real_lock_page() should have taken care of this.
    	plpaw	(a2)			* load atc for ADDR1
    	plpaw	(a4)			* load atc for ADDR1+1
    	plpaw	(a3)			* load atc for ADDR2
    	plpaw	(a5)			* load atc for ADDR2+1

    * push the operand cache lines from the cache if they exist.
    	cpushl	dc,(a2)			* push line for ADDR1
    	cpushl	dc,(a4)			* push line for ADDR1+1
    	cpushl	dc,(a3)			* push line for ADDR2
    	cpushl	dc,(a5)			* push line for ADDR2+1

    	move.l	d1,a2			* ADDR1
    	addq.l	#$3,d1
    	move.l	d1,a4			* ADDR1+3
    * if ADDR1 was ATC resident before the above "plpaw" and was executed
    * and it was the next entry scheduled for replacement and ADDR2
    * shares the same set, then the "plpaw" for ADDR2 can push the ADDR1
    * entries from the ATC. so, we do a second set of "plpa"s.
    	plpar	(a2)			* load atc for ADDR1
    	plpar	(a4)			* load atc for ADDR1+3

    * load the BUSCR values.
    	move.l	#$80000000,a2		* assert LOCK* buscr value
    	move.l	#$a0000000,a3		* assert LOCKE* buscr value
    	move.l	#$00000000,a4		* buscr unlock value

    * there are two possible mis-aligned cases for word cas. they
    * are separated because the final write which asserts LOCKE* must
    * be aligned.
    	move.l	a0,d0			* is ADDR1 misaligned?
    	btst	#$0,d0
    	bne.w	CAS2W2_ENTER		* yes
    	bra.b	CAS2W_ENTER		* no

    *
    * D0 = dst operand 1 <-
    * D1 = dst operand 2 <-
    * D2 = cmp operand 1
    * D3 = cmp operand 2
    * D4 = update oper 1
    * D5 = update oper 2
    * D6 = old SFC/DFC
    * D7 = old SR
    * A0 = ADDR1
    * A1 = ADDR2
    * A2 = bus LOCK*  value
    * A3 = bus LOCKE* value
    * A4 = bus unlock value
    * A5 = xxxxxxxx
    *
    	align	$10,$51FC
    CAS2W_START:
    	movec	a2,buscr		* assert LOCK*
    	moves.w	(a1),d1			* fetch Dest2[15:0]
    	moves.w	(a0),d0			* fetch Dest1[15:0]
    	bra.b	CAS2W_CONT2
    CAS2W_ENTER:
    	bra.b	(*)+16

    CAS2W_CONT2:
    	cmp.w	d2,d0			* Dest1 - Compare1
    	bne.b	CAS2W_NOUPDATE
    	cmp.w	d3,d1			* Dest2 - Compare2
    	bne.b	CAS2W_NOUPDATE
    	moves.w	d5,(a1)			* Update2[15:0] -> DEST2
    	bra.b	CAS2W_UPDATE
    	bra.b	(*)+16

    CAS2W_UPDATE:
    	movec	a3,buscr		* assert LOCKE*
    	moves.w	d4,(a0)			* Update1[15:0] -> DEST1
    	movec	a4,buscr		* unlock the bus
    	bra.b	cas2w_update_done
    	bra.b	(*)+16

    CAS2W_NOUPDATE:
    	movec	a3,buscr		* assert LOCKE*
    	moves.w	d0,(a0)			* Dest1[15:0] -> DEST1
    	movec	a4,buscr		* unlock the bus
    	bra.b	cas2w_noupdate_done
    	bra.b	(*)+16

    CAS2W_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CAS2W_START

    *###

    *################################################################
    * THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON	#
    * ENTERING _isp_cas2().						#
    *								#
    * D0 = destination[15:0] operand 1				#
    * D1 = destination[15:0] operand 2				#
    * D2 = cmp[15:0] operand 1					#
    * D3 = cmp[15:0] operand 2					#
    * D4 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required	#
    * D5 = xxxxxxxx							#
    * D6 = xxxxxxxx							#
    * D7 = xxxxxxxx							#
    * A0 = xxxxxxxx							#
    * A1 = xxxxxxxx							#
    * A2 = xxxxxxxx							#
    * A3 = xxxxxxxx							#
    * A4 = xxxxxxxx							#
    * A5 = xxxxxxxx							#
    * A6 = frame pointer						#
    * A7 = stack pointer						#
    *################################################################

    cas2w_noupdate_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	sf	d4			* indicate no update was done
    	bra.l	_isp_cas2_finish

    cas2w_update_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	st	d4			* indicate update was done
    	bra.l	_isp_cas2_finish
    *###

    	align	$10,$51FC
    CAS2W2_START:
    	movec	a2,buscr		* assert LOCK*
    	moves.w	(a1),d1			* fetch Dest2[15:0]
    	moves.w	(a0),d0			* fetch Dest1[15:0]
    	bra.b	CAS2W2_CONT2
    CAS2W2_ENTER:
    	bra.b	(*)+16

    CAS2W2_CONT2:
    	cmp.w	d2,d0			* Dest1 - Compare1
    	bne.b	CAS2W2_NOUPDATE
    	cmp.w	d3,d1			* Dest2 - Compare2
    	bne.b	CAS2W2_NOUPDATE
    	moves.w	d5,(a1)			* Update2[15:0] -> DEST2
    	bra.b	CAS2W2_UPDATE
    	bra.b	(*)+16

    CAS2W2_UPDATE:
    	ror.l	#$8,d4			* get Update1[15:8]
    	moves.b	d4,(a0)+		* Update1[15:8] -> DEST1
    	movec	a3,buscr		* assert LOCKE*
    	rol.l	#$8,d4			* get Update1[7:0]
    	bra.b	CAS2W2_UPDATE2
    	bra.b	(*)+16

    CAS2W2_UPDATE2:
    	moves.b	d4,(a0)			* Update1[7:0] -> DEST1+0x1
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2w_update_done
    	nop
    	bra.b	(*)+16

    CAS2W2_NOUPDATE:
    	ror.l	#$8,d0			* get Dest1[15:8]
    	moves.b	d0,(a0)+		* Dest1[15:8] -> DEST1
    	movec	a3,buscr		* assert LOCKE*
    	rol.l	#$8,d0			* get Dest1[7:0]
    	bra.b	CAS2W2_NOUPDATE2
    	bra.b	(*)+16

    CAS2W2_NOUPDATE2:
    	moves.b	d0,(a0)			* Dest1[7:0] -> DEST1+0x1
    	movec	a4,buscr		* unlock the bus
    	bra.w	cas2w_noupdate_done
    	nop
    	bra.b	(*)+16

    CAS2W2_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CAS2W2_START

    *       ######      ##      ######
    *       #	   #  #     #
    *	#	  ######    ######
    *	#	  #    #         #
    *       ######    #    #    ######

    *########################################################################
    * XDEF ****************************************************************	#
    * 	_isp_cas(): "core" emulation code for the cas instruction	#
    *									#
    * XREF ****************************************************************	#
    *	_isp_cas_finish() - only exit point for this emulation code;	#
    *			    do clean-up					#
    *									#
    * INPUT ***************************************************************	#
    * 	*see entry chart below*						#
    *									#
    * OUTPUT **************************************************************	#
    *	*see exit chart below*						#
    *									#
    * ALGORITHM ***********************************************************	#
    * 	(1) Make several copies of the effective address. 		#
    * 	(2) Save current SR; Then mask off all maskable interrupts.	#
    *	(3) Save current DFC/SFC (ASSUMED TO BE EQUAL!!!); Then set	#
    *	    SFC/DFC according to whether exception occurred in user or	#
    *	    supervisor mode.						#
    *	(4) Use "plpaw" instruction to pre-load ATC with efective	#
    *	    address page(s). THIS SHOULD NOT FAULT!!! The relevant	#
    * 	    page(s) should have been made resident prior to entering 	#
    *	    this routine.						#
    *	(5) Push the operand lines from the cache w/ "cpushl".		#
    *	    In the 68040, this was done within the locked region. In	#
    *	    the 68060, it is done outside of the locked region.		#
    *	(6) Pre-fetch the core emulation instructions by executing one	#
    *	    branch within each physical line (16 bytes) of the code	#
    *	    before actually executing the code.				#
    *	(7) Load the BUSCR with the bus lock value.			#
    *	(8) Fetch the source operand.					#
    *	(9) Do the compare. If equal, go to step (12).			#
    *	(10)Unequal. No update occurs. But, we do write the DST op back	#
    *	    to itself (as w/ the '040) so we can gracefully unlock	#
    *	    the bus (and assert LOCKE*) using BUSCR and the final move.	#
    *	(11)Exit.							#
    *	(12)Write update operand to the DST location. Use BUSCR to	#
    *	    assert LOCKE* for the final write operation.		#
    *	(13)Exit.							#
    * 									#
    * 	The algorithm is actually implemented slightly diferently	#
    * depending on the size of the operation and the misalignment of the	#
    * operand. A misaligned operand must be written in aligned chunks or	#
    * else the BUSCR register control gets confused.			#
    *									#
    *########################################################################

    *########################################################
    * THIS IS THE STATE OF THE INTEGER REGISTER FILE UPON	#
    * ENTERING _isp_cas().					#
    *							#
    * D0 = xxxxxxxx						#
    * D1 = xxxxxxxx						#
    * D2 = update operand					#
    * D3 = xxxxxxxx						#
    * D4 = compare operand					#
    * D5 = xxxxxxxx						#
    * D6 = supervisor ('xxxxxxff) or user mode ('xxxxxx00)	#
    * D7 = longword ('xxxxxxff) or word size ('xxxxxx00)	#
    * A0 = ADDR						#
    * A1 = xxxxxxxx						#
    * A2 = xxxxxxxx						#
    * A3 = xxxxxxxx						#
    * A4 = xxxxxxxx						#
    * A5 = xxxxxxxx						#
    * A6 = frame pointer					#
    * A7 = stack pointer					#
    *########################################################

    	global	_isp_cas
    _isp_cas:
    	tst.b	d6			* user or supervisor mode?
    	bne.b	cas_super		* supervisor
    cas_user:
    	moveq.l	#$1,d0			* load user data fc
    	bra.b	cas_cont
    cas_super:
    	moveq.l	#$5,d0			* load supervisor data fc

    cas_cont:
    	tst.b	d7			* word or longword?
    	bne.w	casl			* longword

    *###
    casw:
    	move.l	a0,a1			* make copy for plpaw1
    	move.l	a0,a2			* make copy for plpaw2
    	addq.l	#$1,a2			* plpaw2 points to end of word

    	move.l	d2,d3			* d3 = update[7:0]
    	lsr.w	#$8,d2			* d2 = update[15:8]

    * mask interrupt levels 0-6. save old mask value.
    	move.w	sr,d7			* save current SR
    	ori.w	#$0700,sr		* inhibit interrupts

    * load the SFC and DFC with the appropriate mode.
    	movec	sfc,d6			* save old SFC/DFC
    	movec	d0,sfc			* load new sfc
    	movec	d0,dfc			* load new dfc

    * pre-load the operand ATC. no page faults should occur here because
    * _real_lock_page() should have taken care of this.
    	plpaw	(a1)			* load atc for ADDR
    	plpaw	(a2)			* load atc for ADDR+1

    * push the operand lines from the cache if they exist.
    	cpushl	dc,(a1)			* push dirty data
    	cpushl	dc,(a2)			* push dirty data

    * load the BUSCR values.
    	move.l	#$80000000,a1		* assert LOCK* buscr value
    	move.l	#$a0000000,a2		* assert LOCKE* buscr value
    	move.l	#$00000000,a3		* buscr unlock value

    * pre-load the instruction cache for the following algorithm.
    * this will minimize the number of cycles that LOCK* will be asserted.
    	bra.b	CASW_ENTER		* start pre-loading icache

    *
    * D0 = dst operand <-
    * D1 = update[15:8] operand
    * D2 = update[7:0]  operand
    * D3 = xxxxxxxx
    * D4 = compare[15:0] operand
    * D5 = xxxxxxxx
    * D6 = old SFC/DFC
    * D7 = old SR
    * A0 = ADDR
    * A1 = bus LOCK*  value
    * A2 = bus LOCKE* value
    * A3 = bus unlock value
    * A4 = xxxxxxxx
    * A5 = xxxxxxxx
    *
    	align	$10,$51FC
    CASW_START:
    	movec	a1,buscr		* assert LOCK*
    	moves.w	(a0),d0			* fetch Dest[15:0]
    	cmp.w	d4,d0			* Dest - Compare
    	bne.b	CASW_NOUPDATE
    	bra.b	CASW_UPDATE
    CASW_ENTER:
    	bra.b	(*)+16

    CASW_UPDATE:
    	moves.b	d2,(a0)+		* Update[15:8] -> DEST
    	movec	a2,buscr		* assert LOCKE*
    	moves.b	d3,(a0)			* Update[7:0] -> DEST+0x1
    	bra.b	CASW_UPDATE2
    	bra.b	(*)+16

    CASW_UPDATE2:
    	movec	a3,buscr		* unlock the bus
    	bra.b	casw_update_done
    	nop
    	nop
    	nop
    	nop
    	bra.b	(*)+16

    CASW_NOUPDATE:
    	ror.l	#$8,d0			* get Dest[15:8]
    	moves.b	d0,(a0)+		* Dest[15:8] -> DEST
    	movec	a2,buscr		* assert LOCKE*
    	rol.l	#$8,d0			* get Dest[7:0]
    	bra.b	CASW_NOUPDATE2
    	bra.b	(*)+16

    CASW_NOUPDATE2:
    	moves.b	d0,(a0)			* Dest[7:0] -> DEST+0x1
    	movec	a3,buscr		* unlock the bus
    	bra.b	casw_noupdate_done
    	nop
    	nop
    	bra.b	(*)+16

    CASW_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CASW_START

    *################################################################
    * THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON	#
    * CALLING _isp_cas_finish().					#
    *								#
    * D0 = destination[15:0] operand				#
    * D1 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required	#
    * D2 = xxxxxxxx							#
    * D3 = xxxxxxxx							#
    * D4 = compare[15:0] operand					#
    * D5 = xxxxxxxx							#
    * D6 = xxxxxxxx							#
    * D7 = xxxxxxxx							#
    * A0 = xxxxxxxx							#
    * A1 = xxxxxxxx							#
    * A2 = xxxxxxxx							#
    * A3 = xxxxxxxx							#
    * A4 = xxxxxxxx							#
    * A5 = xxxxxxxx							#
    * A6 = frame pointer						#
    * A7 = stack pointer						#
    *################################################################

    casw_noupdate_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	sf	d1			* indicate no update was done
    	bra.l	_isp_cas_finish

    casw_update_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	st	d1			* indicate update was done
    	bra.l	_isp_cas_finish

    *###############

    * there are two possible mis-aligned cases for longword cas. they
    * are separated because the final write which asserts LOCKE* must
    * be an aligned write.
    casl:
    	move.l	a0,a1			* make copy for plpaw1
    	move.l	a0,a2			* make copy for plpaw2
    	addq.l	#$3,a2			* plpaw2 points to end of longword

    	move.l	a0,d1			* byte or word misaligned?
    	btst	#$0,d1
    	bne.w	casl2			* byte misaligned

    	move.l	d2,d3			* d3 = update[15:0]
    	swap	d2			* d2 = update[31:16]

    * mask interrupts levels 0-6. save old mask value.
    	move.w	sr,d7			* save current SR
    	ori.w	#$0700,sr		* inhibit interrupts

    * load the SFC and DFC with the appropriate mode.
    	movec	sfc,d6			* save old SFC/DFC
    	movec	d0,sfc			* load new sfc
    	movec	d0,dfc			* load new dfc

    * pre-load the operand ATC. no page faults should occur here because
    * _real_lock_page() should have taken care of this.
    	plpaw	(a1)			* load atc for ADDR
    	plpaw	(a2)			* load atc for ADDR+3

    * push the operand lines from the cache if they exist.
    	cpushl	dc,(a1)			* push dirty data
    	cpushl	dc,(a2)			* push dirty data

    * load the BUSCR values.
    	move.l	#$80000000,a1		* assert LOCK* buscr value
    	move.l	#$a0000000,a2		* assert LOCKE* buscr value
    	move.l	#$00000000,a3		* buscr unlock value

    	bra.b	CASL_ENTER		* start pre-loading icache

    *
    * D0 = dst operand <-
    * D1 = xxxxxxxx
    * D2 = update[31:16] operand
    * D3 = update[15:0]  operand
    * D4 = compare[31:0] operand
    * D5 = xxxxxxxx
    * D6 = old SFC/DFC
    * D7 = old SR
    * A0 = ADDR
    * A1 = bus LOCK*  value
    * A2 = bus LOCKE* value
    * A3 = bus unlock value
    * A4 = xxxxxxxx
    * A5 = xxxxxxxx
    *
    	align	$10,$51FC
    CASL_START:
    	movec	a1,buscr		* assert LOCK*
    	moves.l	(a0),d0			* fetch Dest[31:0]
    	cmp.l	d4,d0			* Dest - Compare
    	bne.b	CASL_NOUPDATE
    	bra.b	CASL_UPDATE
    CASL_ENTER:
    	bra.b	(*)+16

    CASL_UPDATE:
    	moves.w	d2,(a0)+		* Update[31:16] -> DEST
    	movec	a2,buscr		* assert LOCKE*
    	moves.w	d3,(a0)			* Update[15:0] -> DEST+0x2
    	bra.b	CASL_UPDATE2
    	bra.b	(*)+16

    CASL_UPDATE2:
    	movec	a3,buscr		* unlock the bus
    	bra.b	casl_update_done
    	nop
    	nop
    	nop
    	nop
    	bra.b	(*)+16

    CASL_NOUPDATE:
    	swap	d0			* get Dest[31:16]
    	moves.w	d0,(a0)+		* Dest[31:16] -> DEST
    	swap	d0			* get Dest[15:0]
    	movec	a2,buscr		* assert LOCKE*
    	bra.b	CASL_NOUPDATE2
    	bra.b	(*)+16

    CASL_NOUPDATE2:
    	moves.w	d0,(a0)			* Dest[15:0] -> DEST+0x2
    	movec	a3,buscr		* unlock the bus
    	bra.b	casl_noupdate_done
    	nop
    	nop
    	bra.b	(*)+16

    CASL_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CASL_START

    *################################################################
    * THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON	#
    * CALLING _isp_cas_finish().					#
    *								#
    * D0 = destination[31:0] operand				#
    * D1 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required	#
    * D2 = xxxxxxxx							#
    * D3 = xxxxxxxx							#
    * D4 = compare[31:0] operand					#
    * D5 = xxxxxxxx							#
    * D6 = xxxxxxxx							#
    * D7 = xxxxxxxx							#
    * A0 = xxxxxxxx							#
    * A1 = xxxxxxxx							#
    * A2 = xxxxxxxx							#
    * A3 = xxxxxxxx							#
    * A4 = xxxxxxxx							#
    * A5 = xxxxxxxx							#
    * A6 = frame pointer						#
    * A7 = stack pointer						#
    *################################################################

    casl_noupdate_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupt mask level.
    	move.w	d7,sr			* restore old SR

    	sf	d1			* indicate no update was done
    	bra.l	_isp_cas_finish

    casl_update_done:

    * restore previous SFC/DFC value.
    	movec	d6,sfc			* restore old SFC
    	movec	d6,dfc			* restore old DFC

    * restore previous interrupts mask level.
    	move.w	d7,sr			* restore old SR

    	st	d1			* indicate update was done
    	bra.l	_isp_cas_finish

    *######################################
    casl2:
    	move.l	d2,d5			* d5 = Update[7:0]
    	lsr.l	#$8,d2
    	move.l	d2,d3			* d3 = Update[23:8]
    	swap	d2			* d2 = Update[31:24]

    * mask interrupts levels 0-6. save old mask value.
    	move.w	sr,d7			* save current SR
    	ori.w	#$0700,sr		* inhibit interrupts

    * load the SFC and DFC with the appropriate mode.
    	movec	sfc,d6			* save old SFC/DFC
    	movec	d0,sfc			* load new sfc
    	movec	d0,dfc			* load new dfc

    * pre-load the operand ATC. no page faults should occur here because
    * _real_lock_page() should have taken care of this already.
    	plpaw	(a1)			* load atc for ADDR
    	plpaw	(a2)			* load atc for ADDR+3

    * puch the operand lines from the cache if they exist.
    	cpushl	dc,(a1)			* push dirty data
    	cpushl	dc,(a2)			* push dirty data

    * load the BUSCR values.
    	move.l	#$80000000,a1		* assert LOCK* buscr value
    	move.l	#$a0000000,a2		* assert LOCKE* buscr value
    	move.l	#$00000000,a3		* buscr unlock value

    * pre-load the instruction cache for the following algorithm.
    * this will minimize the number of cycles that LOCK* will be asserted.
    	bra.b	CASL2_ENTER		* start pre-loading icache

    *
    * D0 = dst operand <-
    * D1 = xxxxxxxx
    * D2 = update[31:24] operand
    * D3 = update[23:8]  operand
    * D4 = compare[31:0] operand
    * D5 = update[7:0]  operand
    * D6 = old SFC/DFC
    * D7 = old SR
    * A0 = ADDR
    * A1 = bus LOCK*  value
    * A2 = bus LOCKE* value
    * A3 = bus unlock value
    * A4 = xxxxxxxx
    * A5 = xxxxxxxx
    *
    	align	$10,$51FC
    CASL2_START:
    	movec	a1,buscr		* assert LOCK*
    	moves.l	(a0),d0			* fetch Dest[31:0]
    	cmp.l	d4,d0			* Dest - Compare
    	bne.b	CASL2_NOUPDATE
    	bra.b	CASL2_UPDATE
    CASL2_ENTER:
    	bra.b	(*)+16

    CASL2_UPDATE:
    	moves.b	d2,(a0)+		* Update[31:24] -> DEST
    	moves.w	d3,(a0)+		* Update[23:8] -> DEST+0x1
    	movec	a2,buscr		* assert LOCKE*
    	bra.b	CASL2_UPDATE2
    	bra.b	(*)+16

    CASL2_UPDATE2:
    	moves.b	d5,(a0)			* Update[7:0] -> DEST+0x3
    	movec	a3,buscr		* unlock the bus
    	bra.w	casl_update_done
    	nop
    	bra.b	(*)+16

    CASL2_NOUPDATE:
    	rol.l	#$8,d0			* get Dest[31:24]
    	moves.b	d0,(a0)+		* Dest[31:24] -> DEST
    	swap	d0			* get Dest[23:8]
    	moves.w	d0,(a0)+		* Dest[23:8] -> DEST+0x1
    	bra.b	CASL2_NOUPDATE2
    	bra.b	(*)+16

    CASL2_NOUPDATE2:
    	rol.l	#$8,d0			* get Dest[7:0]
    	movec	a2,buscr		* assert LOCKE*
    	moves.b	d0,(a0)			* Dest[7:0] -> DEST+0x3
    	bra.b	CASL2_NOUPDATE3
    	nop
    	bra.b	(*)+16

    CASL2_NOUPDATE3:
    	movec	a3,buscr		* unlock the bus
    	bra.w	casl_noupdate_done
    	nop
    	nop
    	nop
    	bra.b	(*)+16

    CASL2_FILLER:
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	nop
    	bra.b	CASL2_START

    *###
    *###
    * end label used by _isp_cas_inrange()
    	global	_CASHI
    _CASHI:

STUDIO KAMADA
Copyright © 1999-2026 Makoto Kamada