# # $NetBSD: isp.s,v 1.3 2015/07/11 10:32:46 kamil 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 # # 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 || 0*** 1100 0000 0*** # # mulu.l 0100 1100 00 || 0*** 0100 0000 0*** # # # # divs.l 0100 1100 01 || 0*** 1100 0000 0*** # # divu.l 0100 1100 01 || 0*** 0100 0000 0*** # # # # movep.w m2r 0000 ***1 00 001*** | | # # movep.l m2r 0000 ***1 01 001*** | | # # movep.w r2m 0000 ***1 10 001*** | | # # movep.l r2m 0000 ***1 11 001*** | | # # # # cas.w 0000 1100 11 || 0000 000* **00 0*** # # cas.l 0000 1110 11 || 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 || **** 1000 0000 0000 # # chk2.w 0000 0010 11 || **** 1000 0000 0000 # # chk2.l 0000 0100 11 || **** 1000 0000 0000 # # # # cmp2.b 0000 0000 11 || **** 0000 0000 0000 # # cmp2.w 0000 0010 11 || **** 0000 0000 0000 # # cmp2.l 0000 0100 11 || **** 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 # # # - 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: # # ######################################################################### # word, long: 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 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 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 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 # += index add.l %d4,%d0 # += 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 register, 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 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 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 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 ,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 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 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 ,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 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 compatibility, 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 and go fetch the 32-bit operand. mul64_memop: movq.l &LONG, %d0 # pass # of bytes bsr.l _calc_ea # calculate 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 # # (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 mov.l %a0,ADDR(%a6) # save 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 mov.l %a0,ADDR(%a6) # save 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 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: