4310 lines
124 KiB
ArmAsm
4310 lines
124 KiB
ArmAsm
#
|
|
# $NetBSD: isp.s,v 1.2 1996/05/15 19:48:48 is Exp $
|
|
#
|
|
|
|
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
# 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.
|
|
#
|
|
|
|
set _off_chk, 0x00
|
|
set _off_divbyzero, 0x04
|
|
set _off_trace, 0x08
|
|
set _off_access, 0x0c
|
|
set _off_done, 0x10
|
|
|
|
set _off_cas, 0x14
|
|
set _off_cas2, 0x18
|
|
set _off_lock, 0x1c
|
|
set _off_unlock, 0x20
|
|
|
|
set _off_imr, 0x40
|
|
set _off_dmr, 0x44
|
|
set _off_dmw, 0x48
|
|
set _off_irw, 0x4c
|
|
set _off_irl, 0x50
|
|
set _off_drb, 0x54
|
|
set _off_drw, 0x58
|
|
set _off_drl, 0x5c
|
|
set _off_dwb, 0x60
|
|
set _off_dww, 0x64
|
|
set _off_dwl, 0x68
|
|
|
|
_060ISP_TABLE:
|
|
|
|
# Here's the table of ENTRY POINTS for those linking the package.
|
|
bra.l _isp_unimp
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas2
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas_finish
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas2_finish
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas_inrange
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas_terminate
|
|
short 0x0000
|
|
|
|
bra.l _isp_cas_restart
|
|
short 0x0000
|
|
|
|
space 64
|
|
|
|
#############################################################
|
|
|
|
global _real_chk
|
|
_real_chk:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_chk,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_divbyzero
|
|
_real_divbyzero:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_divbyzero,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_trace
|
|
_real_trace:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_trace,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_access
|
|
_real_access:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_access,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _isp_done
|
|
_isp_done:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_done,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
#######################################
|
|
|
|
global _real_cas
|
|
_real_cas:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_cas,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_cas2
|
|
_real_cas2:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_cas2,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_lock_page
|
|
_real_lock_page:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_lock,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _real_unlock_page
|
|
_real_unlock_page:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_unlock,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
#######################################
|
|
|
|
global _imem_read
|
|
_imem_read:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_imr,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_read
|
|
_dmem_read:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_dmr,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_write
|
|
_dmem_write:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_dmw,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _imem_read_word
|
|
_imem_read_word:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_irw,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _imem_read_long
|
|
_imem_read_long:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_irl,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_read_byte
|
|
_dmem_read_byte:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_drb,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_read_word
|
|
_dmem_read_word:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_drw,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_read_long
|
|
_dmem_read_long:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_drl,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_write_byte
|
|
_dmem_write_byte:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_dwb,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_write_word
|
|
_dmem_write_word:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_dww,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
global _dmem_write_long
|
|
_dmem_write_long:
|
|
mov.l %d0,-(%sp)
|
|
mov.l (_060ISP_TABLE-0x80+_off_dwl,%pc),%d0
|
|
pea.l (_060ISP_TABLE-0x80,%pc,%d0)
|
|
mov.l 0x4(%sp),%d0
|
|
rtd &0x4
|
|
|
|
#
|
|
# This file contains a set of define statements for constants
|
|
# in oreder to promote readability within the core code itself.
|
|
#
|
|
|
|
set LOCAL_SIZE, 96 # stack frame size(bytes)
|
|
set LV, -LOCAL_SIZE # stack offset
|
|
|
|
set EXC_ISR, 0x4 # stack status register
|
|
set EXC_IPC, 0x6 # stack pc
|
|
set EXC_IVOFF, 0xa # stacked vector offset
|
|
|
|
set EXC_AREGS, LV+64 # offset of all address regs
|
|
set EXC_DREGS, LV+32 # offset of all data regs
|
|
|
|
set EXC_A7, EXC_AREGS+(7*4) # offset of a7
|
|
set EXC_A6, EXC_AREGS+(6*4) # offset of a6
|
|
set EXC_A5, EXC_AREGS+(5*4) # offset of a5
|
|
set EXC_A4, EXC_AREGS+(4*4) # offset of a4
|
|
set EXC_A3, EXC_AREGS+(3*4) # offset of a3
|
|
set EXC_A2, EXC_AREGS+(2*4) # offset of a2
|
|
set EXC_A1, EXC_AREGS+(1*4) # offset of a1
|
|
set EXC_A0, EXC_AREGS+(0*4) # offset of a0
|
|
set EXC_D7, EXC_DREGS+(7*4) # offset of d7
|
|
set EXC_D6, EXC_DREGS+(6*4) # offset of d6
|
|
set EXC_D5, EXC_DREGS+(5*4) # offset of d5
|
|
set EXC_D4, EXC_DREGS+(4*4) # offset of d4
|
|
set EXC_D3, EXC_DREGS+(3*4) # offset of d3
|
|
set EXC_D2, EXC_DREGS+(2*4) # offset of d2
|
|
set EXC_D1, EXC_DREGS+(1*4) # offset of d1
|
|
set EXC_D0, EXC_DREGS+(0*4) # offset of d0
|
|
|
|
set EXC_TEMP, LV+16 # offset of temp stack space
|
|
|
|
set EXC_SAVVAL, LV+12 # offset of old areg value
|
|
set EXC_SAVREG, LV+11 # offset of old areg index
|
|
|
|
set SPCOND_FLG, LV+10 # offset of spc condition flg
|
|
|
|
set EXC_CC, LV+8 # offset of cc register
|
|
set EXC_EXTWPTR, LV+4 # offset of current PC
|
|
set EXC_EXTWORD, LV+2 # offset of current ext opword
|
|
set EXC_OPWORD, LV+0 # offset of current opword
|
|
|
|
###########################
|
|
# SPecial CONDition FLaGs #
|
|
###########################
|
|
set mia7_flg, 0x04 # (a7)+ flag
|
|
set mda7_flg, 0x08 # -(a7) flag
|
|
set ichk_flg, 0x10 # chk exception flag
|
|
set idbyz_flg, 0x20 # divbyzero flag
|
|
set restore_flg, 0x40 # restore -(an)+ flag
|
|
set immed_flg, 0x80 # immediate data flag
|
|
|
|
set mia7_bit, 0x2 # (a7)+ bit
|
|
set mda7_bit, 0x3 # -(a7) bit
|
|
set ichk_bit, 0x4 # chk exception bit
|
|
set idbyz_bit, 0x5 # divbyzero bit
|
|
set restore_bit, 0x6 # restore -(a7)+ bit
|
|
set immed_bit, 0x7 # immediate data bit
|
|
|
|
#########
|
|
# Misc. #
|
|
#########
|
|
set BYTE, 1 # len(byte) == 1 byte
|
|
set WORD, 2 # len(word) == 2 bytes
|
|
set LONG, 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
|
|
|
|
movm.l &0x3fff,EXC_DREGS(%a6) # store d0-d7/a0-a5
|
|
mov.l (%a6),EXC_A6(%a6) # store a6
|
|
|
|
btst &0x5,EXC_ISR(%a6) # from s or u mode?
|
|
bne.b uieh_s # supervisor mode
|
|
uieh_u:
|
|
mov.l %usp,%a0 # fetch user stack pointer
|
|
mov.l %a0,EXC_A7(%a6) # store a7
|
|
bra.b uieh_cont
|
|
uieh_s:
|
|
lea 0xc(%a6),%a0
|
|
mov.l %a0,EXC_A7(%a6) # store corrected sp
|
|
|
|
###############################################################################
|
|
|
|
uieh_cont:
|
|
clr.b SPCOND_FLG(%a6) # clear "special case" flag
|
|
|
|
mov.w EXC_ISR(%a6),EXC_CC(%a6) # store cc copy on stack
|
|
mov.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
|
|
#
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_long # fetch opword & extword
|
|
mov.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 &0x1e,%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 &0x16,%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 &0x5,EXC_ISR(%a6) # supervisor mode?
|
|
beq.w uieh_done
|
|
btst &mia7_bit,SPCOND_FLG(%a6) # was a7 changed?
|
|
beq.w uieh_done # no
|
|
btst &0x7,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 &0x5,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 &0x18,%d0 # test for not movep
|
|
beq.b uieh_not_movep
|
|
|
|
|
|
bsr.l _moveperipheral # _movep()
|
|
bra.w uieh_done
|
|
|
|
uieh_not_movep:
|
|
btst &0x1b,%d0 # test for chk2,cmp2
|
|
beq.b uieh_chk2cmp2 # go handle chk2,cmp2
|
|
|
|
swap %d0 # put opword in lo word
|
|
cmpi.b %d0,&0xfc # 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:
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,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 SPCOND_FLG(%a6),&ichk_flg
|
|
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:
|
|
mov.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 &0x5,EXC_ISR(%a6) # user or supervisor?
|
|
bne.b uieh_finish # supervisor
|
|
|
|
mov.l EXC_A7(%a6),%a0 # fetch user stack pointer
|
|
mov.l %a0,%usp # restore it
|
|
|
|
uieh_finish:
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
btst &0x7,EXC_ISR(%a6) # is trace mode on?
|
|
bne.b uieh_trace # yes;go handle trace mode
|
|
|
|
mov.l EXC_EXTWPTR(%a6),EXC_IPC(%a6) # new pc on stack frame
|
|
mov.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:
|
|
mov.l EXC_A6(%a6),-0x4(%a6)
|
|
mov.w EXC_ISR(%a6),0x0(%a6)
|
|
mov.l EXC_IPC(%a6),0x8(%a6)
|
|
mov.l EXC_EXTWPTR(%a6),0x2(%a6)
|
|
mov.w &0x2024,0x6(%a6)
|
|
sub.l &0x4,%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:
|
|
mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
mov.w EXC_ISR(%a6),(%a6) # put new SR on stack
|
|
mov.l EXC_IPC(%a6),0x8(%a6) # put "Current PC" on stack
|
|
mov.l EXC_EXTWPTR(%a6),0x2(%a6) # put "Next PC" on stack
|
|
mov.w &0x2018,0x6(%a6) # put Vector Offset on stack
|
|
|
|
mov.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:
|
|
mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
mov.w EXC_ISR(%a6),(%a6) # put new SR on stack
|
|
mov.l EXC_IPC(%a6),0x8(%a6) # put "Current PC" on stack
|
|
mov.l EXC_EXTWPTR(%a6),0x2(%a6) # put "Next PC" on stack
|
|
mov.w &0x2014,0x6(%a6) # put Vector Offset on stack
|
|
|
|
mov.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:
|
|
mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
mov.l EXC_IPC(%a6),0xc(%a6) # put "Current PC" on stack
|
|
mov.w &0x2014,0xa(%a6) # put Vector Offset on stack
|
|
mov.l EXC_EXTWPTR(%a6),0x6(%a6) # put "Next PC" on stack
|
|
|
|
mov.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:
|
|
mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
mov.l EXC_IPC(%a6),0xc(%a6) # put "Current PC" on stack
|
|
mov.w &0x2024,0xa(%a6) # put Vector Offset on stack
|
|
mov.l EXC_EXTWPTR(%a6),0x6(%a6) # put "Next PC" on stack
|
|
|
|
mov.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:
|
|
mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
|
|
mov.w &0x00f4,0xe(%a6) # put Vector Offset on stack
|
|
mov.l EXC_EXTWPTR(%a6),0xa(%a6) # put "Next PC" on stack
|
|
mov.w EXC_ISR(%a6),0x8(%a6) # put SR on stack
|
|
|
|
mov.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:
|
|
mov.l %a0,(%a6) # save address
|
|
mov.l %d0,-0x4(%a6) # save partial fslw
|
|
|
|
lea -64(%a6),%sp
|
|
movm.l (%sp)+,&0x7fff # restore d0-d7/a0-a6
|
|
|
|
mov.l 0xc(%sp),-(%sp) # move voff,hi(pc)
|
|
mov.l 0x4(%sp),0x10(%sp) # store fslw
|
|
mov.l 0xc(%sp),0x4(%sp) # store sr,lo(pc)
|
|
mov.l 0x8(%sp),0xc(%sp) # store address
|
|
mov.l (%sp)+,0x4(%sp) # store voff,hi(pc)
|
|
mov.w &0x4008,0x6(%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:
|
|
movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5
|
|
unlk %a6 # unlink frame
|
|
sub.w &0x8,%sp # make room for acc frame
|
|
mov.l 0x8(%sp),(%sp) # store sr,lo(pc)
|
|
mov.w 0xc(%sp),0x4(%sp) # store hi(pc)
|
|
mov.w &0x4008,0x6(%sp) # store new voff
|
|
mov.l 0x2(%sp),0x8(%sp) # store address (=pc)
|
|
mov.l &0x09428001,0xc(%sp) # store fslw
|
|
|
|
isp_acc_exit:
|
|
btst &0x5,(%sp) # user or supervisor?
|
|
beq.b isp_acc_exit2 # user
|
|
bset &0x2,0xd(%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 SPCOND_FLG(%a6),&restore_flg # do we need a restore?
|
|
bne.b isp_restore_done # no
|
|
clr.l %d0
|
|
mov.b EXC_SAVREG(%a6),%d0 # regno to restore
|
|
mov.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:
|
|
mov.l %d0,%a0 # move # bytes to a0
|
|
|
|
# MODE and REG are taken from the EXC_OPWORD.
|
|
mov.w EXC_OPWORD(%a6),%d0 # fetch opcode word
|
|
mov.w %d0,%d1 # make a copy
|
|
|
|
andi.w &0x3f,%d0 # extract mode field
|
|
andi.l &0x7,%d1 # extract reg field
|
|
|
|
# jump to the corresponding function for each {MODE,REG} pair.
|
|
mov.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
|
|
|
|
swbeg &64
|
|
tbl_ea_mode:
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
|
|
short addr_ind_a0 - tbl_ea_mode
|
|
short addr_ind_a1 - tbl_ea_mode
|
|
short addr_ind_a2 - tbl_ea_mode
|
|
short addr_ind_a3 - tbl_ea_mode
|
|
short addr_ind_a4 - tbl_ea_mode
|
|
short addr_ind_a5 - tbl_ea_mode
|
|
short addr_ind_a6 - tbl_ea_mode
|
|
short addr_ind_a7 - tbl_ea_mode
|
|
|
|
short addr_ind_p_a0 - tbl_ea_mode
|
|
short addr_ind_p_a1 - tbl_ea_mode
|
|
short addr_ind_p_a2 - tbl_ea_mode
|
|
short addr_ind_p_a3 - tbl_ea_mode
|
|
short addr_ind_p_a4 - tbl_ea_mode
|
|
short addr_ind_p_a5 - tbl_ea_mode
|
|
short addr_ind_p_a6 - tbl_ea_mode
|
|
short addr_ind_p_a7 - tbl_ea_mode
|
|
|
|
short addr_ind_m_a0 - tbl_ea_mode
|
|
short addr_ind_m_a1 - tbl_ea_mode
|
|
short addr_ind_m_a2 - tbl_ea_mode
|
|
short addr_ind_m_a3 - tbl_ea_mode
|
|
short addr_ind_m_a4 - tbl_ea_mode
|
|
short addr_ind_m_a5 - tbl_ea_mode
|
|
short addr_ind_m_a6 - tbl_ea_mode
|
|
short addr_ind_m_a7 - tbl_ea_mode
|
|
|
|
short addr_ind_disp_a0 - tbl_ea_mode
|
|
short addr_ind_disp_a1 - tbl_ea_mode
|
|
short addr_ind_disp_a2 - tbl_ea_mode
|
|
short addr_ind_disp_a3 - tbl_ea_mode
|
|
short addr_ind_disp_a4 - tbl_ea_mode
|
|
short addr_ind_disp_a5 - tbl_ea_mode
|
|
short addr_ind_disp_a6 - tbl_ea_mode
|
|
short addr_ind_disp_a7 - tbl_ea_mode
|
|
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
short _addr_ind_ext - tbl_ea_mode
|
|
|
|
short abs_short - tbl_ea_mode
|
|
short abs_long - tbl_ea_mode
|
|
short pc_ind - tbl_ea_mode
|
|
short pc_ind_ext - tbl_ea_mode
|
|
short immediate - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
short tbl_ea_mode - tbl_ea_mode
|
|
|
|
###################################
|
|
# Address register indirect: (An) #
|
|
###################################
|
|
addr_ind_a0:
|
|
mov.l EXC_A0(%a6),%a0 # Get current a0
|
|
rts
|
|
|
|
addr_ind_a1:
|
|
mov.l EXC_A1(%a6),%a0 # Get current a1
|
|
rts
|
|
|
|
addr_ind_a2:
|
|
mov.l EXC_A2(%a6),%a0 # Get current a2
|
|
rts
|
|
|
|
addr_ind_a3:
|
|
mov.l EXC_A3(%a6),%a0 # Get current a3
|
|
rts
|
|
|
|
addr_ind_a4:
|
|
mov.l EXC_A4(%a6),%a0 # Get current a4
|
|
rts
|
|
|
|
addr_ind_a5:
|
|
mov.l EXC_A5(%a6),%a0 # Get current a5
|
|
rts
|
|
|
|
addr_ind_a6:
|
|
mov.l EXC_A6(%a6),%a0 # Get current a6
|
|
rts
|
|
|
|
addr_ind_a7:
|
|
mov.l EXC_A7(%a6),%a0 # Get current a7
|
|
rts
|
|
|
|
#####################################################
|
|
# Address register indirect w/ postincrement: (An)+ #
|
|
#####################################################
|
|
addr_ind_p_a0:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A0(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A0(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x0,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a1:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A1(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A1(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x1,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a2:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A2(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A2(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x2,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a3:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A3(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A3(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x3,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a4:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A4(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A4(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x4,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a5:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A5(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A5(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x5,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a6:
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A6(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A6(%a6) # save incremented value
|
|
|
|
mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error
|
|
mov.b &0x6,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_p_a7:
|
|
mov.b &mia7_flg,SPCOND_FLG(%a6) # set "special case" flag
|
|
|
|
mov.l %a0,%d0 # copy no. bytes
|
|
mov.l EXC_A7(%a6),%a0 # load current value
|
|
add.l %a0,%d0 # increment
|
|
mov.l %d0,EXC_A7(%a6) # save incremented value
|
|
rts
|
|
|
|
####################################################
|
|
# Address register indirect w/ predecrement: -(An) #
|
|
####################################################
|
|
addr_ind_m_a0:
|
|
mov.l EXC_A0(%a6),%d0 # Get current a0
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A0(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x0,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a1:
|
|
mov.l EXC_A1(%a6),%d0 # Get current a1
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A1(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x1,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a2:
|
|
mov.l EXC_A2(%a6),%d0 # Get current a2
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A2(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x2,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a3:
|
|
mov.l EXC_A3(%a6),%d0 # Get current a3
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A3(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x3,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a4:
|
|
mov.l EXC_A4(%a6),%d0 # Get current a4
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A4(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x4,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a5:
|
|
mov.l EXC_A5(%a6),%d0 # Get current a5
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A5(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x5,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a6:
|
|
mov.l EXC_A6(%a6),%d0 # Get current a6
|
|
mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A6(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
|
|
mov.b &0x6,EXC_SAVREG(%a6) # save regno, too
|
|
mov.b &restore_flg,SPCOND_FLG(%a6) # set flag
|
|
rts
|
|
|
|
addr_ind_m_a7:
|
|
mov.b &mda7_flg,SPCOND_FLG(%a6) # set "special case" flag
|
|
|
|
mov.l EXC_A7(%a6),%d0 # Get current a7
|
|
sub.l %a0,%d0 # Decrement
|
|
mov.l %d0,EXC_A7(%a6) # Save decr value
|
|
mov.l %d0,%a0
|
|
rts
|
|
|
|
########################################################
|
|
# Address register indirect w/ displacement: (d16, An) #
|
|
########################################################
|
|
addr_ind_disp_a0:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A0(%a6),%a0 # a0 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a1:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A1(%a6),%a0 # a1 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a2:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A2(%a6),%a0 # a2 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a3:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A3(%a6),%a0 # a3 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a4:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A4(%a6),%a0 # a4 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a5:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A5(%a6),%a0 # a5 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a6:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # sign extend displacement
|
|
add.l EXC_A6(%a6),%a0 # a6 + d16
|
|
rts
|
|
|
|
addr_ind_disp_a7:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.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:
|
|
mov.l %d1,-(%sp)
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,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
|
|
|
|
mov.l (%sp)+,%d1
|
|
|
|
mov.l (EXC_AREGS,%a6,%d1.w*4),%a0 # put base in a0
|
|
|
|
btst &0x8,%d0
|
|
beq.b addr_ind_index_8bit # for ext word or not?
|
|
|
|
movm.l &0x3c00,-(%sp) # save d2-d5
|
|
|
|
mov.l %d0,%d5 # put extword in d5
|
|
mov.l %a0,%d3 # put base in d3
|
|
|
|
bra.l calc_mem_ind # calc memory indirect
|
|
|
|
addr_ind_index_8bit:
|
|
mov.l %d2,-(%sp) # save old d2
|
|
|
|
mov.l %d0,%d1
|
|
rol.w &0x4,%d1
|
|
andi.w &0xf,%d1 # extract index regno
|
|
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4),%d1 # fetch index reg value
|
|
|
|
btst &0xb,%d0 # is it word or long?
|
|
bne.b aii8_long
|
|
ext.l %d1 # sign extend word index
|
|
aii8_long:
|
|
mov.l %d0,%d2
|
|
rol.w &0x7,%d2
|
|
andi.l &0x3,%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)
|
|
|
|
mov.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:
|
|
mov.b &immed_flg,SPCOND_FLG(%a6) # set immediate flag
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch extension word ptr
|
|
rts
|
|
|
|
###########################
|
|
# Absolute short: (XXX).W #
|
|
###########################
|
|
abs_short:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word # fetch short address
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.w %d0,%a0 # return <ea> in a0
|
|
rts
|
|
|
|
##########################
|
|
# Absolute long: (XXX).L #
|
|
##########################
|
|
abs_long:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_long # fetch long address
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.l %d0,%a0 # return <ea> in a0
|
|
rts
|
|
|
|
#######################################################
|
|
# Program counter indirect w/ displacement: (d16, PC) #
|
|
#######################################################
|
|
pc_ind:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word # fetch word displacement
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.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 &0x2,%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:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr
|
|
bsr.l _imem_read_word # fetch ext word
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # put base in a0
|
|
subq.l &0x2,%a0 # adjust base
|
|
|
|
btst &0x8,%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
|
|
movm.l &0x3c00,-(%sp) # save d2-d5
|
|
|
|
mov.l %d0,%d5 # put extword in d5
|
|
mov.l %a0,%d3 # put base in d3
|
|
|
|
bra.l calc_mem_ind # calc memory indirect
|
|
|
|
pc_ind_index_8bit:
|
|
mov.l %d2,-(%sp) # create a temp register
|
|
|
|
mov.l %d0,%d1 # make extword copy
|
|
rol.w &0x4,%d1 # rotate reg num into place
|
|
andi.w &0xf,%d1 # extract register number
|
|
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4),%d1 # fetch index reg value
|
|
|
|
btst &0xb,%d0 # is index word or long?
|
|
bne.b pii8_long # long
|
|
ext.l %d1 # sign extend word index
|
|
pii8_long:
|
|
mov.l %d0,%d2 # make extword copy
|
|
rol.w &0x7,%d2 # rotate scale value into place
|
|
andi.l &0x3,%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)
|
|
|
|
mov.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 &0x6,%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
|
|
mov.l (EXC_DREGS,%a6,%d2.w*4),%d2
|
|
btst &0xb,%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 &0x7,%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 %d0,&2
|
|
blt.b no_bd
|
|
beq.b get_word_bd
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x4,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:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,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 %d0,&0x2
|
|
blt.b null_od
|
|
beq.b word_od
|
|
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x4,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:
|
|
mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr
|
|
addq.l &0x2,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:
|
|
mov.l %d0,%d4
|
|
btst &0x2,%d5 # pre or post indexing?
|
|
beq.b pre_indexed
|
|
|
|
mov.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
|
|
mov.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
|
|
mov.l %d3,%d0
|
|
done_ea:
|
|
mov.l %d0,%a0
|
|
|
|
movm.l (%sp)+,&0x003c # 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:
|
|
mov.l %d3,%a0 # pass failing address
|
|
mov.l &0x01010001,%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:
|
|
mov.w EXC_OPWORD(%a6),%d1 # fetch the opcode word
|
|
|
|
mov.b %d1,%d0
|
|
and.w &0x7,%d0 # extract Ay from opcode word
|
|
|
|
mov.l (EXC_AREGS,%a6,%d0.w*4),%a0 # fetch ay
|
|
|
|
add.w EXC_EXTWORD(%a6),%a0 # add: an + sgn_ext(disp)
|
|
|
|
btst &0x7,%d1 # (reg 2 mem) or (mem 2 reg)
|
|
beq.w mem2reg
|
|
|
|
# reg2mem: fetch dx, then write it to memory
|
|
reg2mem:
|
|
mov.w %d1,%d0
|
|
rol.w &0x7,%d0
|
|
and.w &0x7,%d0 # extract Dx from opcode word
|
|
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4), %d0 # fetch dx
|
|
|
|
btst &0x6,%d1 # word or long operation?
|
|
beq.b r2mwtrans
|
|
|
|
# a0 = dst addr
|
|
# d0 = Dx
|
|
r2mltrans:
|
|
mov.l %d0,%d2 # store data
|
|
mov.l %a0,%a2 # store addr
|
|
rol.l &0x8,%d2
|
|
mov.l %d2,%d0
|
|
|
|
bsr.l _dmem_write_byte # os : write hi
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_write_err # yes
|
|
|
|
add.w &0x2,%a2 # incr addr
|
|
mov.l %a2,%a0
|
|
rol.l &0x8,%d2
|
|
mov.l %d2,%d0
|
|
|
|
bsr.l _dmem_write_byte # os : write lo
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_write_err # yes
|
|
|
|
add.w &0x2,%a2 # incr addr
|
|
mov.l %a2,%a0
|
|
rol.l &0x8,%d2
|
|
mov.l %d2,%d0
|
|
|
|
bsr.l _dmem_write_byte # os : write lo
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_write_err # yes
|
|
|
|
add.w &0x2,%a2 # incr addr
|
|
mov.l %a2,%a0
|
|
rol.l &0x8,%d2
|
|
mov.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:
|
|
mov.l %d0,%d2 # store data
|
|
mov.l %a0,%a2 # store addr
|
|
lsr.w &0x8,%d0
|
|
|
|
bsr.l _dmem_write_byte # os : write hi
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_write_err # yes
|
|
|
|
add.w &0x2,%a2
|
|
mov.l %a2,%a0
|
|
mov.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 &0x6,%d1 # word or long operation?
|
|
beq.b m2rwtrans
|
|
|
|
# a0 = dst addr
|
|
m2rltrans:
|
|
mov.l %a0,%a2 # store addr
|
|
|
|
bsr.l _dmem_read_byte # read first byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
mov.l %d0,%d2
|
|
|
|
add.w &0x2,%a2 # incr addr by 2 bytes
|
|
mov.l %a2,%a0
|
|
|
|
bsr.l _dmem_read_byte # read second byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
lsl.w &0x8,%d2
|
|
mov.b %d0,%d2 # append bytes
|
|
|
|
add.w &0x2,%a2 # incr addr by 2 bytes
|
|
mov.l %a2,%a0
|
|
|
|
bsr.l _dmem_read_byte # read second byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
lsl.l &0x8,%d2
|
|
mov.b %d0,%d2 # append bytes
|
|
|
|
add.w &0x2,%a2 # incr addr by 2 bytes
|
|
mov.l %a2,%a0
|
|
|
|
bsr.l _dmem_read_byte # read second byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
lsl.l &0x8,%d2
|
|
mov.b %d0,%d2 # append bytes
|
|
|
|
mov.b EXC_OPWORD(%a6),%d1
|
|
lsr.b &0x1,%d1
|
|
and.w &0x7,%d1 # extract Dx from opcode word
|
|
|
|
mov.l %d2,(EXC_DREGS,%a6,%d1.w*4) # store dx
|
|
|
|
rts
|
|
|
|
# a0 = dst addr
|
|
m2rwtrans:
|
|
mov.l %a0,%a2 # store addr
|
|
|
|
bsr.l _dmem_read_byte # read first byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
mov.l %d0,%d2
|
|
|
|
add.w &0x2,%a2 # incr addr by 2 bytes
|
|
mov.l %a2,%a0
|
|
|
|
bsr.l _dmem_read_byte # read second byte
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w movp_read_err # yes
|
|
|
|
lsl.w &0x8,%d2
|
|
mov.b %d0,%d2 # append bytes
|
|
|
|
mov.b EXC_OPWORD(%a6),%d1
|
|
lsr.b &0x1,%d1
|
|
and.w &0x7,%d1 # extract Dx from opcode word
|
|
|
|
mov.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:
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x00a10001,%d0 # pass fslw
|
|
bra.l isp_dacc
|
|
|
|
# FSLW:
|
|
# read = true
|
|
# size = byte
|
|
# TM = data
|
|
# software emulation error = true
|
|
movp_read_err:
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x01210001,%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>
|
|
|
|
mov.b EXC_EXTWORD(%a6), %d0 # fetch hi extension word
|
|
rol.b &0x4, %d0 # rotate reg bits into lo
|
|
and.w &0xf, %d0 # extract reg bits
|
|
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4), %d2 # get regval
|
|
|
|
cmpi.b EXC_OPWORD(%a6), &0x2 # 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:
|
|
mov.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
|
|
|
|
mov.l %d0,%d3 # save long lower bound
|
|
addq.l &0x4,%a2
|
|
mov.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
|
|
|
|
mov.l %d0,%d1 # long upper bound in d1
|
|
mov.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:
|
|
mov.l %a0,%a2
|
|
bsr.l _dmem_read_long # fetch 2 word bounds
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w chk2_cmp2_err_l # yes
|
|
|
|
mov.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 &0x7, 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:
|
|
mov.l %a0,%a2
|
|
bsr.l _dmem_read_word # fetch 2 byte bounds
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w chk2_cmp2_err_w # yes
|
|
|
|
mov.b %d0, %d1 # place hi in %d1
|
|
lsr.w &0x8, %d0 # place lo in %d0
|
|
|
|
extb.l %d0 # sign extend lo bnd
|
|
extb.l %d1 # sign extend hi bnd
|
|
|
|
btst &0x7, 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)
|
|
mov.w %cc, %d3 # fetch resulting ccodes
|
|
andi.b &0x4, %d3 # keep 'Z' bit
|
|
sub.l %d0, %d1 # (hi - lo)
|
|
cmp.l %d1,%d2 # ((hi - lo) - (Rn - hi))
|
|
|
|
mov.w %cc, %d4 # fetch resulting ccodes
|
|
or.b %d4, %d3 # combine w/ earlier ccodes
|
|
andi.b &0x5, %d3 # keep 'Z' and 'N'
|
|
|
|
mov.w EXC_CC(%a6), %d4 # fetch old ccodes
|
|
andi.b &0x1a, %d4 # keep 'X','N','V' bits
|
|
or.b %d3, %d4 # insert new ccodes
|
|
mov.w %d4, EXC_CC(%a6) # save new ccodes
|
|
|
|
btst &0x3, 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 &0x0, %d4 # is 'N' bit set?
|
|
bne.b chk2_trap # yes;chk2 should trap
|
|
rts
|
|
chk2_trap:
|
|
mov.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:
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x01010001,%d0 # pass fslw
|
|
bra.l isp_dacc
|
|
|
|
# FSLW:
|
|
# read = true
|
|
# size = word
|
|
# TM = data
|
|
# software emulation error = true
|
|
chk2_cmp2_err_w:
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x01410001,%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.#
|
|
# #
|
|
#########################################################################
|
|
|
|
set NDIVISOR, EXC_TEMP+0x0
|
|
set NDIVIDEND, EXC_TEMP+0x1
|
|
set NDRSAVE, EXC_TEMP+0x2
|
|
set NDQSAVE, EXC_TEMP+0x4
|
|
set DDSECOND, EXC_TEMP+0x6
|
|
set DDQUOTIENT, EXC_TEMP+0x8
|
|
set DDNORMAL, EXC_TEMP+0xc
|
|
|
|
global _div64
|
|
#############
|
|
# div(u,s)l #
|
|
#############
|
|
_div64:
|
|
mov.b EXC_OPWORD+1(%a6), %d0
|
|
andi.b &0x38, %d0 # extract src mode
|
|
|
|
bne.w dcontrolmodel_s # %dn dest or control mode?
|
|
|
|
mov.b EXC_OPWORD+1(%a6), %d0 # extract Dn from opcode
|
|
andi.w &0x7, %d0
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4), %d7 # fetch divisor from register
|
|
|
|
dgotsrcl:
|
|
beq.w div64eq0 # divisor is = 0!!!
|
|
|
|
mov.b EXC_EXTWORD+1(%a6), %d0 # extract Dr from extword
|
|
mov.b EXC_EXTWORD(%a6), %d1 # extract Dq from extword
|
|
and.w &0x7, %d0
|
|
lsr.b &0x4, %d1
|
|
and.w &0x7, %d1
|
|
mov.w %d0, NDRSAVE(%a6) # save Dr for later
|
|
mov.w %d1, NDQSAVE(%a6) # save Dq for later
|
|
|
|
# fetch %dr and %dq directly off stack since all regs are saved there
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4), %d5 # get dividend hi
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4), %d6 # get dividend lo
|
|
|
|
# separate signed and unsigned divide
|
|
btst &0x3, 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
|
|
|
|
mov.w &0x0, %cc # 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 %d7,%d6 # 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:
|
|
tdivu.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 %d7,%d5
|
|
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 &0x3, 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:
|
|
mov.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 %d6, &0x80000000 # will (-quot) fit in 32 bits?
|
|
bhi.b ddovf
|
|
|
|
neg.l %d6 # make (-quot) 2's comp
|
|
|
|
bra.b ddone
|
|
|
|
dqpos:
|
|
btst &0x1f, %d6 # will (+quot) fit in 32 bits?
|
|
bne.b ddovf
|
|
|
|
ddone:
|
|
# at this point, result is normal so ccodes are set based on result.
|
|
mov.w EXC_CC(%a6), %cc
|
|
tst.l %d6 # set %ccode bits
|
|
mov.w %cc, EXC_CC(%a6)
|
|
|
|
mov.w NDRSAVE(%a6), %d0 # get Dr off stack
|
|
mov.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
|
|
mov.l %d5, (EXC_DREGS,%a6,%d0.w*4) # save remainder
|
|
mov.l %d6, (EXC_DREGS,%a6,%d1.w*4) # save quotient
|
|
|
|
rts
|
|
|
|
ddovf:
|
|
bset &0x1, EXC_CC+1(%a6) # 'V' set on overflow
|
|
bclr &0x0, EXC_CC+1(%a6) # 'C' cleared on overflow
|
|
|
|
rts
|
|
|
|
div64eq0:
|
|
andi.b &0x1e, 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 %d7, &0xffff
|
|
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
|
|
mov.w %d6, %d5 # rb + u3
|
|
|
|
divu.w %d7, %d5
|
|
|
|
mov.w %d5, %d1 # first quotient word
|
|
swap %d6 # get u4
|
|
mov.w %d6, %d5 # rb + u4
|
|
|
|
divu.w %d7, %d5
|
|
|
|
swap %d1
|
|
mov.w %d5, %d1 # 2nd quotient 'digit'
|
|
clr.w %d5
|
|
swap %d5 # now remainder
|
|
mov.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 &0x1, DDNORMAL(%a6) # count normalization shifts
|
|
lsl.l &0x1, %d7 # shift the divisor
|
|
lsl.l &0x1, %d6 # shift u4,u3 with overflow to u2
|
|
roxl.l &0x1, %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.
|
|
mov.l %d7, %d3 # divisor
|
|
mov.l %d5, %d2 # dividend mslw
|
|
swap %d2
|
|
swap %d3
|
|
cmp.w %d2, %d3 # V1 = U1 ?
|
|
bne.b ddqcalc1
|
|
mov.w &0xffff, %d1 # use max trial quotient word
|
|
bra.b ddadj0
|
|
ddqcalc1:
|
|
mov.l %d5, %d1
|
|
|
|
divu.w %d3, %d1 # use quotient of mslw/msw
|
|
|
|
andi.l &0x0000ffff, %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.
|
|
mov.l %d6, -(%sp)
|
|
clr.w %d6 # word u3 left
|
|
swap %d6 # in lsw position
|
|
ddadj1: mov.l %d7, %d3
|
|
mov.l %d1, %d2
|
|
mulu.w %d7, %d2 # V2q
|
|
swap %d3
|
|
mulu.w %d1, %d3 # V1q
|
|
mov.l %d5, %d4 # U1U2
|
|
sub.l %d3, %d4 # U1U2 - V1q
|
|
|
|
swap %d4
|
|
|
|
mov.w %d4,%d0
|
|
mov.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 %d2, %d4
|
|
bls.b ddadjd1 # is V2q > (U1U2-V1q) + U3 ?
|
|
subq.l &0x1, %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
|
|
mov.l %d5, -(%sp) # save %d5 (%d6 already saved)
|
|
mov.l %d1, %d6
|
|
swap %d6 # shift answer to ms 3 words
|
|
mov.l %d7, %d5
|
|
bsr.l dmm2
|
|
mov.l %d5, %d2 # now %d2,%d3 are trial*divisor
|
|
mov.l %d6, %d3
|
|
mov.l (%sp)+, %d5 # restore dividend
|
|
mov.l (%sp)+, %d6
|
|
sub.l %d3, %d6
|
|
subx.l %d2, %d5 # subtract double precision
|
|
bcc dd2nd # no carry, do next quotient digit
|
|
subq.l &0x1, %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
|
|
mov.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
|
|
mov.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
|
|
mov.w %d1, DDQUOTIENT(%a6)
|
|
clr.l %d1
|
|
swap %d5
|
|
swap %d6
|
|
mov.w %d6, %d5
|
|
clr.w %d6
|
|
st DDSECOND(%a6) # second digit
|
|
bra.w ddnormalized
|
|
ddremain:
|
|
# add 2nd word to quotient, get the remainder.
|
|
mov.w %d1, DDQUOTIENT+2(%a6)
|
|
# shift down one word/digit to renormalize remainder.
|
|
mov.w %d5, %d6
|
|
swap %d6
|
|
swap %d5
|
|
mov.l DDNORMAL(%a6), %d7 # get norm shift count
|
|
beq.b ddrn
|
|
subq.l &0x1, %d7 # set for loop count
|
|
ddnlp:
|
|
lsr.l &0x1, %d5 # shift into %d6
|
|
roxr.l &0x1, %d6
|
|
dbf %d7, ddnlp
|
|
ddrn:
|
|
mov.l %d6, %d5 # remainder
|
|
mov.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
|
|
mov.l %d6, %d2
|
|
mov.l %d6, %d3
|
|
mov.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:
|
|
movq.l &LONG,%d0
|
|
bsr.l _calc_ea # calc <ea>
|
|
|
|
cmpi.b SPCOND_FLG(%a6),&immed_flg # immediate addressing mode?
|
|
beq.b dimmed # yes
|
|
|
|
mov.l %a0,%a2
|
|
bsr.l _dmem_read_long # fetch divisor from <ea>
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.b div64_err # yes
|
|
|
|
mov.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 &0x4,EXC_EXTWPTR(%a6)
|
|
bsr.l _imem_read_long # read immediate value
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.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
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x01010001,%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:
|
|
mov.b EXC_OPWORD+1(%a6), %d0 # extract src {mode,reg}
|
|
cmpi.b %d0, &0x7 # 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 &0x7, %d0 # extract Dn
|
|
mov.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:
|
|
mov.w EXC_EXTWORD(%a6), %d2 # fetch ext word
|
|
clr.w %d1 # clear Dh reg
|
|
mov.b %d2, %d1 # grab Dh
|
|
rol.w &0x4, %d2 # align Dl byte
|
|
andi.w &0x7, %d2 # extract Dl
|
|
|
|
mov.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 &0x3, 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 &0x1, 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 &0x1, 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
|
|
mov.l %d3, %d5 # mr in %d5
|
|
mov.l %d3, %d6 # mr in %d6
|
|
mov.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:
|
|
mov.l %d3, (EXC_DREGS,%a6,%d2.w*4) # save lo(result)
|
|
mov.w &0x0, %cc
|
|
mov.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.
|
|
mov.w %cc, %d7 # fetch %ccr to see if 'N' set
|
|
andi.b &0x8, %d7 # extract 'N' bit
|
|
|
|
mul64_ccode_set:
|
|
mov.b EXC_CC+1(%a6), %d6 # fetch previous %ccr
|
|
andi.b &0x10, %d6 # all but 'X' bit changes
|
|
|
|
or.b %d7, %d6 # group 'X' and 'N'
|
|
mov.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)
|
|
|
|
movq.l &0x4, %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:
|
|
movq.l &LONG, %d0 # pass # of bytes
|
|
bsr.l _calc_ea # calculate <ea>
|
|
|
|
cmpi.b SPCOND_FLG(%a6),&immed_flg # immediate addressing mode?
|
|
beq.b mul64_immed # yes
|
|
|
|
mov.l %a0,%a2
|
|
bsr.l _dmem_read_long # fetch src from addr (%a0)
|
|
|
|
tst.l %d1 # dfetch error?
|
|
bne.w mul64_err # yes
|
|
|
|
mov.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 &0x4,EXC_EXTWPTR(%a6)
|
|
bsr.l _imem_read_long # read immediate value
|
|
|
|
tst.l %d1 # ifetch error?
|
|
bne.l isp_iacc # yes
|
|
|
|
mov.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
|
|
mov.l %a2,%a0 # pass failing address
|
|
mov.l &0x01010001,%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(). #
|
|
# #
|
|
#########################################################################
|
|
|
|
set ADDR1, EXC_TEMP+0xc
|
|
set ADDR2, EXC_TEMP+0x0
|
|
set DC2, EXC_TEMP+0xa
|
|
set DC1, EXC_TEMP+0x8
|
|
|
|
global _compandset2
|
|
_compandset2:
|
|
mov.l %d0,EXC_TEMP+0x4(%a6) # store for possible restart
|
|
mov.l %d0,%d1 # extension word in d0
|
|
|
|
rol.w &0x4,%d0
|
|
andi.w &0xf,%d0 # extract Rn2
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4),%a1 # fetch ADDR2
|
|
mov.l %a1,ADDR2(%a6)
|
|
|
|
mov.l %d1,%d0
|
|
|
|
lsr.w &0x6,%d1
|
|
andi.w &0x7,%d1 # extract Du2
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4),%d5 # fetch Update2 Op
|
|
|
|
andi.w &0x7,%d0 # extract Dc2
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4),%d3 # fetch Compare2 Op
|
|
mov.w %d0,DC2(%a6)
|
|
|
|
mov.w EXC_EXTWORD(%a6),%d0
|
|
mov.l %d0,%d1
|
|
|
|
rol.w &0x4,%d0
|
|
andi.w &0xf,%d0 # extract Rn1
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4),%a0 # fetch ADDR1
|
|
mov.l %a0,ADDR1(%a6)
|
|
|
|
mov.l %d1,%d0
|
|
|
|
lsr.w &0x6,%d1
|
|
andi.w &0x7,%d1 # extract Du1
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4),%d4 # fetch Update1 Op
|
|
|
|
andi.w &0x7,%d0 # extract Dc1
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4),%d2 # fetch Compare1 Op
|
|
mov.w %d0,DC1(%a6)
|
|
|
|
btst &0x1,EXC_OPWORD(%a6) # word or long?
|
|
sne %d7
|
|
|
|
btst &0x5,EXC_ISR(%a6) # user or supervisor?
|
|
sne %d6
|
|
|
|
mov.l %a0,%a2
|
|
mov.l %a1,%a3
|
|
|
|
mov.l %d7,%d1 # pass size
|
|
mov.l %d6,%d0 # pass mode
|
|
bsr.l _real_lock_page # lock page
|
|
mov.l %a2,%a0
|
|
tst.l %d0 # error?
|
|
bne.l _cas_terminate2 # yes
|
|
|
|
mov.l %d7,%d1 # pass size
|
|
mov.l %d6,%d0 # pass mode
|
|
mov.l %a3,%a0 # pass addr
|
|
bsr.l _real_lock_page # lock page
|
|
mov.l %a3,%a0
|
|
tst.l %d0 # error?
|
|
bne.b cas_preterm # yes
|
|
|
|
mov.l %a2,%a0
|
|
mov.l %a3,%a1
|
|
|
|
bra.l _real_cas2
|
|
|
|
# if the 2nd lock attempt fails, then we must still unlock the
|
|
# first page(s).
|
|
cas_preterm:
|
|
mov.l %d0,-(%sp) # save FSLW
|
|
mov.l %d7,%d1 # pass size
|
|
mov.l %d6,%d0 # pass mode
|
|
mov.l %a2,%a0 # pass ADDR1
|
|
bsr.l _real_unlock_page # unlock first page(s)
|
|
mov.l (%sp)+,%d0 # restore FSLW
|
|
mov.l %a3,%a0 # pass failing addr
|
|
bra.l _cas_terminate2
|
|
|
|
#############################################################
|
|
|
|
global _isp_cas2_finish
|
|
_isp_cas2_finish:
|
|
btst &0x1,EXC_OPWORD(%a6)
|
|
bne.b cas2_finish_l
|
|
|
|
mov.w EXC_CC(%a6),%cc # load old ccodes
|
|
cmp.w %d0,%d2
|
|
bne.b cas2_finish_w_save
|
|
cmp.w %d1,%d3
|
|
cas2_finish_w_save:
|
|
mov.w %cc,EXC_CC(%a6) # save new ccodes
|
|
|
|
tst.b %d4 # update compare reg?
|
|
bne.b cas2_finish_w_done # no
|
|
|
|
mov.w DC2(%a6),%d3 # fetch Dc2
|
|
mov.w %d1,(2+EXC_DREGS,%a6,%d3.w*4) # store new Compare2 Op
|
|
|
|
mov.w DC1(%a6),%d2 # fetch Dc1
|
|
mov.w %d0,(2+EXC_DREGS,%a6,%d2.w*4) # store new Compare1 Op
|
|
|
|
cas2_finish_w_done:
|
|
btst &0x5,EXC_ISR(%a6)
|
|
sne %d2
|
|
mov.l %d2,%d0 # pass mode
|
|
sf %d1 # pass size
|
|
mov.l ADDR1(%a6),%a0 # pass ADDR1
|
|
bsr.l _real_unlock_page # unlock page
|
|
|
|
mov.l %d2,%d0 # pass mode
|
|
sf %d1 # pass size
|
|
mov.l ADDR2(%a6),%a0 # pass ADDR2
|
|
bsr.l _real_unlock_page # unlock page
|
|
rts
|
|
|
|
cas2_finish_l:
|
|
mov.w EXC_CC(%a6),%cc # load old ccodes
|
|
cmp.l %d0,%d2
|
|
bne.b cas2_finish_l_save
|
|
cmp.l %d1,%d3
|
|
cas2_finish_l_save:
|
|
mov.w %cc,EXC_CC(%a6) # save new ccodes
|
|
|
|
tst.b %d4 # update compare reg?
|
|
bne.b cas2_finish_l_done # no
|
|
|
|
mov.w DC2(%a6),%d3 # fetch Dc2
|
|
mov.l %d1,(EXC_DREGS,%a6,%d3.w*4) # store new Compare2 Op
|
|
|
|
mov.w DC1(%a6),%d2 # fetch Dc1
|
|
mov.l %d0,(EXC_DREGS,%a6,%d2.w*4) # store new Compare1 Op
|
|
|
|
cas2_finish_l_done:
|
|
btst &0x5,EXC_ISR(%a6)
|
|
sne %d2
|
|
mov.l %d2,%d0 # pass mode
|
|
st %d1 # pass size
|
|
mov.l ADDR1(%a6),%a0 # pass ADDR1
|
|
bsr.l _real_unlock_page # unlock page
|
|
|
|
mov.l %d2,%d0 # pass mode
|
|
st %d1 # pass size
|
|
mov.l ADDR2(%a6),%a0 # pass ADDR2
|
|
bsr.l _real_unlock_page # unlock page
|
|
rts
|
|
|
|
########
|
|
global cr_cas2
|
|
cr_cas2:
|
|
mov.l EXC_TEMP+0x4(%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. #
|
|
# #
|
|
#########################################################################
|
|
|
|
set DC, EXC_TEMP+0x8
|
|
set ADDR, EXC_TEMP+0x4
|
|
|
|
global _compandset
|
|
_compandset:
|
|
btst &0x1,EXC_OPWORD(%a6) # word or long operation?
|
|
bne.b compandsetl # long
|
|
|
|
compandsetw:
|
|
movq.l &0x2,%d0 # size = 2 bytes
|
|
bsr.l _calc_ea # a0 = calculated <ea>
|
|
mov.l %a0,ADDR(%a6) # save <ea> for possible restart
|
|
sf %d7 # clear d7 for word size
|
|
bra.b compandsetfetch
|
|
|
|
compandsetl:
|
|
movq.l &0x4,%d0 # size = 4 bytes
|
|
bsr.l _calc_ea # a0 = calculated <ea>
|
|
mov.l %a0,ADDR(%a6) # save <ea> for possible restart
|
|
st %d7 # set d7 for longword size
|
|
|
|
compandsetfetch:
|
|
mov.w EXC_EXTWORD(%a6),%d0 # fetch cas extension word
|
|
mov.l %d0,%d1 # make a copy
|
|
|
|
lsr.w &0x6,%d0
|
|
andi.w &0x7,%d0 # extract Du
|
|
mov.l (EXC_DREGS,%a6,%d0.w*4),%d2 # get update operand
|
|
|
|
andi.w &0x7,%d1 # extract Dc
|
|
mov.l (EXC_DREGS,%a6,%d1.w*4),%d4 # get compare operand
|
|
mov.w %d1,DC(%a6) # save Dc
|
|
|
|
btst &0x5,EXC_ISR(%a6) # which mode for exception?
|
|
sne %d6 # set on supervisor mode
|
|
|
|
mov.l %a0,%a2 # save temporarily
|
|
mov.l %d7,%d1 # pass size
|
|
mov.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
|
|
mov.l %a2,%a0 # pass addr in a0
|
|
|
|
bra.l _real_cas
|
|
|
|
########
|
|
global _isp_cas_finish
|
|
_isp_cas_finish:
|
|
btst &0x1,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:
|
|
mov.w EXC_CC(%a6),%cc # restore cc
|
|
cmp.w %d0,%d4 # do word compare
|
|
mov.w %cc,EXC_CC(%a6) # save cc
|
|
|
|
tst.b %d1 # update compare reg?
|
|
bne.b cas_finish_w_done # no
|
|
|
|
mov.w DC(%a6),%d3
|
|
mov.w %d0,(EXC_DREGS+2,%a6,%d3.w*4) # Dc = destination
|
|
|
|
cas_finish_w_done:
|
|
mov.l ADDR(%a6),%a0 # pass addr
|
|
sf %d1 # pass size
|
|
btst &0x5,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:
|
|
mov.w EXC_CC(%a6),%cc # restore cc
|
|
cmp.l %d0,%d4 # do longword compare
|
|
mov.w %cc,EXC_CC(%a6) # save cc
|
|
|
|
tst.b %d1 # update compare reg?
|
|
bne.b cas_finish_l_done # no
|
|
|
|
mov.w DC(%a6),%d3
|
|
mov.l %d0,(EXC_DREGS,%a6,%d3.w*4) # Dc = destination
|
|
|
|
cas_finish_l_done:
|
|
mov.l ADDR(%a6),%a0 # pass addr
|
|
st %d1 # pass size
|
|
btst &0x5,EXC_ISR(%a6)
|
|
sne %d0 # pass mode
|
|
bsr.l _real_unlock_page # unlock page
|
|
rts
|
|
|
|
########
|
|
|
|
global _isp_cas_restart
|
|
_isp_cas_restart:
|
|
mov.l %d6,%sfc # restore previous sfc
|
|
mov.l %d6,%dfc # restore previous dfc
|
|
|
|
cmpi.b EXC_OPWORD+1(%a6),&0xfc # cas or cas2?
|
|
beq.l cr_cas2 # cas2
|
|
cr_cas:
|
|
mov.l ADDR(%a6),%a0 # load <ea>
|
|
btst &0x1,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:
|
|
mov.l %d6,%sfc # restore previous sfc
|
|
mov.l %d6,%dfc # restore previous dfc
|
|
|
|
global _cas_terminate2
|
|
_cas_terminate2:
|
|
mov.l %a0,%a2 # copy failing addr to a2
|
|
|
|
mov.l %d0,-(%sp)
|
|
bsr.l isp_restore # restore An (if ()+ or -())
|
|
mov.l (%sp)+,%d0
|
|
|
|
addq.l &0x4,%sp # remove sub return addr
|
|
subq.l &0x8,%sp # make room for bigger stack
|
|
subq.l &0x8,%a6 # shift frame ptr down, too
|
|
mov.l &26,%d1 # want to move 51 longwords
|
|
lea 0x8(%sp),%a0 # get address of old stack
|
|
lea 0x0(%sp),%a1 # get address of new stack
|
|
cas_term_cont:
|
|
mov.l (%a0)+,(%a1)+ # move a longword
|
|
dbra.w %d1,cas_term_cont # keep going
|
|
|
|
mov.w &0x4008,EXC_IVOFF(%a6) # put new stk fmt, voff
|
|
mov.l %a2,EXC_IVOFF+0x2(%a6) # put faulting addr on stack
|
|
mov.l %d0,EXC_IVOFF+0x6(%a6) # put FSLW on stack
|
|
movm.l EXC_DREGS(%a6),&0x3fff # 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(%pc),%a1 # load end of CAS core code
|
|
cmp.l %a1,%a0 # is PC in range?
|
|
blt.b cin_no # no
|
|
lea _CASLO(%pc),%a1 # load begin of CAS core code
|
|
cmp.l %a0,%a1 # is PC in range?
|
|
blt.b cin_no # no
|
|
rts # yes; return d0 = 0
|
|
cin_no:
|
|
mov.l &-0x1,%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:
|
|
movq.l &0x1,%d0 # load user data fc
|
|
bra.b cas2_cont
|
|
cas2_supervisor:
|
|
movq.l &0x5,%d0 # load supervisor data fc
|
|
cas2_cont:
|
|
tst.b %d7 # word or longword?
|
|
beq.w cas2w # word
|
|
|
|
####
|
|
cas2l:
|
|
mov.l %a0,%a2 # copy ADDR1
|
|
mov.l %a1,%a3 # copy ADDR2
|
|
mov.l %a0,%a4 # copy ADDR1
|
|
mov.l %a1,%a5 # copy ADDR2
|
|
|
|
addq.l &0x3,%a4 # ADDR1+3
|
|
addq.l &0x3,%a5 # ADDR2+3
|
|
mov.l %a2,%d1 # ADDR1
|
|
|
|
# mask interrupts levels 0-6. save old mask value.
|
|
mov.w %sr,%d7 # save current SR
|
|
ori.w &0x0700,%sr # inhibit interrupts
|
|
|
|
# load the SFC and DFC with the appropriate mode.
|
|
movc %sfc,%d6 # save old SFC/DFC
|
|
movc %d0,%sfc # store new SFC
|
|
movc %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
|
|
|
|
mov.l %d1,%a2 # ADDR1
|
|
addq.l &0x3,%d1
|
|
mov.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.
|
|
mov.l &0x80000000,%a2 # assert LOCK* buscr value
|
|
mov.l &0xa0000000,%a3 # assert LOCKE* buscr value
|
|
mov.l &0x00000000,%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.
|
|
mov.l %a0,%d0 # is ADDR1 misaligned?
|
|
andi.b &0x3,%d0
|
|
beq.b CAS2L_ENTER # no
|
|
cmpi.b %d0,&0x2
|
|
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 0x10
|
|
CAS2L_START:
|
|
movc %a2,%buscr # assert LOCK*
|
|
movs.l (%a1),%d1 # fetch Dest2[31:0]
|
|
movs.l (%a0),%d0 # fetch Dest1[31:0]
|
|
bra.b CAS2L_CONT
|
|
CAS2L_ENTER:
|
|
bra.b ~+16
|
|
|
|
CAS2L_CONT:
|
|
cmp.l %d0,%d2 # Dest1 - Compare1
|
|
bne.b CAS2L_NOUPDATE
|
|
cmp.l %d1,%d3 # Dest2 - Compare2
|
|
bne.b CAS2L_NOUPDATE
|
|
movs.l %d5,(%a1) # Update2[31:0] -> DEST2
|
|
bra.b CAS2L_UPDATE
|
|
bra.b ~+16
|
|
|
|
CAS2L_UPDATE:
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.l %d4,(%a0) # Update1[31:0] -> DEST1
|
|
movc %a4,%buscr # unlock the bus
|
|
bra.b cas2l_update_done
|
|
bra.b ~+16
|
|
|
|
CAS2L_NOUPDATE:
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.l %d0,(%a0) # Dest1[31:0] -> DEST1
|
|
movc %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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.w %d7,%sr # restore old SR
|
|
|
|
st %d4 # indicate update was done
|
|
bra.l _isp_cas2_finish
|
|
####
|
|
|
|
align 0x10
|
|
CAS2L2_START:
|
|
movc %a2,%buscr # assert LOCK*
|
|
movs.l (%a1),%d1 # fetch Dest2[31:0]
|
|
movs.l (%a0),%d0 # fetch Dest1[31:0]
|
|
bra.b CAS2L2_CONT
|
|
CAS2L2_ENTER:
|
|
bra.b ~+16
|
|
|
|
CAS2L2_CONT:
|
|
cmp.l %d0,%d2 # Dest1 - Compare1
|
|
bne.b CAS2L2_NOUPDATE
|
|
cmp.l %d1,%d3 # Dest2 - Compare2
|
|
bne.b CAS2L2_NOUPDATE
|
|
movs.l %d5,(%a1) # Update2[31:0] -> Dest2
|
|
bra.b CAS2L2_UPDATE
|
|
bra.b ~+16
|
|
|
|
CAS2L2_UPDATE:
|
|
swap %d4 # get Update1[31:16]
|
|
movs.w %d4,(%a0)+ # Update1[31:16] -> DEST1
|
|
movc %a3,%buscr # assert LOCKE*
|
|
swap %d4 # get Update1[15:0]
|
|
bra.b CAS2L2_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2L2_UPDATE2:
|
|
movs.w %d4,(%a0) # Update1[15:0] -> DEST1+0x2
|
|
movc %a4,%buscr # unlock the bus
|
|
bra.w cas2l_update_done
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CAS2L2_NOUPDATE:
|
|
swap %d0 # get Dest1[31:16]
|
|
movs.w %d0,(%a0)+ # Dest1[31:16] -> DEST1
|
|
movc %a3,%buscr # assert LOCKE*
|
|
swap %d0 # get Dest1[15:0]
|
|
bra.b CAS2L2_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2L2_NOUPDATE2:
|
|
movs.w %d0,(%a0) # Dest1[15:0] -> DEST1+0x2
|
|
movc %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 0x10
|
|
CAS2L3_START:
|
|
movc %a2,%buscr # assert LOCK*
|
|
movs.l (%a1),%d1 # fetch Dest2[31:0]
|
|
movs.l (%a0),%d0 # fetch Dest1[31:0]
|
|
bra.b CAS2L3_CONT
|
|
CAS2L3_ENTER:
|
|
bra.b ~+16
|
|
|
|
CAS2L3_CONT:
|
|
cmp.l %d0,%d2 # Dest1 - Compare1
|
|
bne.b CAS2L3_NOUPDATE
|
|
cmp.l %d1,%d3 # Dest2 - Compare2
|
|
bne.b CAS2L3_NOUPDATE
|
|
movs.l %d5,(%a1) # Update2[31:0] -> DEST2
|
|
bra.b CAS2L3_UPDATE
|
|
bra.b ~+16
|
|
|
|
CAS2L3_UPDATE:
|
|
rol.l &0x8,%d4 # get Update1[31:24]
|
|
movs.b %d4,(%a0)+ # Update1[31:24] -> DEST1
|
|
swap %d4 # get Update1[23:8]
|
|
movs.w %d4,(%a0)+ # Update1[23:8] -> DEST1+0x1
|
|
bra.b CAS2L3_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2L3_UPDATE2:
|
|
rol.l &0x8,%d4 # get Update1[7:0]
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.b %d4,(%a0) # Update1[7:0] -> DEST1+0x3
|
|
bra.b CAS2L3_UPDATE3
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CAS2L3_UPDATE3:
|
|
movc %a4,%buscr # unlock the bus
|
|
bra.w cas2l_update_done
|
|
nop
|
|
nop
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CAS2L3_NOUPDATE:
|
|
rol.l &0x8,%d0 # get Dest1[31:24]
|
|
movs.b %d0,(%a0)+ # Dest1[31:24] -> DEST1
|
|
swap %d0 # get Dest1[23:8]
|
|
movs.w %d0,(%a0)+ # Dest1[23:8] -> DEST1+0x1
|
|
bra.b CAS2L3_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2L3_NOUPDATE2:
|
|
rol.l &0x8,%d0 # get Dest1[7:0]
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.b %d0,(%a0) # Update1[7:0] -> DEST1+0x3
|
|
bra.b CAS2L3_NOUPDATE3
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CAS2L3_NOUPDATE3:
|
|
movc %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:
|
|
mov.l %a0,%a2 # copy ADDR1
|
|
mov.l %a1,%a3 # copy ADDR2
|
|
mov.l %a0,%a4 # copy ADDR1
|
|
mov.l %a1,%a5 # copy ADDR2
|
|
|
|
addq.l &0x1,%a4 # ADDR1+1
|
|
addq.l &0x1,%a5 # ADDR2+1
|
|
mov.l %a2,%d1 # ADDR1
|
|
|
|
# mask interrupt levels 0-6. save old mask value.
|
|
mov.w %sr,%d7 # save current SR
|
|
ori.w &0x0700,%sr # inhibit interrupts
|
|
|
|
# load the SFC and DFC with the appropriate mode.
|
|
movc %sfc,%d6 # save old SFC/DFC
|
|
movc %d0,%sfc # store new SFC
|
|
movc %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
|
|
|
|
mov.l %d1,%a2 # ADDR1
|
|
addq.l &0x3,%d1
|
|
mov.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.
|
|
mov.l &0x80000000,%a2 # assert LOCK* buscr value
|
|
mov.l &0xa0000000,%a3 # assert LOCKE* buscr value
|
|
mov.l &0x00000000,%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.
|
|
mov.l %a0,%d0 # is ADDR1 misaligned?
|
|
btst &0x0,%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 0x10
|
|
CAS2W_START:
|
|
movc %a2,%buscr # assert LOCK*
|
|
movs.w (%a1),%d1 # fetch Dest2[15:0]
|
|
movs.w (%a0),%d0 # fetch Dest1[15:0]
|
|
bra.b CAS2W_CONT2
|
|
CAS2W_ENTER:
|
|
bra.b ~+16
|
|
|
|
CAS2W_CONT2:
|
|
cmp.w %d0,%d2 # Dest1 - Compare1
|
|
bne.b CAS2W_NOUPDATE
|
|
cmp.w %d1,%d3 # Dest2 - Compare2
|
|
bne.b CAS2W_NOUPDATE
|
|
movs.w %d5,(%a1) # Update2[15:0] -> DEST2
|
|
bra.b CAS2W_UPDATE
|
|
bra.b ~+16
|
|
|
|
CAS2W_UPDATE:
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.w %d4,(%a0) # Update1[15:0] -> DEST1
|
|
movc %a4,%buscr # unlock the bus
|
|
bra.b cas2w_update_done
|
|
bra.b ~+16
|
|
|
|
CAS2W_NOUPDATE:
|
|
movc %a3,%buscr # assert LOCKE*
|
|
movs.w %d0,(%a0) # Dest1[15:0] -> DEST1
|
|
movc %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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.w %d7,%sr # restore old SR
|
|
|
|
st %d4 # indicate update was done
|
|
bra.l _isp_cas2_finish
|
|
####
|
|
|
|
align 0x10
|
|
CAS2W2_START:
|
|
movc %a2,%buscr # assert LOCK*
|
|
movs.w (%a1),%d1 # fetch Dest2[15:0]
|
|
movs.w (%a0),%d0 # fetch Dest1[15:0]
|
|
bra.b CAS2W2_CONT2
|
|
CAS2W2_ENTER:
|
|
bra.b ~+16
|
|
|
|
CAS2W2_CONT2:
|
|
cmp.w %d0,%d2 # Dest1 - Compare1
|
|
bne.b CAS2W2_NOUPDATE
|
|
cmp.w %d1,%d3 # Dest2 - Compare2
|
|
bne.b CAS2W2_NOUPDATE
|
|
movs.w %d5,(%a1) # Update2[15:0] -> DEST2
|
|
bra.b CAS2W2_UPDATE
|
|
bra.b ~+16
|
|
|
|
CAS2W2_UPDATE:
|
|
ror.l &0x8,%d4 # get Update1[15:8]
|
|
movs.b %d4,(%a0)+ # Update1[15:8] -> DEST1
|
|
movc %a3,%buscr # assert LOCKE*
|
|
rol.l &0x8,%d4 # get Update1[7:0]
|
|
bra.b CAS2W2_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2W2_UPDATE2:
|
|
movs.b %d4,(%a0) # Update1[7:0] -> DEST1+0x1
|
|
movc %a4,%buscr # unlock the bus
|
|
bra.w cas2w_update_done
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CAS2W2_NOUPDATE:
|
|
ror.l &0x8,%d0 # get Dest1[15:8]
|
|
movs.b %d0,(%a0)+ # Dest1[15:8] -> DEST1
|
|
movc %a3,%buscr # assert LOCKE*
|
|
rol.l &0x8,%d0 # get Dest1[7:0]
|
|
bra.b CAS2W2_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CAS2W2_NOUPDATE2:
|
|
movs.b %d0,(%a0) # Dest1[7:0] -> DEST1+0x1
|
|
movc %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:
|
|
movq.l &0x1,%d0 # load user data fc
|
|
bra.b cas_cont
|
|
cas_super:
|
|
movq.l &0x5,%d0 # load supervisor data fc
|
|
|
|
cas_cont:
|
|
tst.b %d7 # word or longword?
|
|
bne.w casl # longword
|
|
|
|
####
|
|
casw:
|
|
mov.l %a0,%a1 # make copy for plpaw1
|
|
mov.l %a0,%a2 # make copy for plpaw2
|
|
addq.l &0x1,%a2 # plpaw2 points to end of word
|
|
|
|
mov.l %d2,%d3 # d3 = update[7:0]
|
|
lsr.w &0x8,%d2 # d2 = update[15:8]
|
|
|
|
# mask interrupt levels 0-6. save old mask value.
|
|
mov.w %sr,%d7 # save current SR
|
|
ori.w &0x0700,%sr # inhibit interrupts
|
|
|
|
# load the SFC and DFC with the appropriate mode.
|
|
movc %sfc,%d6 # save old SFC/DFC
|
|
movc %d0,%sfc # load new sfc
|
|
movc %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.
|
|
mov.l &0x80000000,%a1 # assert LOCK* buscr value
|
|
mov.l &0xa0000000,%a2 # assert LOCKE* buscr value
|
|
mov.l &0x00000000,%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 0x10
|
|
CASW_START:
|
|
movc %a1,%buscr # assert LOCK*
|
|
movs.w (%a0),%d0 # fetch Dest[15:0]
|
|
cmp.w %d0,%d4 # Dest - Compare
|
|
bne.b CASW_NOUPDATE
|
|
bra.b CASW_UPDATE
|
|
CASW_ENTER:
|
|
bra.b ~+16
|
|
|
|
CASW_UPDATE:
|
|
movs.b %d2,(%a0)+ # Update[15:8] -> DEST
|
|
movc %a2,%buscr # assert LOCKE*
|
|
movs.b %d3,(%a0) # Update[7:0] -> DEST+0x1
|
|
bra.b CASW_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CASW_UPDATE2:
|
|
movc %a3,%buscr # unlock the bus
|
|
bra.b casw_update_done
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CASW_NOUPDATE:
|
|
ror.l &0x8,%d0 # get Dest[15:8]
|
|
movs.b %d0,(%a0)+ # Dest[15:8] -> DEST
|
|
movc %a2,%buscr # assert LOCKE*
|
|
rol.l &0x8,%d0 # get Dest[7:0]
|
|
bra.b CASW_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CASW_NOUPDATE2:
|
|
movs.b %d0,(%a0) # Dest[7:0] -> DEST+0x1
|
|
movc %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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.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:
|
|
mov.l %a0,%a1 # make copy for plpaw1
|
|
mov.l %a0,%a2 # make copy for plpaw2
|
|
addq.l &0x3,%a2 # plpaw2 points to end of longword
|
|
|
|
mov.l %a0,%d1 # byte or word misaligned?
|
|
btst &0x0,%d1
|
|
bne.w casl2 # byte misaligned
|
|
|
|
mov.l %d2,%d3 # d3 = update[15:0]
|
|
swap %d2 # d2 = update[31:16]
|
|
|
|
# mask interrupts levels 0-6. save old mask value.
|
|
mov.w %sr,%d7 # save current SR
|
|
ori.w &0x0700,%sr # inhibit interrupts
|
|
|
|
# load the SFC and DFC with the appropriate mode.
|
|
movc %sfc,%d6 # save old SFC/DFC
|
|
movc %d0,%sfc # load new sfc
|
|
movc %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.
|
|
mov.l &0x80000000,%a1 # assert LOCK* buscr value
|
|
mov.l &0xa0000000,%a2 # assert LOCKE* buscr value
|
|
mov.l &0x00000000,%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 0x10
|
|
CASL_START:
|
|
movc %a1,%buscr # assert LOCK*
|
|
movs.l (%a0),%d0 # fetch Dest[31:0]
|
|
cmp.l %d0,%d4 # Dest - Compare
|
|
bne.b CASL_NOUPDATE
|
|
bra.b CASL_UPDATE
|
|
CASL_ENTER:
|
|
bra.b ~+16
|
|
|
|
CASL_UPDATE:
|
|
movs.w %d2,(%a0)+ # Update[31:16] -> DEST
|
|
movc %a2,%buscr # assert LOCKE*
|
|
movs.w %d3,(%a0) # Update[15:0] -> DEST+0x2
|
|
bra.b CASL_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CASL_UPDATE2:
|
|
movc %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]
|
|
movs.w %d0,(%a0)+ # Dest[31:16] -> DEST
|
|
swap %d0 # get Dest[15:0]
|
|
movc %a2,%buscr # assert LOCKE*
|
|
bra.b CASL_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CASL_NOUPDATE2:
|
|
movs.w %d0,(%a0) # Dest[15:0] -> DEST+0x2
|
|
movc %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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupt mask level.
|
|
mov.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.
|
|
movc %d6,%sfc # restore old SFC
|
|
movc %d6,%dfc # restore old DFC
|
|
|
|
# restore previous interrupts mask level.
|
|
mov.w %d7,%sr # restore old SR
|
|
|
|
st %d1 # indicate update was done
|
|
bra.l _isp_cas_finish
|
|
|
|
#######################################
|
|
casl2:
|
|
mov.l %d2,%d5 # d5 = Update[7:0]
|
|
lsr.l &0x8,%d2
|
|
mov.l %d2,%d3 # d3 = Update[23:8]
|
|
swap %d2 # d2 = Update[31:24]
|
|
|
|
# mask interrupts levels 0-6. save old mask value.
|
|
mov.w %sr,%d7 # save current SR
|
|
ori.w &0x0700,%sr # inhibit interrupts
|
|
|
|
# load the SFC and DFC with the appropriate mode.
|
|
movc %sfc,%d6 # save old SFC/DFC
|
|
movc %d0,%sfc # load new sfc
|
|
movc %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.
|
|
mov.l &0x80000000,%a1 # assert LOCK* buscr value
|
|
mov.l &0xa0000000,%a2 # assert LOCKE* buscr value
|
|
mov.l &0x00000000,%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 0x10
|
|
CASL2_START:
|
|
movc %a1,%buscr # assert LOCK*
|
|
movs.l (%a0),%d0 # fetch Dest[31:0]
|
|
cmp.l %d0,%d4 # Dest - Compare
|
|
bne.b CASL2_NOUPDATE
|
|
bra.b CASL2_UPDATE
|
|
CASL2_ENTER:
|
|
bra.b ~+16
|
|
|
|
CASL2_UPDATE:
|
|
movs.b %d2,(%a0)+ # Update[31:24] -> DEST
|
|
movs.w %d3,(%a0)+ # Update[23:8] -> DEST+0x1
|
|
movc %a2,%buscr # assert LOCKE*
|
|
bra.b CASL2_UPDATE2
|
|
bra.b ~+16
|
|
|
|
CASL2_UPDATE2:
|
|
movs.b %d5,(%a0) # Update[7:0] -> DEST+0x3
|
|
movc %a3,%buscr # unlock the bus
|
|
bra.w casl_update_done
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CASL2_NOUPDATE:
|
|
rol.l &0x8,%d0 # get Dest[31:24]
|
|
movs.b %d0,(%a0)+ # Dest[31:24] -> DEST
|
|
swap %d0 # get Dest[23:8]
|
|
movs.w %d0,(%a0)+ # Dest[23:8] -> DEST+0x1
|
|
bra.b CASL2_NOUPDATE2
|
|
bra.b ~+16
|
|
|
|
CASL2_NOUPDATE2:
|
|
rol.l &0x8,%d0 # get Dest[7:0]
|
|
movc %a2,%buscr # assert LOCKE*
|
|
movs.b %d0,(%a0) # Dest[7:0] -> DEST+0x3
|
|
bra.b CASL2_NOUPDATE3
|
|
nop
|
|
bra.b ~+16
|
|
|
|
CASL2_NOUPDATE3:
|
|
movc %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:
|