| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP | |
| M68000 Hi-Performance Microprocessor Division | |
| M68060 Software Package | |
| Production Release P1.00 -- October 10, 1994 | |
| M68060 Software Package Copyright © 1993, 1994 Motorola Inc. All rights reserved. | |
| THE SOFTWARE is provided on an "AS IS" basis and without warranty. | |
| To the maximum extent permitted by applicable law, | |
| MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, | |
| INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | |
| and any warranty against infringement with regard to the SOFTWARE | |
| (INCLUDING ANY MODIFIED VERSIONS THEREOF) and any accompanying written materials. | |
| To the maximum extent permitted by applicable law, | |
| IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER | |
| (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, | |
| BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) | |
| ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE. | |
| Motorola assumes no responsibility for the maintenance and support of the SOFTWARE. | |
| You are hereby granted a copyright license to use, modify, and distribute the SOFTWARE | |
| so long as this entire notice is retained without alteration in any modified and/or | |
| redistributed versions, and that such modified versions are clearly identified as such. | |
| No licenses are granted by implication, estoppel or otherwise under any patents | |
| or trademarks of Motorola, Inc. | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # ireal.s: | |
| # This file is appended to the top of the 060ISP package | |
| # and contains the entry points into the package. The user, in | |
| # effect, branches to one of the branch table entries located | |
| # after _060ISP_TABLE. | |
| # Also, subroutine stubs exist in this file (_isp_done for | |
| # example) that are referenced by the ISP package itself in order | |
| # to call a given routine. The stub routine actually performs the | |
| # callout. The ISP code does a "bsr" to the stub routine. This | |
| # extra layer of hierarchy adds a slight performance penalty but | |
| # it makes the ISP code easier to read and more mainatinable. | |
| # | |
| set _off_chk, 0x00 | |
| set _off_divbyzero, 0x04 | |
| set _off_trace, 0x08 | |
| set _off_access, 0x0c | |
| set _off_done, 0x10 | |
| set _off_cas, 0x14 | |
| set _off_cas2, 0x18 | |
| set _off_lock, 0x1c | |
| set _off_unlock, 0x20 | |
| set _off_imr, 0x40 | |
| set _off_dmr, 0x44 | |
| set _off_dmw, 0x48 | |
| set _off_irw, 0x4c | |
| set _off_irl, 0x50 | |
| set _off_drb, 0x54 | |
| set _off_drw, 0x58 | |
| set _off_drl, 0x5c | |
| set _off_dwb, 0x60 | |
| set _off_dww, 0x64 | |
| set _off_dwl, 0x68 | |
| _060ISP_TABLE: | |
| # Here's the table of ENTRY POINTS for those linking the package. | |
| bra.l _isp_unimp | |
| short 0x0000 | |
| bra.l _isp_cas | |
| short 0x0000 | |
| bra.l _isp_cas2 | |
| short 0x0000 | |
| bra.l _isp_cas_finish | |
| short 0x0000 | |
| bra.l _isp_cas2_finish | |
| short 0x0000 | |
| bra.l _isp_cas_inrange | |
| short 0x0000 | |
| bra.l _isp_cas_terminate | |
| short 0x0000 | |
| bra.l _isp_cas_restart | |
| short 0x0000 | |
| space 64 | |
| ############################################################# | |
| global _real_chk | |
| _real_chk: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_chk,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_divbyzero | |
| _real_divbyzero: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_divbyzero,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_trace | |
| _real_trace: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_trace,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_access | |
| _real_access: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_access,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _isp_done | |
| _isp_done: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_done,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| ####################################### | |
| global _real_cas | |
| _real_cas: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_cas,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_cas2 | |
| _real_cas2: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_cas2,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_lock_page | |
| _real_lock_page: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_lock,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _real_unlock_page | |
| _real_unlock_page: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_unlock,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| ####################################### | |
| global _imem_read | |
| _imem_read: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_imr,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_read | |
| _dmem_read: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_dmr,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_write | |
| _dmem_write: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_dmw,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _imem_read_word | |
| _imem_read_word: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_irw,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _imem_read_long | |
| _imem_read_long: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_irl,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_read_byte | |
| _dmem_read_byte: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_drb,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_read_word | |
| _dmem_read_word: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_drw,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_read_long | |
| _dmem_read_long: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_drl,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_write_byte | |
| _dmem_write_byte: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_dwb,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_write_word | |
| _dmem_write_word: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_dww,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| global _dmem_write_long | |
| _dmem_write_long: | |
| mov.l %d0,-(%sp) | |
| mov.l (_060ISP_TABLE-0x80+_off_dwl,%pc),%d0 | |
| pea.l (_060ISP_TABLE-0x80,%pc,%d0) | |
| mov.l 0x4(%sp),%d0 | |
| rtd &0x4 | |
| # | |
| # This file contains a set of define statements for constants | |
| # in oreder to promote readability within the core code itself. | |
| # | |
| set LOCAL_SIZE, 96 # stack frame size(bytes) | |
| set LV, -LOCAL_SIZE # stack offset | |
| set EXC_ISR, 0x4 # stack status register | |
| set EXC_IPC, 0x6 # stack pc | |
| set EXC_IVOFF, 0xa # stacked vector offset | |
| set EXC_AREGS, LV+64 # offset of all address regs | |
| set EXC_DREGS, LV+32 # offset of all data regs | |
| set EXC_A7, EXC_AREGS+(7*4) # offset of a7 | |
| set EXC_A6, EXC_AREGS+(6*4) # offset of a6 | |
| set EXC_A5, EXC_AREGS+(5*4) # offset of a5 | |
| set EXC_A4, EXC_AREGS+(4*4) # offset of a4 | |
| set EXC_A3, EXC_AREGS+(3*4) # offset of a3 | |
| set EXC_A2, EXC_AREGS+(2*4) # offset of a2 | |
| set EXC_A1, EXC_AREGS+(1*4) # offset of a1 | |
| set EXC_A0, EXC_AREGS+(0*4) # offset of a0 | |
| set EXC_D7, EXC_DREGS+(7*4) # offset of d7 | |
| set EXC_D6, EXC_DREGS+(6*4) # offset of d6 | |
| set EXC_D5, EXC_DREGS+(5*4) # offset of d5 | |
| set EXC_D4, EXC_DREGS+(4*4) # offset of d4 | |
| set EXC_D3, EXC_DREGS+(3*4) # offset of d3 | |
| set EXC_D2, EXC_DREGS+(2*4) # offset of d2 | |
| set EXC_D1, EXC_DREGS+(1*4) # offset of d1 | |
| set EXC_D0, EXC_DREGS+(0*4) # offset of d0 | |
| set EXC_TEMP, LV+16 # offset of temp stack space | |
| set EXC_SAVVAL, LV+12 # offset of old areg value | |
| set EXC_SAVREG, LV+11 # offset of old areg index | |
| set SPCOND_FLG, LV+10 # offset of spc condition flg | |
| set EXC_CC, LV+8 # offset of cc register | |
| set EXC_EXTWPTR, LV+4 # offset of current PC | |
| set EXC_EXTWORD, LV+2 # offset of current ext opword | |
| set EXC_OPWORD, LV+0 # offset of current opword | |
| ########################### | |
| # SPecial CONDition FLaGs # | |
| ########################### | |
| set mia7_flg, 0x04 # (a7)+ flag | |
| set mda7_flg, 0x08 # -(a7) flag | |
| set ichk_flg, 0x10 # chk exception flag | |
| set idbyz_flg, 0x20 # divbyzero flag | |
| set restore_flg, 0x40 # restore -(an)+ flag | |
| set immed_flg, 0x80 # immediate data flag | |
| set mia7_bit, 0x2 # (a7)+ bit | |
| set mda7_bit, 0x3 # -(a7) bit | |
| set ichk_bit, 0x4 # chk exception bit | |
| set idbyz_bit, 0x5 # divbyzero bit | |
| set restore_bit, 0x6 # restore -(a7)+ bit | |
| set immed_bit, 0x7 # immediate data bit | |
| ######### | |
| # Misc. # | |
| ######### | |
| set BYTE, 1 # len(byte) == 1 byte | |
| set WORD, 2 # len(word) == 2 bytes | |
| set LONG, 4 # len(longword) == 4 bytes | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _isp_unimp(): 060ISP entry point for Unimplemented Instruction # | |
| # # | |
| # This handler should be the first code executed upon taking the # | |
| # "Unimplemented Integer Instruction" exception in an operating # | |
| # system. # | |
| # # | |
| # XREF **************************************************************** # | |
| # _imem_read_{word,long}() - read instruction word/longword # | |
| # _mul64() - emulate 64-bit multiply # | |
| # _div64() - emulate 64-bit divide # | |
| # _moveperipheral() - emulate "movep" # | |
| # _compandset() - emulate misaligned "cas" # | |
| # _compandset2() - emulate "cas2" # | |
| # _chk2_cmp2() - emulate "cmp2" and "chk2" # | |
| # _isp_done() - "callout" for normal final exit # | |
| # _real_trace() - "callout" for Trace exception # | |
| # _real_chk() - "callout" for Chk exception # | |
| # _real_divbyzero() - "callout" for DZ exception # | |
| # _real_access() - "callout" for access error exception # | |
| # # | |
| # INPUT *************************************************************** # | |
| # - The system stack contains the Unimp Int Instr stack frame # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If Trace exception: # | |
| # - The system stack changed to contain Trace exc stack frame # | |
| # If Chk exception: # | |
| # - The system stack changed to contain Chk exc stack frame # | |
| # If DZ exception: # | |
| # - The system stack changed to contain DZ exc stack frame # | |
| # If access error exception: # | |
| # - The system stack changed to contain access err exc stk frame # | |
| # Else: # | |
| # - Results saved as appropriate # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # This handler fetches the first instruction longword from # | |
| # memory and decodes it to determine which of the unimplemented # | |
| # integer instructions caused this exception. This handler then calls # | |
| # one of _mul64(), _div64(), _moveperipheral(), _compandset(), # | |
| # _compandset2(), or _chk2_cmp2() as appropriate. # | |
| # Some of these instructions, by their nature, may produce other # | |
| # types of exceptions. "div" can produce a divide-by-zero exception, # | |
| # and "chk2" can cause a "Chk" exception. In both cases, the current # | |
| # exception stack frame must be converted to an exception stack frame # | |
| # of the correct exception type and an exit must be made through # | |
| # _real_divbyzero() or _real_chk() as appropriate. In addition, all # | |
| # instructions may be executing while Trace is enabled. If so, then # | |
| # a Trace exception stack frame must be created and an exit made # | |
| # through _real_trace(). # | |
| # Meanwhile, if any read or write to memory using the # | |
| # _mem_{read,write}() "callout"s returns a failing value, then an # | |
| # access error frame must be created and an exit made through # | |
| # _real_access(). # | |
| # If none of these occur, then a normal exit is made through # | |
| # _isp_done(). # | |
| # # | |
| # This handler, upon entry, saves almost all user-visible # | |
| # address and data registers to the stack. Although this may seem to # | |
| # cause excess memory traffic, it was found that due to having to # | |
| # access these register files for things like data retrieval and <ea> # | |
| # calculations, it was more efficient to have them on the stack where # | |
| # they could be accessed by indexing rather than to make subroutine # | |
| # calls to retrieve a register of a particular index. # | |
| # # | |
| ######################################################################### | |
| global _isp_unimp | |
| _isp_unimp: | |
| link.w %a6,&-LOCAL_SIZE # create room for stack frame | |
| movm.l &0x3fff,EXC_DREGS(%a6) # store d0-d7/a0-a5 | |
| mov.l (%a6),EXC_A6(%a6) # store a6 | |
| btst &0x5,EXC_ISR(%a6) # from s or u mode? | |
| bne.b uieh_s # supervisor mode | |
| uieh_u: | |
| mov.l %usp,%a0 # fetch user stack pointer | |
| mov.l %a0,EXC_A7(%a6) # store a7 | |
| bra.b uieh_cont | |
| uieh_s: | |
| lea 0xc(%a6),%a0 | |
| mov.l %a0,EXC_A7(%a6) # store corrected sp | |
| ############################################################################### | |
| uieh_cont: | |
| clr.b SPCOND_FLG(%a6) # clear "special case" flag | |
| mov.w EXC_ISR(%a6),EXC_CC(%a6) # store cc copy on stack | |
| mov.l EXC_IPC(%a6),EXC_EXTWPTR(%a6) # store extwptr on stack | |
| # | |
| # fetch the opword and first extension word pointed to by the stacked pc | |
| # and store them to the stack for now | |
| # | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_long # fetch opword & extword | |
| mov.l %d0,EXC_OPWORD(%a6) # store extword on stack | |
| ######################################################################### | |
| # muls.l 0100 1100 00 |<ea>| 0*** 1100 0000 0*** # | |
| # mulu.l 0100 1100 00 |<ea>| 0*** 0100 0000 0*** # | |
| # # | |
| # divs.l 0100 1100 01 |<ea>| 0*** 1100 0000 0*** # | |
| # divu.l 0100 1100 01 |<ea>| 0*** 0100 0000 0*** # | |
| # # | |
| # movep.w m2r 0000 ***1 00 001*** | <displacement> | # | |
| # movep.l m2r 0000 ***1 01 001*** | <displacement> | # | |
| # movep.w r2m 0000 ***1 10 001*** | <displacement> | # | |
| # movep.l r2m 0000 ***1 11 001*** | <displacement> | # | |
| # # | |
| # cas.w 0000 1100 11 |<ea>| 0000 000* **00 0*** # | |
| # cas.l 0000 1110 11 |<ea>| 0000 000* **00 0*** # | |
| # # | |
| # cas2.w 0000 1100 11 111100 **** 000* **00 0*** # | |
| # **** 000* **00 0*** # | |
| # cas2.l 0000 1110 11 111100 **** 000* **00 0*** # | |
| # **** 000* **00 0*** # | |
| # # | |
| # chk2.b 0000 0000 11 |<ea>| **** 1000 0000 0000 # | |
| # chk2.w 0000 0010 11 |<ea>| **** 1000 0000 0000 # | |
| # chk2.l 0000 0100 11 |<ea>| **** 1000 0000 0000 # | |
| # # | |
| # cmp2.b 0000 0000 11 |<ea>| **** 0000 0000 0000 # | |
| # cmp2.w 0000 0010 11 |<ea>| **** 0000 0000 0000 # | |
| # cmp2.l 0000 0100 11 |<ea>| **** 0000 0000 0000 # | |
| ######################################################################### | |
| # | |
| # using bit 14 of the operation word, separate into 2 groups: | |
| # (group1) mul64, div64 | |
| # (group2) movep, chk2, cmp2, cas2, cas | |
| # | |
| btst &0x1e,%d0 # group1 or group2 | |
| beq.b uieh_group2 # go handle group2 | |
| # | |
| # now, w/ group1, make mul64's decode the fastest since it will | |
| # most likely be used the most. | |
| # | |
| uieh_group1: | |
| btst &0x16,%d0 # test for div64 | |
| bne.b uieh_div64 # go handle div64 | |
| uieh_mul64: | |
| # mul64() may use ()+ addressing and may, therefore, alter a7 | |
| bsr.l _mul64 # _mul64() | |
| btst &0x5,EXC_ISR(%a6) # supervisor mode? | |
| beq.w uieh_done | |
| btst &mia7_bit,SPCOND_FLG(%a6) # was a7 changed? | |
| beq.w uieh_done # no | |
| btst &0x7,EXC_ISR(%a6) # is trace enabled? | |
| bne.w uieh_trace_a7 # yes | |
| bra.w uieh_a7 # no | |
| uieh_div64: | |
| # div64() may use ()+ addressing and may, therefore, alter a7. | |
| # div64() may take a divide by zero exception. | |
| bsr.l _div64 # _div64() | |
| # here, we sort out all of the special cases that may have happened. | |
| btst &mia7_bit,SPCOND_FLG(%a6) # was a7 changed? | |
| bne.b uieh_div64_a7 # yes | |
| uieh_div64_dbyz: | |
| btst &idbyz_bit,SPCOND_FLG(%a6) # did divide-by-zero occur? | |
| bne.w uieh_divbyzero # yes | |
| bra.w uieh_done # no | |
| uieh_div64_a7: | |
| btst &0x5,EXC_ISR(%a6) # supervisor mode? | |
| beq.b uieh_div64_dbyz # no | |
| # here, a7 has been incremented by 4 bytes in supervisor mode. we still | |
| # may have the following 3 cases: | |
| # (i) (a7)+ | |
| # (ii) (a7)+; trace | |
| # (iii) (a7)+; divide-by-zero | |
| # | |
| btst &idbyz_bit,SPCOND_FLG(%a6) # did divide-by-zero occur? | |
| bne.w uieh_divbyzero_a7 # yes | |
| tst.b EXC_ISR(%a6) # no; is trace enabled? | |
| bmi.w uieh_trace_a7 # yes | |
| bra.w uieh_a7 # no | |
| # | |
| # now, w/ group2, make movep's decode the fastest since it will | |
| # most likely be used the most. | |
| # | |
| uieh_group2: | |
| btst &0x18,%d0 # test for not movep | |
| beq.b uieh_not_movep | |
| bsr.l _moveperipheral # _movep() | |
| bra.w uieh_done | |
| uieh_not_movep: | |
| btst &0x1b,%d0 # test for chk2,cmp2 | |
| beq.b uieh_chk2cmp2 # go handle chk2,cmp2 | |
| swap %d0 # put opword in lo word | |
| cmpi.b %d0,&0xfc # test for cas2 | |
| beq.b uieh_cas2 # go handle cas2 | |
| uieh_cas: | |
| bsr.l _compandset # _cas() | |
| # the cases of "cas Dc,Du,(a7)+" and "cas Dc,Du,-(a7)" used from supervisor | |
| # mode are simply not considered valid and therefore are not handled. | |
| bra.w uieh_done | |
| uieh_cas2: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word # read extension word | |
| tst.l %d1 # ifetch error? | |
| bne.w isp_iacc # yes | |
| bsr.l _compandset2 # _cas2() | |
| bra.w uieh_done | |
| uieh_chk2cmp2: | |
| # chk2 may take a chk exception | |
| bsr.l _chk2_cmp2 # _chk2_cmp2() | |
| # here we check to see if a chk trap should be taken | |
| cmpi.b SPCOND_FLG(%a6),&ichk_flg | |
| bne.w uieh_done | |
| bra.b uieh_chk_trap | |
| ########################################################################### | |
| # | |
| # the required emulation has been completed. now, clean up the necessary stack | |
| # info and prepare for rte | |
| # | |
| uieh_done: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| # if exception occurred in user mode, then we have to restore a7 in case it | |
| # changed. we don't have to update a7 for supervisor mose because that case | |
| # doesn't flow through here | |
| btst &0x5,EXC_ISR(%a6) # user or supervisor? | |
| bne.b uieh_finish # supervisor | |
| mov.l EXC_A7(%a6),%a0 # fetch user stack pointer | |
| mov.l %a0,%usp # restore it | |
| uieh_finish: | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| btst &0x7,EXC_ISR(%a6) # is trace mode on? | |
| bne.b uieh_trace # yes;go handle trace mode | |
| mov.l EXC_EXTWPTR(%a6),EXC_IPC(%a6) # new pc on stack frame | |
| mov.l EXC_A6(%a6),(%a6) # prepare new a6 for unlink | |
| unlk %a6 # unlink stack frame | |
| bra.l _isp_done | |
| # | |
| # The instruction that was just emulated was also being traced. The trace | |
| # trap for this instruction will be lost unless we jump to the trace handler. | |
| # So, here we create a Trace Exception format number two exception stack | |
| # frame from the Unimplemented Integer Intruction Exception stack frame | |
| # format number zero and jump to the user supplied hook "_real_trace()". | |
| # | |
| # UIEH FRAME TRACE FRAME | |
| # ***************** ***************** | |
| # * 0x0 * 0x0f4 * * Current * | |
| # ***************** * PC * | |
| # * Current * ***************** | |
| # * PC * * 0x2 * 0x024 * | |
| # ***************** ***************** | |
| # * SR * * Next * | |
| # ***************** * PC * | |
| # ->* Old * ***************** | |
| # from link -->* A6 * * SR * | |
| # ***************** ***************** | |
| # /* A7 * * New * <-- for final unlink | |
| # / * * * A6 * | |
| # link frame < ***************** ***************** | |
| # \ ~ ~ ~ ~ | |
| # \***************** ***************** | |
| # | |
| uieh_trace: | |
| mov.l EXC_A6(%a6),-0x4(%a6) | |
| mov.w EXC_ISR(%a6),0x0(%a6) | |
| mov.l EXC_IPC(%a6),0x8(%a6) | |
| mov.l EXC_EXTWPTR(%a6),0x2(%a6) | |
| mov.w &0x2024,0x6(%a6) | |
| sub.l &0x4,%a6 | |
| unlk %a6 | |
| bra.l _real_trace | |
| # | |
| # UIEH FRAME CHK FRAME | |
| # ***************** ***************** | |
| # * 0x0 * 0x0f4 * * Current * | |
| # ***************** * PC * | |
| # * Current * ***************** | |
| # * PC * * 0x2 * 0x018 * | |
| # ***************** ***************** | |
| # * SR * * Next * | |
| # ***************** * PC * | |
| # (4 words) ***************** | |
| # * SR * | |
| # ***************** | |
| # (6 words) | |
| # | |
| # the chk2 instruction should take a chk trap. so, here we must create a | |
| # chk stack frame from an unimplemented integer instruction exception frame | |
| # and jump to the user supplied entry point "_real_chk()". | |
| # | |
| uieh_chk_trap: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| mov.w EXC_ISR(%a6),(%a6) # put new SR on stack | |
| mov.l EXC_IPC(%a6),0x8(%a6) # put "Current PC" on stack | |
| mov.l EXC_EXTWPTR(%a6),0x2(%a6) # put "Next PC" on stack | |
| mov.w &0x2018,0x6(%a6) # put Vector Offset on stack | |
| mov.l EXC_A6(%a6),%a6 # restore a6 | |
| add.l &LOCAL_SIZE,%sp # clear stack frame | |
| bra.l _real_chk | |
| # | |
| # UIEH FRAME DIVBYZERO FRAME | |
| # ***************** ***************** | |
| # * 0x0 * 0x0f4 * * Current * | |
| # ***************** * PC * | |
| # * Current * ***************** | |
| # * PC * * 0x2 * 0x014 * | |
| # ***************** ***************** | |
| # * SR * * Next * | |
| # ***************** * PC * | |
| # (4 words) ***************** | |
| # * SR * | |
| # ***************** | |
| # (6 words) | |
| # | |
| # the divide instruction should take an integer divide by zero trap. so, here | |
| # we must create a divbyzero stack frame from an unimplemented integer | |
| # instruction exception frame and jump to the user supplied entry point | |
| # "_real_divbyzero()". | |
| # | |
| uieh_divbyzero: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| mov.w EXC_ISR(%a6),(%a6) # put new SR on stack | |
| mov.l EXC_IPC(%a6),0x8(%a6) # put "Current PC" on stack | |
| mov.l EXC_EXTWPTR(%a6),0x2(%a6) # put "Next PC" on stack | |
| mov.w &0x2014,0x6(%a6) # put Vector Offset on stack | |
| mov.l EXC_A6(%a6),%a6 # restore a6 | |
| add.l &LOCAL_SIZE,%sp # clear stack frame | |
| bra.l _real_divbyzero | |
| # | |
| # DIVBYZERO FRAME | |
| # ***************** | |
| # * Current * | |
| # UIEH FRAME * PC * | |
| # ***************** ***************** | |
| # * 0x0 * 0x0f4 * * 0x2 * 0x014 * | |
| # ***************** ***************** | |
| # * Current * * Next * | |
| # * PC * * PC * | |
| # ***************** ***************** | |
| # * SR * * SR * | |
| # ***************** ***************** | |
| # (4 words) (6 words) | |
| # | |
| # the divide instruction should take an integer divide by zero trap. so, here | |
| # we must create a divbyzero stack frame from an unimplemented integer | |
| # instruction exception frame and jump to the user supplied entry point | |
| # "_real_divbyzero()". | |
| # | |
| # However, we must also deal with the fact that (a7)+ was used from supervisor | |
| # mode, thereby shifting the stack frame up 4 bytes. | |
| # | |
| uieh_divbyzero_a7: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| mov.l EXC_IPC(%a6),0xc(%a6) # put "Current PC" on stack | |
| mov.w &0x2014,0xa(%a6) # put Vector Offset on stack | |
| mov.l EXC_EXTWPTR(%a6),0x6(%a6) # put "Next PC" on stack | |
| mov.l EXC_A6(%a6),%a6 # restore a6 | |
| add.l &4+LOCAL_SIZE,%sp # clear stack frame | |
| bra.l _real_divbyzero | |
| # | |
| # TRACE FRAME | |
| # ***************** | |
| # * Current * | |
| # UIEH FRAME * PC * | |
| # ***************** ***************** | |
| # * 0x0 * 0x0f4 * * 0x2 * 0x024 * | |
| # ***************** ***************** | |
| # * Current * * Next * | |
| # * PC * * PC * | |
| # ***************** ***************** | |
| # * SR * * SR * | |
| # ***************** ***************** | |
| # (4 words) (6 words) | |
| # | |
| # | |
| # The instruction that was just emulated was also being traced. The trace | |
| # trap for this instruction will be lost unless we jump to the trace handler. | |
| # So, here we create a Trace Exception format number two exception stack | |
| # frame from the Unimplemented Integer Intruction Exception stack frame | |
| # format number zero and jump to the user supplied hook "_real_trace()". | |
| # | |
| # However, we must also deal with the fact that (a7)+ was used from supervisor | |
| # mode, thereby shifting the stack frame up 4 bytes. | |
| # | |
| uieh_trace_a7: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| mov.l EXC_IPC(%a6),0xc(%a6) # put "Current PC" on stack | |
| mov.w &0x2024,0xa(%a6) # put Vector Offset on stack | |
| mov.l EXC_EXTWPTR(%a6),0x6(%a6) # put "Next PC" on stack | |
| mov.l EXC_A6(%a6),%a6 # restore a6 | |
| add.l &4+LOCAL_SIZE,%sp # clear stack frame | |
| bra.l _real_trace | |
| # | |
| # UIEH FRAME | |
| # ***************** | |
| # * 0x0 * 0x0f4 * | |
| # UIEH FRAME ***************** | |
| # ***************** * Next * | |
| # * 0x0 * 0x0f4 * * PC * | |
| # ***************** ***************** | |
| # * Current * * SR * | |
| # * PC * ***************** | |
| # ***************** (4 words) | |
| # * SR * | |
| # ***************** | |
| # (4 words) | |
| uieh_a7: | |
| mov.b EXC_CC+1(%a6),EXC_ISR+1(%a6) # insert new ccodes | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| mov.w &0x00f4,0xe(%a6) # put Vector Offset on stack | |
| mov.l EXC_EXTWPTR(%a6),0xa(%a6) # put "Next PC" on stack | |
| mov.w EXC_ISR(%a6),0x8(%a6) # put SR on stack | |
| mov.l EXC_A6(%a6),%a6 # restore a6 | |
| add.l &8+LOCAL_SIZE,%sp # clear stack frame | |
| bra.l _isp_done | |
| ########## | |
| # this is the exit point if a data read or write fails. | |
| # a0 = failing address | |
| # d0 = fslw | |
| isp_dacc: | |
| mov.l %a0,(%a6) # save address | |
| mov.l %d0,-0x4(%a6) # save partial fslw | |
| lea -64(%a6),%sp | |
| movm.l (%sp)+,&0x7fff # restore d0-d7/a0-a6 | |
| mov.l 0xc(%sp),-(%sp) # move voff,hi(pc) | |
| mov.l 0x4(%sp),0x10(%sp) # store fslw | |
| mov.l 0xc(%sp),0x4(%sp) # store sr,lo(pc) | |
| mov.l 0x8(%sp),0xc(%sp) # store address | |
| mov.l (%sp)+,0x4(%sp) # store voff,hi(pc) | |
| mov.w &0x4008,0x6(%sp) # store new voff | |
| bra.b isp_acc_exit | |
| # this is the exit point if an instruction word read fails. | |
| # FSLW: | |
| # misaligned = true | |
| # read = true | |
| # size = word | |
| # instruction = true | |
| # software emulation error = true | |
| isp_iacc: | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore d0-d7/a0-a5 | |
| unlk %a6 # unlink frame | |
| sub.w &0x8,%sp # make room for acc frame | |
| mov.l 0x8(%sp),(%sp) # store sr,lo(pc) | |
| mov.w 0xc(%sp),0x4(%sp) # store hi(pc) | |
| mov.w &0x4008,0x6(%sp) # store new voff | |
| mov.l 0x2(%sp),0x8(%sp) # store address (=pc) | |
| mov.l &0x09428001,0xc(%sp) # store fslw | |
| isp_acc_exit: | |
| btst &0x5,(%sp) # user or supervisor? | |
| beq.b isp_acc_exit2 # user | |
| bset &0x2,0xd(%sp) # set supervisor TM bit | |
| isp_acc_exit2: | |
| bra.l _real_access | |
| # if the addressing mode was (an)+ or -(an), the address register must | |
| # be restored to its pre-exception value before entering _real_access. | |
| isp_restore: | |
| cmpi.b SPCOND_FLG(%a6),&restore_flg # do we need a restore? | |
| bne.b isp_restore_done # no | |
| clr.l %d0 | |
| mov.b EXC_SAVREG(%a6),%d0 # regno to restore | |
| mov.l EXC_SAVVAL(%a6),(EXC_AREGS,%a6,%d0.l*4) # restore value | |
| isp_restore_done: | |
| rts | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _calc_ea(): routine to calculate effective address # | |
| # # | |
| # XREF **************************************************************** # | |
| # _imem_read_word() - read instruction word # | |
| # _imem_read_long() - read instruction longword # | |
| # _dmem_read_long() - read data longword (for memory indirect) # | |
| # isp_iacc() - handle instruction access error exception # | |
| # isp_dacc() - handle data access error exception # | |
| # # | |
| # INPUT *************************************************************** # | |
| # d0 = number of bytes related to effective address (w,l) # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If exiting through isp_dacc... # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # elsif exiting though isp_iacc... # | |
| # none # | |
| # else # | |
| # a0 = effective address # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # The effective address type is decoded from the opword residing # | |
| # on the stack. A jump table is used to vector to a routine for the # | |
| # appropriate mode. Since none of the emulated integer instructions # | |
| # uses byte-sized operands, only handle word and long operations. # | |
| # # | |
| # Dn,An - shouldn't enter here # | |
| # (An) - fetch An value from stack # | |
| # -(An) - fetch An value from stack; return decr value; # | |
| # place decr value on stack; store old value in case of # | |
| # future access error; if -(a7), set mda7_flg in # | |
| # SPCOND_FLG # | |
| # (An)+ - fetch An value from stack; return value; # | |
| # place incr value on stack; store old value in case of # | |
| # future access error; if (a7)+, set mia7_flg in # | |
| # SPCOND_FLG # | |
| # (d16,An) - fetch An value from stack; read d16 using # | |
| # _imem_read_word(); fetch may fail -> branch to # | |
| # isp_iacc() # | |
| # (xxx).w,(xxx).l - use _imem_read_{word,long}() to fetch # | |
| # address; fetch may fail # | |
| # #<data> - return address of immediate value; set immed_flg # | |
| # in SPCOND_FLG # | |
| # (d16,PC) - fetch stacked PC value; read d16 using # | |
| # _imem_read_word(); fetch may fail -> branch to # | |
| # isp_iacc() # | |
| # everything else - read needed displacements as appropriate w/ # | |
| # _imem_read_{word,long}(); read may fail; if memory # | |
| # indirect, read indirect address using # | |
| # _dmem_read_long() which may also fail # | |
| # # | |
| ######################################################################### | |
| global _calc_ea | |
| _calc_ea: | |
| mov.l %d0,%a0 # move # bytes to a0 | |
| # MODE and REG are taken from the EXC_OPWORD. | |
| mov.w EXC_OPWORD(%a6),%d0 # fetch opcode word | |
| mov.w %d0,%d1 # make a copy | |
| andi.w &0x3f,%d0 # extract mode field | |
| andi.l &0x7,%d1 # extract reg field | |
| # jump to the corresponding function for each {MODE,REG} pair. | |
| mov.w (tbl_ea_mode.b,%pc,%d0.w*2), %d0 # fetch jmp distance | |
| jmp (tbl_ea_mode.b,%pc,%d0.w*1) # jmp to correct ea mode | |
| swbeg &64 | |
| tbl_ea_mode: | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short addr_ind_a0 - tbl_ea_mode | |
| short addr_ind_a1 - tbl_ea_mode | |
| short addr_ind_a2 - tbl_ea_mode | |
| short addr_ind_a3 - tbl_ea_mode | |
| short addr_ind_a4 - tbl_ea_mode | |
| short addr_ind_a5 - tbl_ea_mode | |
| short addr_ind_a6 - tbl_ea_mode | |
| short addr_ind_a7 - tbl_ea_mode | |
| short addr_ind_p_a0 - tbl_ea_mode | |
| short addr_ind_p_a1 - tbl_ea_mode | |
| short addr_ind_p_a2 - tbl_ea_mode | |
| short addr_ind_p_a3 - tbl_ea_mode | |
| short addr_ind_p_a4 - tbl_ea_mode | |
| short addr_ind_p_a5 - tbl_ea_mode | |
| short addr_ind_p_a6 - tbl_ea_mode | |
| short addr_ind_p_a7 - tbl_ea_mode | |
| short addr_ind_m_a0 - tbl_ea_mode | |
| short addr_ind_m_a1 - tbl_ea_mode | |
| short addr_ind_m_a2 - tbl_ea_mode | |
| short addr_ind_m_a3 - tbl_ea_mode | |
| short addr_ind_m_a4 - tbl_ea_mode | |
| short addr_ind_m_a5 - tbl_ea_mode | |
| short addr_ind_m_a6 - tbl_ea_mode | |
| short addr_ind_m_a7 - tbl_ea_mode | |
| short addr_ind_disp_a0 - tbl_ea_mode | |
| short addr_ind_disp_a1 - tbl_ea_mode | |
| short addr_ind_disp_a2 - tbl_ea_mode | |
| short addr_ind_disp_a3 - tbl_ea_mode | |
| short addr_ind_disp_a4 - tbl_ea_mode | |
| short addr_ind_disp_a5 - tbl_ea_mode | |
| short addr_ind_disp_a6 - tbl_ea_mode | |
| short addr_ind_disp_a7 - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short _addr_ind_ext - tbl_ea_mode | |
| short abs_short - tbl_ea_mode | |
| short abs_long - tbl_ea_mode | |
| short pc_ind - tbl_ea_mode | |
| short pc_ind_ext - tbl_ea_mode | |
| short immediate - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| short tbl_ea_mode - tbl_ea_mode | |
| ################################### | |
| # Address register indirect: (An) # | |
| ################################### | |
| addr_ind_a0: | |
| mov.l EXC_A0(%a6),%a0 # Get current a0 | |
| rts | |
| addr_ind_a1: | |
| mov.l EXC_A1(%a6),%a0 # Get current a1 | |
| rts | |
| addr_ind_a2: | |
| mov.l EXC_A2(%a6),%a0 # Get current a2 | |
| rts | |
| addr_ind_a3: | |
| mov.l EXC_A3(%a6),%a0 # Get current a3 | |
| rts | |
| addr_ind_a4: | |
| mov.l EXC_A4(%a6),%a0 # Get current a4 | |
| rts | |
| addr_ind_a5: | |
| mov.l EXC_A5(%a6),%a0 # Get current a5 | |
| rts | |
| addr_ind_a6: | |
| mov.l EXC_A6(%a6),%a0 # Get current a6 | |
| rts | |
| addr_ind_a7: | |
| mov.l EXC_A7(%a6),%a0 # Get current a7 | |
| rts | |
| ##################################################### | |
| # Address register indirect w/ postincrement: (An)+ # | |
| ##################################################### | |
| addr_ind_p_a0: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A0(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A0(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x0,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a1: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A1(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A1(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x1,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a2: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A2(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A2(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x2,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a3: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A3(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A3(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x3,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a4: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A4(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A4(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x4,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a5: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A5(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A5(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x5,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a6: | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A6(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A6(%a6) # save incremented value | |
| mov.l %a0,EXC_SAVVAL(%a6) # save in case of access error | |
| mov.b &0x6,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_p_a7: | |
| mov.b &mia7_flg,SPCOND_FLG(%a6) # set "special case" flag | |
| mov.l %a0,%d0 # copy no. bytes | |
| mov.l EXC_A7(%a6),%a0 # load current value | |
| add.l %a0,%d0 # increment | |
| mov.l %d0,EXC_A7(%a6) # save incremented value | |
| rts | |
| #################################################### | |
| # Address register indirect w/ predecrement: -(An) # | |
| #################################################### | |
| addr_ind_m_a0: | |
| mov.l EXC_A0(%a6),%d0 # Get current a0 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A0(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x0,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a1: | |
| mov.l EXC_A1(%a6),%d0 # Get current a1 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A1(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x1,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a2: | |
| mov.l EXC_A2(%a6),%d0 # Get current a2 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A2(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x2,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a3: | |
| mov.l EXC_A3(%a6),%d0 # Get current a3 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A3(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x3,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a4: | |
| mov.l EXC_A4(%a6),%d0 # Get current a4 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A4(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x4,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a5: | |
| mov.l EXC_A5(%a6),%d0 # Get current a5 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A5(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x5,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a6: | |
| mov.l EXC_A6(%a6),%d0 # Get current a6 | |
| mov.l %d0,EXC_SAVVAL(%a6) # save in case of access error | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A6(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| mov.b &0x6,EXC_SAVREG(%a6) # save regno, too | |
| mov.b &restore_flg,SPCOND_FLG(%a6) # set flag | |
| rts | |
| addr_ind_m_a7: | |
| mov.b &mda7_flg,SPCOND_FLG(%a6) # set "special case" flag | |
| mov.l EXC_A7(%a6),%d0 # Get current a7 | |
| sub.l %a0,%d0 # Decrement | |
| mov.l %d0,EXC_A7(%a6) # Save decr value | |
| mov.l %d0,%a0 | |
| rts | |
| ######################################################## | |
| # Address register indirect w/ displacement: (d16, An) # | |
| ######################################################## | |
| addr_ind_disp_a0: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A0(%a6),%a0 # a0 + d16 | |
| rts | |
| addr_ind_disp_a1: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A1(%a6),%a0 # a1 + d16 | |
| rts | |
| addr_ind_disp_a2: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A2(%a6),%a0 # a2 + d16 | |
| rts | |
| addr_ind_disp_a3: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A3(%a6),%a0 # a3 + d16 | |
| rts | |
| addr_ind_disp_a4: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A4(%a6),%a0 # a4 + d16 | |
| rts | |
| addr_ind_disp_a5: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A5(%a6),%a0 # a5 + d16 | |
| rts | |
| addr_ind_disp_a6: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A6(%a6),%a0 # a6 + d16 | |
| rts | |
| addr_ind_disp_a7: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_A7(%a6),%a0 # a7 + d16 | |
| rts | |
| ######################################################################## | |
| # Address register indirect w/ index(8-bit displacement): (dn, An, Xn) # | |
| # " " " w/ " (base displacement): (bd, An, Xn) # | |
| # Memory indirect postindexed: ([bd, An], Xn, od) # | |
| # Memory indirect preindexed: ([bd, An, Xn], od) # | |
| ######################################################################## | |
| _addr_ind_ext: | |
| mov.l %d1,-(%sp) | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word # fetch extword in d0 | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.l (%sp)+,%d1 | |
| mov.l (EXC_AREGS,%a6,%d1.w*4),%a0 # put base in a0 | |
| btst &0x8,%d0 | |
| beq.b addr_ind_index_8bit # for ext word or not? | |
| movm.l &0x3c00,-(%sp) # save d2-d5 | |
| mov.l %d0,%d5 # put extword in d5 | |
| mov.l %a0,%d3 # put base in d3 | |
| bra.l calc_mem_ind # calc memory indirect | |
| addr_ind_index_8bit: | |
| mov.l %d2,-(%sp) # save old d2 | |
| mov.l %d0,%d1 | |
| rol.w &0x4,%d1 | |
| andi.w &0xf,%d1 # extract index regno | |
| mov.l (EXC_DREGS,%a6,%d1.w*4),%d1 # fetch index reg value | |
| btst &0xb,%d0 # is it word or long? | |
| bne.b aii8_long | |
| ext.l %d1 # sign extend word index | |
| aii8_long: | |
| mov.l %d0,%d2 | |
| rol.w &0x7,%d2 | |
| andi.l &0x3,%d2 # extract scale value | |
| lsl.l %d2,%d1 # shift index by scale | |
| extb.l %d0 # sign extend displacement | |
| add.l %d1,%d0 # index + disp | |
| add.l %d0,%a0 # An + (index + disp) | |
| mov.l (%sp)+,%d2 # restore old d2 | |
| rts | |
| ###################### | |
| # Immediate: #<data> # | |
| ######################################################################### | |
| # word, long: <ea> of the data is the current extension word # | |
| # pointer value. new extension word pointer is simply the old # | |
| # plus the number of bytes in the data type(2 or 4). # | |
| ######################################################################### | |
| immediate: | |
| mov.b &immed_flg,SPCOND_FLG(%a6) # set immediate flag | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch extension word ptr | |
| rts | |
| ########################### | |
| # Absolute short: (XXX).W # | |
| ########################### | |
| abs_short: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word # fetch short address | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # return <ea> in a0 | |
| rts | |
| ########################## | |
| # Absolute long: (XXX).L # | |
| ########################## | |
| abs_long: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_long # fetch long address | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.l %d0,%a0 # return <ea> in a0 | |
| rts | |
| ####################################################### | |
| # Program counter indirect w/ displacement: (d16, PC) # | |
| ####################################################### | |
| pc_ind: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word # fetch word displacement | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.w %d0,%a0 # sign extend displacement | |
| add.l EXC_EXTWPTR(%a6),%a0 # pc + d16 | |
| # _imem_read_word() increased the extwptr by 2. need to adjust here. | |
| subq.l &0x2,%a0 # adjust <ea> | |
| rts | |
| ########################################################## | |
| # PC indirect w/ index(8-bit displacement): (d8, PC, An) # | |
| # " " w/ " (base displacement): (bd, PC, An) # | |
| # PC memory indirect postindexed: ([bd, PC], Xn, od) # | |
| # PC memory indirect preindexed: ([bd, PC, Xn], od) # | |
| ########################################################## | |
| pc_ind_ext: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word # fetch ext word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.l EXC_EXTWPTR(%a6),%a0 # put base in a0 | |
| subq.l &0x2,%a0 # adjust base | |
| btst &0x8,%d0 # is disp only 8 bits? | |
| beq.b pc_ind_index_8bit # yes | |
| # the indexed addressing mode uses a base displacement of size | |
| # word or long | |
| movm.l &0x3c00,-(%sp) # save d2-d5 | |
| mov.l %d0,%d5 # put extword in d5 | |
| mov.l %a0,%d3 # put base in d3 | |
| bra.l calc_mem_ind # calc memory indirect | |
| pc_ind_index_8bit: | |
| mov.l %d2,-(%sp) # create a temp register | |
| mov.l %d0,%d1 # make extword copy | |
| rol.w &0x4,%d1 # rotate reg num into place | |
| andi.w &0xf,%d1 # extract register number | |
| mov.l (EXC_DREGS,%a6,%d1.w*4),%d1 # fetch index reg value | |
| btst &0xb,%d0 # is index word or long? | |
| bne.b pii8_long # long | |
| ext.l %d1 # sign extend word index | |
| pii8_long: | |
| mov.l %d0,%d2 # make extword copy | |
| rol.w &0x7,%d2 # rotate scale value into place | |
| andi.l &0x3,%d2 # extract scale value | |
| lsl.l %d2,%d1 # shift index by scale | |
| extb.l %d0 # sign extend displacement | |
| add.l %d1,%d0 # index + disp | |
| add.l %d0,%a0 # An + (index + disp) | |
| mov.l (%sp)+,%d2 # restore temp register | |
| rts | |
| # a5 = exc_extwptr (global to uaeh) | |
| # a4 = exc_opword (global to uaeh) | |
| # a3 = exc_dregs (global to uaeh) | |
| # d2 = index (internal " " ) | |
| # d3 = base (internal " " ) | |
| # d4 = od (internal " " ) | |
| # d5 = extword (internal " " ) | |
| calc_mem_ind: | |
| btst &0x6,%d5 # is the index suppressed? | |
| beq.b calc_index | |
| clr.l %d2 # yes, so index = 0 | |
| bra.b base_supp_ck | |
| calc_index: | |
| bfextu %d5{&16:&4},%d2 | |
| mov.l (EXC_DREGS,%a6,%d2.w*4),%d2 | |
| btst &0xb,%d5 # is index word or long? | |
| bne.b no_ext | |
| ext.l %d2 | |
| no_ext: | |
| bfextu %d5{&21:&2},%d0 | |
| lsl.l %d0,%d2 | |
| base_supp_ck: | |
| btst &0x7,%d5 # is the bd suppressed? | |
| beq.b no_base_sup | |
| clr.l %d3 | |
| no_base_sup: | |
| bfextu %d5{&26:&2},%d0 # get bd size | |
| # beq.l _error # if (size == 0) it's reserved | |
| cmpi.b %d0,&2 | |
| blt.b no_bd | |
| beq.b get_word_bd | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_long | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| bra.b chk_ind | |
| get_word_bd: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| ext.l %d0 # sign extend bd | |
| chk_ind: | |
| add.l %d0,%d3 # base += bd | |
| no_bd: | |
| bfextu %d5{&30:&2},%d0 # is od suppressed? | |
| beq.w aii_bd | |
| cmpi.b %d0,&0x2 | |
| blt.b null_od | |
| beq.b word_od | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_long | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| bra.b add_them | |
| word_od: | |
| mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr | |
| addq.l &0x2,EXC_EXTWPTR(%a6) # incr instruction ptr | |
| bsr.l _imem_read_word | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| ext.l %d0 # sign extend od | |
| bra.b add_them | |
| null_od: | |
| clr.l %d0 | |
| add_them: | |
| mov.l %d0,%d4 | |
| btst &0x2,%d5 # pre or post indexing? | |
| beq.b pre_indexed | |
| mov.l %d3,%a0 | |
| bsr.l _dmem_read_long | |
| tst.l %d1 # dfetch error? | |
| bne.b calc_ea_err # yes | |
| add.l %d2,%d0 # <ea> += index | |
| add.l %d4,%d0 # <ea> += od | |
| bra.b done_ea | |
| pre_indexed: | |
| add.l %d2,%d3 # preindexing | |
| mov.l %d3,%a0 | |
| bsr.l _dmem_read_long | |
| tst.l %d1 # ifetch error? | |
| bne.b calc_ea_err # yes | |
| add.l %d4,%d0 # ea += od | |
| bra.b done_ea | |
| aii_bd: | |
| add.l %d2,%d3 # ea = (base + bd) + index | |
| mov.l %d3,%d0 | |
| done_ea: | |
| mov.l %d0,%a0 | |
| movm.l (%sp)+,&0x003c # restore d2-d5 | |
| rts | |
| # if dmem_read_long() returns a fail message in d1, the package | |
| # must create an access error frame. here, we pass a skeleton fslw | |
| # and the failing address to the routine that creates the new frame. | |
| # FSLW: | |
| # read = true | |
| # size = longword | |
| # TM = data | |
| # software emulation error = true | |
| calc_ea_err: | |
| mov.l %d3,%a0 # pass failing address | |
| mov.l &0x01010001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _moveperipheral(): routine to emulate movep instruction # | |
| # # | |
| # XREF **************************************************************** # | |
| # _dmem_read_byte() - read byte from memory # | |
| # _dmem_write_byte() - write byte to memory # | |
| # isp_dacc() - handle data access error exception # | |
| # # | |
| # INPUT *************************************************************** # | |
| # none # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If exiting through isp_dacc... # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # else # | |
| # none # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # Decode the movep instruction words stored at EXC_OPWORD and # | |
| # either read or write the required bytes from/to memory. Use the # | |
| # _dmem_{read,write}_byte() routines. If one of the memory routines # | |
| # returns a failing value, we must pass the failing address and a FSLW # | |
| # to the _isp_dacc() routine. # | |
| # Since this instruction is used to access peripherals, make sure # | |
| # to only access the required bytes. # | |
| # # | |
| ######################################################################### | |
| ########################### | |
| # movep.(w,l) Dx,(d,Ay) # | |
| # movep.(w,l) (d,Ay),Dx # | |
| ########################### | |
| global _moveperipheral | |
| _moveperipheral: | |
| mov.w EXC_OPWORD(%a6),%d1 # fetch the opcode word | |
| mov.b %d1,%d0 | |
| and.w &0x7,%d0 # extract Ay from opcode word | |
| mov.l (EXC_AREGS,%a6,%d0.w*4),%a0 # fetch ay | |
| add.w EXC_EXTWORD(%a6),%a0 # add: an + sgn_ext(disp) | |
| btst &0x7,%d1 # (reg 2 mem) or (mem 2 reg) | |
| beq.w mem2reg | |
| # reg2mem: fetch dx, then write it to memory | |
| reg2mem: | |
| mov.w %d1,%d0 | |
| rol.w &0x7,%d0 | |
| and.w &0x7,%d0 # extract Dx from opcode word | |
| mov.l (EXC_DREGS,%a6,%d0.w*4), %d0 # fetch dx | |
| btst &0x6,%d1 # word or long operation? | |
| beq.b r2mwtrans | |
| # a0 = dst addr | |
| # d0 = Dx | |
| r2mltrans: | |
| mov.l %d0,%d2 # store data | |
| mov.l %a0,%a2 # store addr | |
| rol.l &0x8,%d2 | |
| mov.l %d2,%d0 | |
| bsr.l _dmem_write_byte # os : write hi | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| add.w &0x2,%a2 # incr addr | |
| mov.l %a2,%a0 | |
| rol.l &0x8,%d2 | |
| mov.l %d2,%d0 | |
| bsr.l _dmem_write_byte # os : write lo | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| add.w &0x2,%a2 # incr addr | |
| mov.l %a2,%a0 | |
| rol.l &0x8,%d2 | |
| mov.l %d2,%d0 | |
| bsr.l _dmem_write_byte # os : write lo | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| add.w &0x2,%a2 # incr addr | |
| mov.l %a2,%a0 | |
| rol.l &0x8,%d2 | |
| mov.l %d2,%d0 | |
| bsr.l _dmem_write_byte # os : write lo | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| rts | |
| # a0 = dst addr | |
| # d0 = Dx | |
| r2mwtrans: | |
| mov.l %d0,%d2 # store data | |
| mov.l %a0,%a2 # store addr | |
| lsr.w &0x8,%d0 | |
| bsr.l _dmem_write_byte # os : write hi | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| add.w &0x2,%a2 | |
| mov.l %a2,%a0 | |
| mov.l %d2,%d0 | |
| bsr.l _dmem_write_byte # os : write lo | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_write_err # yes | |
| rts | |
| # mem2reg: read bytes from memory. | |
| # determines the dest register, and then writes the bytes into it. | |
| mem2reg: | |
| btst &0x6,%d1 # word or long operation? | |
| beq.b m2rwtrans | |
| # a0 = dst addr | |
| m2rltrans: | |
| mov.l %a0,%a2 # store addr | |
| bsr.l _dmem_read_byte # read first byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| mov.l %d0,%d2 | |
| add.w &0x2,%a2 # incr addr by 2 bytes | |
| mov.l %a2,%a0 | |
| bsr.l _dmem_read_byte # read second byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| lsl.w &0x8,%d2 | |
| mov.b %d0,%d2 # append bytes | |
| add.w &0x2,%a2 # incr addr by 2 bytes | |
| mov.l %a2,%a0 | |
| bsr.l _dmem_read_byte # read second byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| lsl.l &0x8,%d2 | |
| mov.b %d0,%d2 # append bytes | |
| add.w &0x2,%a2 # incr addr by 2 bytes | |
| mov.l %a2,%a0 | |
| bsr.l _dmem_read_byte # read second byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| lsl.l &0x8,%d2 | |
| mov.b %d0,%d2 # append bytes | |
| mov.b EXC_OPWORD(%a6),%d1 | |
| lsr.b &0x1,%d1 | |
| and.w &0x7,%d1 # extract Dx from opcode word | |
| mov.l %d2,(EXC_DREGS,%a6,%d1.w*4) # store dx | |
| rts | |
| # a0 = dst addr | |
| m2rwtrans: | |
| mov.l %a0,%a2 # store addr | |
| bsr.l _dmem_read_byte # read first byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| mov.l %d0,%d2 | |
| add.w &0x2,%a2 # incr addr by 2 bytes | |
| mov.l %a2,%a0 | |
| bsr.l _dmem_read_byte # read second byte | |
| tst.l %d1 # dfetch error? | |
| bne.w movp_read_err # yes | |
| lsl.w &0x8,%d2 | |
| mov.b %d0,%d2 # append bytes | |
| mov.b EXC_OPWORD(%a6),%d1 | |
| lsr.b &0x1,%d1 | |
| and.w &0x7,%d1 # extract Dx from opcode word | |
| mov.w %d2,(EXC_DREGS+2,%a6,%d1.w*4) # store dx | |
| rts | |
| # if dmem_{read,write}_byte() returns a fail message in d1, the package | |
| # must create an access error frame. here, we pass a skeleton fslw | |
| # and the failing address to the routine that creates the new frame. | |
| # FSLW: | |
| # write = true | |
| # size = byte | |
| # TM = data | |
| # software emulation error = true | |
| movp_write_err: | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x00a10001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| # FSLW: | |
| # read = true | |
| # size = byte | |
| # TM = data | |
| # software emulation error = true | |
| movp_read_err: | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x01210001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _chk2_cmp2(): routine to emulate chk2/cmp2 instructions # | |
| # # | |
| # XREF **************************************************************** # | |
| # _calc_ea(): calculate effective address # | |
| # _dmem_read_long(): read operands # | |
| # _dmem_read_word(): read operands # | |
| # isp_dacc(): handle data access error exception # | |
| # # | |
| # INPUT *************************************************************** # | |
| # none # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If exiting through isp_dacc... # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # else # | |
| # none # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # First, calculate the effective address, then fetch the byte, # | |
| # word, or longword sized operands. Then, in the interest of # | |
| # simplicity, all operands are converted to longword size whether the # | |
| # operation is byte, word, or long. The bounds are sign extended # | |
| # accordingly. If Rn is a data regsiter, Rn is also sign extended. If # | |
| # Rn is an address register, it need not be sign extended since the # | |
| # full register is always used. # | |
| # The comparisons are made and the condition codes calculated. # | |
| # If the instruction is chk2 and the Rn value is out-of-bounds, set # | |
| # the ichk_flg in SPCOND_FLG. # | |
| # If the memory fetch returns a failing value, pass the failing # | |
| # address and FSLW to the isp_dacc() routine. # | |
| # # | |
| ######################################################################### | |
| global _chk2_cmp2 | |
| _chk2_cmp2: | |
| # passing size parameter doesn't matter since chk2 & cmp2 can't do | |
| # either predecrement, postincrement, or immediate. | |
| bsr.l _calc_ea # calculate <ea> | |
| mov.b EXC_EXTWORD(%a6), %d0 # fetch hi extension word | |
| rol.b &0x4, %d0 # rotate reg bits into lo | |
| and.w &0xf, %d0 # extract reg bits | |
| mov.l (EXC_DREGS,%a6,%d0.w*4), %d2 # get regval | |
| cmpi.b EXC_OPWORD(%a6), &0x2 # what size is operation? | |
| blt.b chk2_cmp2_byte # size == byte | |
| beq.b chk2_cmp2_word # size == word | |
| # the bounds are longword size. call routine to read the lower | |
| # bound into d0 and the higher bound into d1. | |
| chk2_cmp2_long: | |
| mov.l %a0,%a2 # save copy of <ea> | |
| bsr.l _dmem_read_long # fetch long lower bound | |
| tst.l %d1 # dfetch error? | |
| bne.w chk2_cmp2_err_l # yes | |
| mov.l %d0,%d3 # save long lower bound | |
| addq.l &0x4,%a2 | |
| mov.l %a2,%a0 # pass <ea> of long upper bound | |
| bsr.l _dmem_read_long # fetch long upper bound | |
| tst.l %d1 # dfetch error? | |
| bne.w chk2_cmp2_err_l # yes | |
| mov.l %d0,%d1 # long upper bound in d1 | |
| mov.l %d3,%d0 # long lower bound in d0 | |
| bra.w chk2_cmp2_compare # go do the compare emulation | |
| # the bounds are word size. fetch them in one subroutine call by | |
| # reading a longword. sign extend both. if it's a data operation, | |
| # sign extend Rn to long, also. | |
| chk2_cmp2_word: | |
| mov.l %a0,%a2 | |
| bsr.l _dmem_read_long # fetch 2 word bounds | |
| tst.l %d1 # dfetch error? | |
| bne.w chk2_cmp2_err_l # yes | |
| mov.w %d0, %d1 # place hi in %d1 | |
| swap %d0 # place lo in %d0 | |
| ext.l %d0 # sign extend lo bnd | |
| ext.l %d1 # sign extend hi bnd | |
| btst &0x7, EXC_EXTWORD(%a6) # address compare? | |
| bne.w chk2_cmp2_compare # yes; don't sign extend | |
| # operation is a data register compare. | |
| # sign extend word to long so we can do simple longword compares. | |
| ext.l %d2 # sign extend data word | |
| bra.w chk2_cmp2_compare # go emulate compare | |
| # the bounds are byte size. fetch them in one subroutine call by | |
| # reading a word. sign extend both. if it's a data operation, | |
| # sign extend Rn to long, also. | |
| chk2_cmp2_byte: | |
| mov.l %a0,%a2 | |
| bsr.l _dmem_read_word # fetch 2 byte bounds | |
| tst.l %d1 # dfetch error? | |
| bne.w chk2_cmp2_err_w # yes | |
| mov.b %d0, %d1 # place hi in %d1 | |
| lsr.w &0x8, %d0 # place lo in %d0 | |
| extb.l %d0 # sign extend lo bnd | |
| extb.l %d1 # sign extend hi bnd | |
| btst &0x7, EXC_EXTWORD(%a6) # address compare? | |
| bne.b chk2_cmp2_compare # yes; don't sign extend | |
| # operation is a data register compare. | |
| # sign extend byte to long so we can do simple longword compares. | |
| extb.l %d2 # sign extend data byte | |
| # | |
| # To set the ccodes correctly: | |
| # (1) save 'Z' bit from (Rn - lo) | |
| # (2) save 'Z' and 'N' bits from ((hi - lo) - (Rn - hi)) | |
| # (3) keep 'X', 'N', and 'V' from before instruction | |
| # (4) combine ccodes | |
| # | |
| chk2_cmp2_compare: | |
| sub.l %d0, %d2 # (Rn - lo) | |
| mov.w %cc, %d3 # fetch resulting ccodes | |
| andi.b &0x4, %d3 # keep 'Z' bit | |
| sub.l %d0, %d1 # (hi - lo) | |
| cmp.l %d1,%d2 # ((hi - lo) - (Rn - hi)) | |
| mov.w %cc, %d4 # fetch resulting ccodes | |
| or.b %d4, %d3 # combine w/ earlier ccodes | |
| andi.b &0x5, %d3 # keep 'Z' and 'N' | |
| mov.w EXC_CC(%a6), %d4 # fetch old ccodes | |
| andi.b &0x1a, %d4 # keep 'X','N','V' bits | |
| or.b %d3, %d4 # insert new ccodes | |
| mov.w %d4, EXC_CC(%a6) # save new ccodes | |
| btst &0x3, EXC_EXTWORD(%a6) # separate chk2,cmp2 | |
| bne.b chk2_finish # it's a chk2 | |
| rts | |
| # this code handles the only difference between chk2 and cmp2. chk2 would | |
| # have trapped out if the value was out of bounds. we check this by seeing | |
| # if the 'N' bit was set by the operation. | |
| chk2_finish: | |
| btst &0x0, %d4 # is 'N' bit set? | |
| bne.b chk2_trap # yes;chk2 should trap | |
| rts | |
| chk2_trap: | |
| mov.b &ichk_flg,SPCOND_FLG(%a6) # set "special case" flag | |
| rts | |
| # if dmem_read_{long,word}() returns a fail message in d1, the package | |
| # must create an access error frame. here, we pass a skeleton fslw | |
| # and the failing address to the routine that creates the new frame. | |
| # FSLW: | |
| # read = true | |
| # size = longword | |
| # TM = data | |
| # software emulation error = true | |
| chk2_cmp2_err_l: | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x01010001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| # FSLW: | |
| # read = true | |
| # size = word | |
| # TM = data | |
| # software emulation error = true | |
| chk2_cmp2_err_w: | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x01410001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _div64(): routine to emulate div{u,s}.l <ea>,Dr:Dq # | |
| # 64/32->32r:32q # | |
| # # | |
| # XREF **************************************************************** # | |
| # _calc_ea() - calculate effective address # | |
| # isp_iacc() - handle instruction access error exception # | |
| # isp_dacc() - handle data access error exception # | |
| # isp_restore() - restore An on access error w/ -() or ()+ # | |
| # # | |
| # INPUT *************************************************************** # | |
| # none # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If exiting through isp_dacc... # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # else # | |
| # none # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # First, decode the operand location. If it's in Dn, fetch from # | |
| # the stack. If it's in memory, use _calc_ea() to calculate the # | |
| # effective address. Use _dmem_read_long() to fetch at that address. # | |
| # Unless the operand is immediate data. Then use _imem_read_long(). # | |
| # Send failures to isp_dacc() or isp_iacc() as appropriate. # | |
| # If the operands are signed, make them unsigned and save the # | |
| # sign info for later. Separate out special cases like divide-by-zero # | |
| # or 32-bit divides if possible. Else, use a special math algorithm # | |
| # to calculate the result. # | |
| # Restore sign info if signed instruction. Set the condition # | |
| # codes. Set idbyz_flg in SPCOND_FLG if divisor was zero. Store the # | |
| # quotient and remainder in the appropriate data registers on the stack.# | |
| # # | |
| ######################################################################### | |
| set NDIVISOR, EXC_TEMP+0x0 | |
| set NDIVIDEND, EXC_TEMP+0x1 | |
| set NDRSAVE, EXC_TEMP+0x2 | |
| set NDQSAVE, EXC_TEMP+0x4 | |
| set DDSECOND, EXC_TEMP+0x6 | |
| set DDQUOTIENT, EXC_TEMP+0x8 | |
| set DDNORMAL, EXC_TEMP+0xc | |
| global _div64 | |
| ############# | |
| # div(u,s)l # | |
| ############# | |
| _div64: | |
| mov.b EXC_OPWORD+1(%a6), %d0 | |
| andi.b &0x38, %d0 # extract src mode | |
| bne.w dcontrolmodel_s # %dn dest or control mode? | |
| mov.b EXC_OPWORD+1(%a6), %d0 # extract Dn from opcode | |
| andi.w &0x7, %d0 | |
| mov.l (EXC_DREGS,%a6,%d0.w*4), %d7 # fetch divisor from register | |
| dgotsrcl: | |
| beq.w div64eq0 # divisor is = 0!!! | |
| mov.b EXC_EXTWORD+1(%a6), %d0 # extract Dr from extword | |
| mov.b EXC_EXTWORD(%a6), %d1 # extract Dq from extword | |
| and.w &0x7, %d0 | |
| lsr.b &0x4, %d1 | |
| and.w &0x7, %d1 | |
| mov.w %d0, NDRSAVE(%a6) # save Dr for later | |
| mov.w %d1, NDQSAVE(%a6) # save Dq for later | |
| # fetch %dr and %dq directly off stack since all regs are saved there | |
| mov.l (EXC_DREGS,%a6,%d0.w*4), %d5 # get dividend hi | |
| mov.l (EXC_DREGS,%a6,%d1.w*4), %d6 # get dividend lo | |
| # separate signed and unsigned divide | |
| btst &0x3, EXC_EXTWORD(%a6) # signed or unsigned? | |
| beq.b dspecialcases # use positive divide | |
| # save the sign of the divisor | |
| # make divisor unsigned if it's negative | |
| tst.l %d7 # chk sign of divisor | |
| slt NDIVISOR(%a6) # save sign of divisor | |
| bpl.b dsgndividend | |
| neg.l %d7 # complement negative divisor | |
| # save the sign of the dividend | |
| # make dividend unsigned if it's negative | |
| dsgndividend: | |
| tst.l %d5 # chk sign of hi(dividend) | |
| slt NDIVIDEND(%a6) # save sign of dividend | |
| bpl.b dspecialcases | |
| mov.w &0x0, %cc # clear 'X' cc bit | |
| negx.l %d6 # complement signed dividend | |
| negx.l %d5 | |
| # extract some special cases: | |
| # - is (dividend == 0) ? | |
| # - is (hi(dividend) == 0 && (divisor <= lo(dividend))) ? (32-bit div) | |
| dspecialcases: | |
| tst.l %d5 # is (hi(dividend) == 0) | |
| bne.b dnormaldivide # no, so try it the long way | |
| tst.l %d6 # is (lo(dividend) == 0), too | |
| beq.w ddone # yes, so (dividend == 0) | |
| cmp.l %d7,%d6 # is (divisor <= lo(dividend)) | |
| bls.b d32bitdivide # yes, so use 32 bit divide | |
| exg %d5,%d6 # q = 0, r = dividend | |
| bra.w divfinish # can't divide, we're done. | |
| d32bitdivide: | |
| tdivu.l %d7, %d5:%d6 # it's only a 32/32 bit div! | |
| bra.b divfinish | |
| dnormaldivide: | |
| # last special case: | |
| # - is hi(dividend) >= divisor ? if yes, then overflow | |
| cmp.l %d7,%d5 | |
| bls.b ddovf # answer won't fit in 32 bits | |
| # perform the divide algorithm: | |
| bsr.l dclassical # do int divide | |
| # separate into signed and unsigned finishes. | |
| divfinish: | |
| btst &0x3, EXC_EXTWORD(%a6) # do divs, divu separately | |
| beq.b ddone # divu has no processing!!! | |
| # it was a divs.l, so ccode setting is a little more complicated... | |
| tst.b NDIVIDEND(%a6) # remainder has same sign | |
| beq.b dcc # as dividend. | |
| neg.l %d5 # sgn(rem) = sgn(dividend) | |
| dcc: | |
| mov.b NDIVISOR(%a6), %d0 | |
| eor.b %d0, NDIVIDEND(%a6) # chk if quotient is negative | |
| beq.b dqpos # branch to quot positive | |
| # 0x80000000 is the largest number representable as a 32-bit negative | |
| # number. the negative of 0x80000000 is 0x80000000. | |
| cmpi.l %d6, &0x80000000 # will (-quot) fit in 32 bits? | |
| bhi.b ddovf | |
| neg.l %d6 # make (-quot) 2's comp | |
| bra.b ddone | |
| dqpos: | |
| btst &0x1f, %d6 # will (+quot) fit in 32 bits? | |
| bne.b ddovf | |
| ddone: | |
| # at this point, result is normal so ccodes are set based on result. | |
| mov.w EXC_CC(%a6), %cc | |
| tst.l %d6 # set %ccode bits | |
| mov.w %cc, EXC_CC(%a6) | |
| mov.w NDRSAVE(%a6), %d0 # get Dr off stack | |
| mov.w NDQSAVE(%a6), %d1 # get Dq off stack | |
| # if the register numbers are the same, only the quotient gets saved. | |
| # so, if we always save the quotient second, we save ourselves a cmp&beq | |
| mov.l %d5, (EXC_DREGS,%a6,%d0.w*4) # save remainder | |
| mov.l %d6, (EXC_DREGS,%a6,%d1.w*4) # save quotient | |
| rts | |
| ddovf: | |
| bset &0x1, EXC_CC+1(%a6) # 'V' set on overflow | |
| bclr &0x0, EXC_CC+1(%a6) # 'C' cleared on overflow | |
| rts | |
| div64eq0: | |
| andi.b &0x1e, EXC_CC+1(%a6) # clear 'C' bit on divbyzero | |
| ori.b &idbyz_flg,SPCOND_FLG(%a6) # set "special case" flag | |
| rts | |
| ########################################################################### | |
| ######################################################################### | |
| # This routine uses the 'classical' Algorithm D from Donald Knuth's # | |
| # Art of Computer Programming, vol II, Seminumerical Algorithms. # | |
| # For this implementation b=2**16, and the target is U1U2U3U4/V1V2, # | |
| # where U,V are words of the quadword dividend and longword divisor, # | |
| # and U1, V1 are the most significant words. # | |
| # # | |
| # The most sig. longword of the 64 bit dividend must be in %d5, least # | |
| # in %d6. The divisor must be in the variable ddivisor, and the # | |
| # signed/unsigned flag ddusign must be set (0=unsigned,1=signed). # | |
| # The quotient is returned in %d6, remainder in %d5, unless the # | |
| # v (overflow) bit is set in the saved %ccr. If overflow, the dividend # | |
| # is unchanged. # | |
| ######################################################################### | |
| dclassical: | |
| # if the divisor msw is 0, use simpler algorithm then the full blown | |
| # one at ddknuth: | |
| cmpi.l %d7, &0xffff | |
| bhi.b ddknuth # go use D. Knuth algorithm | |
| # Since the divisor is only a word (and larger than the mslw of the dividend), | |
| # a simpler algorithm may be used : | |
| # In the general case, four quotient words would be created by | |
| # dividing the divisor word into each dividend word. In this case, | |
| # the first two quotient words must be zero, or overflow would occur. | |
| # Since we already checked this case above, we can treat the most significant | |
| # longword of the dividend as (0) remainder (see Knuth) and merely complete | |
| # the last two divisions to get a quotient longword and word remainder: | |
| clr.l %d1 | |
| swap %d5 # same as r*b if previous step rqd | |
| swap %d6 # get u3 to lsw position | |
| mov.w %d6, %d5 # rb + u3 | |
| divu.w %d7, %d5 | |
| mov.w %d5, %d1 # first quotient word | |
| swap %d6 # get u4 | |
| mov.w %d6, %d5 # rb + u4 | |
| divu.w %d7, %d5 | |
| swap %d1 | |
| mov.w %d5, %d1 # 2nd quotient 'digit' | |
| clr.w %d5 | |
| swap %d5 # now remainder | |
| mov.l %d1, %d6 # and quotient | |
| rts | |
| ddknuth: | |
| # In this algorithm, the divisor is treated as a 2 digit (word) number | |
| # which is divided into a 3 digit (word) dividend to get one quotient | |
| # digit (word). After subtraction, the dividend is shifted and the | |
| # process repeated. Before beginning, the divisor and quotient are | |
| # 'normalized' so that the process of estimating the quotient digit | |
| # will yield verifiably correct results.. | |
| clr.l DDNORMAL(%a6) # count of shifts for normalization | |
| clr.b DDSECOND(%a6) # clear flag for quotient digits | |
| clr.l %d1 # %d1 will hold trial quotient | |
| ddnchk: | |
| btst &31, %d7 # must we normalize? first word of | |
| bne.b ddnormalized # divisor (V1) must be >= 65536/2 | |
| addq.l &0x1, DDNORMAL(%a6) # count normalization shifts | |
| lsl.l &0x1, %d7 # shift the divisor | |
| lsl.l &0x1, %d6 # shift u4,u3 with overflow to u2 | |
| roxl.l &0x1, %d5 # shift u1,u2 | |
| bra.w ddnchk | |
| ddnormalized: | |
| # Now calculate an estimate of the quotient words (msw first, then lsw). | |
| # The comments use subscripts for the first quotient digit determination. | |
| mov.l %d7, %d3 # divisor | |
| mov.l %d5, %d2 # dividend mslw | |
| swap %d2 | |
| swap %d3 | |
| cmp.w %d2, %d3 # V1 = U1 ? | |
| bne.b ddqcalc1 | |
| mov.w &0xffff, %d1 # use max trial quotient word | |
| bra.b ddadj0 | |
| ddqcalc1: | |
| mov.l %d5, %d1 | |
| divu.w %d3, %d1 # use quotient of mslw/msw | |
| andi.l &0x0000ffff, %d1 # zero any remainder | |
| ddadj0: | |
| # now test the trial quotient and adjust. This step plus the | |
| # normalization assures (according to Knuth) that the trial | |
| # quotient will be at worst 1 too large. | |
| mov.l %d6, -(%sp) | |
| clr.w %d6 # word u3 left | |
| swap %d6 # in lsw position | |
| ddadj1: mov.l %d7, %d3 | |
| mov.l %d1, %d2 | |
| mulu.w %d7, %d2 # V2q | |
| swap %d3 | |
| mulu.w %d1, %d3 # V1q | |
| mov.l %d5, %d4 # U1U2 | |
| sub.l %d3, %d4 # U1U2 - V1q | |
| swap %d4 | |
| mov.w %d4,%d0 | |
| mov.w %d6,%d4 # insert lower word (U3) | |
| tst.w %d0 # is upper word set? | |
| bne.w ddadjd1 | |
| # add.l %d6, %d4 # (U1U2 - V1q) + U3 | |
| cmp.l %d2, %d4 | |
| bls.b ddadjd1 # is V2q > (U1U2-V1q) + U3 ? | |
| subq.l &0x1, %d1 # yes, decrement and recheck | |
| bra.b ddadj1 | |
| ddadjd1: | |
| # now test the word by multiplying it by the divisor (V1V2) and comparing | |
| # the 3 digit (word) result with the current dividend words | |
| mov.l %d5, -(%sp) # save %d5 (%d6 already saved) | |
| mov.l %d1, %d6 | |
| swap %d6 # shift answer to ms 3 words | |
| mov.l %d7, %d5 | |
| bsr.l dmm2 | |
| mov.l %d5, %d2 # now %d2,%d3 are trial*divisor | |
| mov.l %d6, %d3 | |
| mov.l (%sp)+, %d5 # restore dividend | |
| mov.l (%sp)+, %d6 | |
| sub.l %d3, %d6 | |
| subx.l %d2, %d5 # subtract double precision | |
| bcc dd2nd # no carry, do next quotient digit | |
| subq.l &0x1, %d1 # q is one too large | |
| # need to add back divisor longword to current ms 3 digits of dividend | |
| # - according to Knuth, this is done only 2 out of 65536 times for random | |
| # divisor, dividend selection. | |
| clr.l %d2 | |
| mov.l %d7, %d3 | |
| swap %d3 | |
| clr.w %d3 # %d3 now ls word of divisor | |
| add.l %d3, %d6 # aligned with 3rd word of dividend | |
| addx.l %d2, %d5 | |
| mov.l %d7, %d3 | |
| clr.w %d3 # %d3 now ms word of divisor | |
| swap %d3 # aligned with 2nd word of dividend | |
| add.l %d3, %d5 | |
| dd2nd: | |
| tst.b DDSECOND(%a6) # both q words done? | |
| bne.b ddremain | |
| # first quotient digit now correct. store digit and shift the | |
| # (subtracted) dividend | |
| mov.w %d1, DDQUOTIENT(%a6) | |
| clr.l %d1 | |
| swap %d5 | |
| swap %d6 | |
| mov.w %d6, %d5 | |
| clr.w %d6 | |
| st DDSECOND(%a6) # second digit | |
| bra.w ddnormalized | |
| ddremain: | |
| # add 2nd word to quotient, get the remainder. | |
| mov.w %d1, DDQUOTIENT+2(%a6) | |
| # shift down one word/digit to renormalize remainder. | |
| mov.w %d5, %d6 | |
| swap %d6 | |
| swap %d5 | |
| mov.l DDNORMAL(%a6), %d7 # get norm shift count | |
| beq.b ddrn | |
| subq.l &0x1, %d7 # set for loop count | |
| ddnlp: | |
| lsr.l &0x1, %d5 # shift into %d6 | |
| roxr.l &0x1, %d6 | |
| dbf %d7, ddnlp | |
| ddrn: | |
| mov.l %d6, %d5 # remainder | |
| mov.l DDQUOTIENT(%a6), %d6 # quotient | |
| rts | |
| dmm2: | |
| # factors for the 32X32->64 multiplication are in %d5 and %d6. | |
| # returns 64 bit result in %d5 (hi) %d6(lo). | |
| # destroys %d2,%d3,%d4. | |
| # multiply hi,lo words of each factor to get 4 intermediate products | |
| mov.l %d6, %d2 | |
| mov.l %d6, %d3 | |
| mov.l %d5, %d4 | |
| swap %d3 | |
| swap %d4 | |
| mulu.w %d5, %d6 # %d6 <- lsw*lsw | |
| mulu.w %d3, %d5 # %d5 <- msw-dest*lsw-source | |
| mulu.w %d4, %d2 # %d2 <- msw-source*lsw-dest | |
| mulu.w %d4, %d3 # %d3 <- msw*msw | |
| # now use swap and addx to consolidate to two longwords | |
| clr.l %d4 | |
| swap %d6 | |
| add.w %d5, %d6 # add msw of l*l to lsw of m*l product | |
| addx.w %d4, %d3 # add any carry to m*m product | |
| add.w %d2, %d6 # add in lsw of other m*l product | |
| addx.w %d4, %d3 # add any carry to m*m product | |
| swap %d6 # %d6 is low 32 bits of final product | |
| clr.w %d5 | |
| clr.w %d2 # lsw of two mixed products used, | |
| swap %d5 # now use msws of longwords | |
| swap %d2 | |
| add.l %d2, %d5 | |
| add.l %d3, %d5 # %d5 now ms 32 bits of final product | |
| rts | |
| ########## | |
| dcontrolmodel_s: | |
| movq.l &LONG,%d0 | |
| bsr.l _calc_ea # calc <ea> | |
| cmpi.b SPCOND_FLG(%a6),&immed_flg # immediate addressing mode? | |
| beq.b dimmed # yes | |
| mov.l %a0,%a2 | |
| bsr.l _dmem_read_long # fetch divisor from <ea> | |
| tst.l %d1 # dfetch error? | |
| bne.b div64_err # yes | |
| mov.l %d0, %d7 | |
| bra.w dgotsrcl | |
| # we have to split out immediate data here because it must be read using | |
| # imem_read() instead of dmem_read(). this becomes especially important | |
| # if the fetch runs into some deadly fault. | |
| dimmed: | |
| addq.l &0x4,EXC_EXTWPTR(%a6) | |
| bsr.l _imem_read_long # read immediate value | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.l %d0,%d7 | |
| bra.w dgotsrcl | |
| ########## | |
| # if dmem_read_long() returns a fail message in d1, the package | |
| # must create an access error frame. here, we pass a skeleton fslw | |
| # and the failing address to the routine that creates the new frame. | |
| # also, we call isp_restore in case the effective addressing mode was | |
| # (an)+ or -(an) in which case the previous "an" value must be restored. | |
| # FSLW: | |
| # read = true | |
| # size = longword | |
| # TM = data | |
| # software emulation error = true | |
| div64_err: | |
| bsr.l isp_restore # restore addr reg | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x01010001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _mul64(): routine to emulate mul{u,s}.l <ea>,Dh:Dl 32x32->64 # | |
| # # | |
| # XREF **************************************************************** # | |
| # _calc_ea() - calculate effective address # | |
| # isp_iacc() - handle instruction access error exception # | |
| # isp_dacc() - handle data access error exception # | |
| # isp_restore() - restore An on access error w/ -() or ()+ # | |
| # # | |
| # INPUT *************************************************************** # | |
| # none # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # If exiting through isp_dacc... # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # else # | |
| # none # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # First, decode the operand location. If it's in Dn, fetch from # | |
| # the stack. If it's in memory, use _calc_ea() to calculate the # | |
| # effective address. Use _dmem_read_long() to fetch at that address. # | |
| # Unless the operand is immediate data. Then use _imem_read_long(). # | |
| # Send failures to isp_dacc() or isp_iacc() as appropriate. # | |
| # If the operands are signed, make them unsigned and save the # | |
| # sign info for later. Perform the multiplication using 16x16->32 # | |
| # unsigned multiplies and "add" instructions. Store the high and low # | |
| # portions of the result in the appropriate data registers on the # | |
| # stack. Calculate the condition codes, also. # | |
| # # | |
| ######################################################################### | |
| ############# | |
| # mul(u,s)l # | |
| ############# | |
| global _mul64 | |
| _mul64: | |
| mov.b EXC_OPWORD+1(%a6), %d0 # extract src {mode,reg} | |
| cmpi.b %d0, &0x7 # is src mode Dn or other? | |
| bgt.w mul64_memop # src is in memory | |
| # multiplier operand in the 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 <ea> and go fetch the 32-bit operand. | |
| mul64_memop: | |
| movq.l &LONG, %d0 # pass # of bytes | |
| bsr.l _calc_ea # calculate <ea> | |
| cmpi.b SPCOND_FLG(%a6),&immed_flg # immediate addressing mode? | |
| beq.b mul64_immed # yes | |
| mov.l %a0,%a2 | |
| bsr.l _dmem_read_long # fetch src from addr (%a0) | |
| tst.l %d1 # dfetch error? | |
| bne.w mul64_err # yes | |
| mov.l %d0, %d3 # store multiplier in %d3 | |
| bra.w mul64_multiplicand | |
| # we have to split out immediate data here because it must be read using | |
| # imem_read() instead of dmem_read(). this becomes especially important | |
| # if the fetch runs into some deadly fault. | |
| mul64_immed: | |
| addq.l &0x4,EXC_EXTWPTR(%a6) | |
| bsr.l _imem_read_long # read immediate value | |
| tst.l %d1 # ifetch error? | |
| bne.l isp_iacc # yes | |
| mov.l %d0,%d3 | |
| bra.w mul64_multiplicand | |
| ########## | |
| # if dmem_read_long() returns a fail message in d1, the package | |
| # must create an access error frame. here, we pass a skeleton fslw | |
| # and the failing address to the routine that creates the new frame. | |
| # also, we call isp_restore in case the effective addressing mode was | |
| # (an)+ or -(an) in which case the previous "an" value must be restored. | |
| # FSLW: | |
| # read = true | |
| # size = longword | |
| # TM = data | |
| # software emulation error = true | |
| mul64_err: | |
| bsr.l isp_restore # restore addr reg | |
| mov.l %a2,%a0 # pass failing address | |
| mov.l &0x01010001,%d0 # pass fslw | |
| bra.l isp_dacc | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _compandset2(): routine to emulate cas2() # | |
| # (internal to package) # | |
| # # | |
| # _isp_cas2_finish(): store ccodes, store compare regs # | |
| # (external to package) # | |
| # # | |
| # XREF **************************************************************** # | |
| # _real_lock_page() - "callout" to lock op's page from page-outs # | |
| # _cas_terminate2() - access error exit # | |
| # _real_cas2() - "callout" to core cas2 emulation code # | |
| # _real_unlock_page() - "callout" to unlock page # | |
| # # | |
| # INPUT *************************************************************** # | |
| # _compandset2(): # | |
| # d0 = instruction extension word # | |
| # # | |
| # _isp_cas2_finish(): # | |
| # see cas2 core emulation code # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # _compandset2(): # | |
| # see cas2 core emulation code # | |
| # # | |
| # _isp_cas_finish(): # | |
| # None (register file or memroy changed as appropriate) # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # compandset2(): # | |
| # Decode the instruction and fetch the appropriate Update and # | |
| # Compare operands. Then call the "callout" _real_lock_page() for each # | |
| # memory operand address so that the operating system can keep these # | |
| # pages from being paged out. If either _real_lock_page() fails, exit # | |
| # through _cas_terminate2(). Don't forget to unlock the 1st locked page # | |
| # using _real_unlock_paged() if the 2nd lock-page fails. # | |
| # Finally, branch to the core cas2 emulation code by calling the # | |
| # "callout" _real_cas2(). # | |
| # # | |
| # _isp_cas2_finish(): # | |
| # Re-perform the comparison so we can determine the condition # | |
| # codes which were too much trouble to keep around during the locked # | |
| # emulation. Then unlock each operands page by calling the "callout" # | |
| # _real_unlock_page(). # | |
| # # | |
| ######################################################################### | |
| set ADDR1, EXC_TEMP+0xc | |
| set ADDR2, EXC_TEMP+0x0 | |
| set DC2, EXC_TEMP+0xa | |
| set DC1, EXC_TEMP+0x8 | |
| global _compandset2 | |
| _compandset2: | |
| mov.l %d0,EXC_TEMP+0x4(%a6) # store for possible restart | |
| mov.l %d0,%d1 # extension word in d0 | |
| rol.w &0x4,%d0 | |
| andi.w &0xf,%d0 # extract Rn2 | |
| mov.l (EXC_DREGS,%a6,%d0.w*4),%a1 # fetch ADDR2 | |
| mov.l %a1,ADDR2(%a6) | |
| mov.l %d1,%d0 | |
| lsr.w &0x6,%d1 | |
| andi.w &0x7,%d1 # extract Du2 | |
| mov.l (EXC_DREGS,%a6,%d1.w*4),%d5 # fetch Update2 Op | |
| andi.w &0x7,%d0 # extract Dc2 | |
| mov.l (EXC_DREGS,%a6,%d0.w*4),%d3 # fetch Compare2 Op | |
| mov.w %d0,DC2(%a6) | |
| mov.w EXC_EXTWORD(%a6),%d0 | |
| mov.l %d0,%d1 | |
| rol.w &0x4,%d0 | |
| andi.w &0xf,%d0 # extract Rn1 | |
| mov.l (EXC_DREGS,%a6,%d0.w*4),%a0 # fetch ADDR1 | |
| mov.l %a0,ADDR1(%a6) | |
| mov.l %d1,%d0 | |
| lsr.w &0x6,%d1 | |
| andi.w &0x7,%d1 # extract Du1 | |
| mov.l (EXC_DREGS,%a6,%d1.w*4),%d4 # fetch Update1 Op | |
| andi.w &0x7,%d0 # extract Dc1 | |
| mov.l (EXC_DREGS,%a6,%d0.w*4),%d2 # fetch Compare1 Op | |
| mov.w %d0,DC1(%a6) | |
| btst &0x1,EXC_OPWORD(%a6) # word or long? | |
| sne %d7 | |
| btst &0x5,EXC_ISR(%a6) # user or supervisor? | |
| sne %d6 | |
| mov.l %a0,%a2 | |
| mov.l %a1,%a3 | |
| mov.l %d7,%d1 # pass size | |
| mov.l %d6,%d0 # pass mode | |
| bsr.l _real_lock_page # lock page | |
| mov.l %a2,%a0 | |
| tst.l %d0 # error? | |
| bne.l _cas_terminate2 # yes | |
| mov.l %d7,%d1 # pass size | |
| mov.l %d6,%d0 # pass mode | |
| mov.l %a3,%a0 # pass addr | |
| bsr.l _real_lock_page # lock page | |
| mov.l %a3,%a0 | |
| tst.l %d0 # error? | |
| bne.b cas_preterm # yes | |
| mov.l %a2,%a0 | |
| mov.l %a3,%a1 | |
| bra.l _real_cas2 | |
| # if the 2nd lock attempt fails, then we must still unlock the | |
| # first page(s). | |
| cas_preterm: | |
| mov.l %d0,-(%sp) # save FSLW | |
| mov.l %d7,%d1 # pass size | |
| mov.l %d6,%d0 # pass mode | |
| mov.l %a2,%a0 # pass ADDR1 | |
| bsr.l _real_unlock_page # unlock first page(s) | |
| mov.l (%sp)+,%d0 # restore FSLW | |
| mov.l %a3,%a0 # pass failing addr | |
| bra.l _cas_terminate2 | |
| ############################################################# | |
| global _isp_cas2_finish | |
| _isp_cas2_finish: | |
| btst &0x1,EXC_OPWORD(%a6) | |
| bne.b cas2_finish_l | |
| mov.w EXC_CC(%a6),%cc # load old ccodes | |
| cmp.w %d0,%d2 | |
| bne.b cas2_finish_w_save | |
| cmp.w %d1,%d3 | |
| cas2_finish_w_save: | |
| mov.w %cc,EXC_CC(%a6) # save new ccodes | |
| tst.b %d4 # update compare reg? | |
| bne.b cas2_finish_w_done # no | |
| mov.w DC2(%a6),%d3 # fetch Dc2 | |
| mov.w %d1,(2+EXC_DREGS,%a6,%d3.w*4) # store new Compare2 Op | |
| mov.w DC1(%a6),%d2 # fetch Dc1 | |
| mov.w %d0,(2+EXC_DREGS,%a6,%d2.w*4) # store new Compare1 Op | |
| cas2_finish_w_done: | |
| btst &0x5,EXC_ISR(%a6) | |
| sne %d2 | |
| mov.l %d2,%d0 # pass mode | |
| sf %d1 # pass size | |
| mov.l ADDR1(%a6),%a0 # pass ADDR1 | |
| bsr.l _real_unlock_page # unlock page | |
| mov.l %d2,%d0 # pass mode | |
| sf %d1 # pass size | |
| mov.l ADDR2(%a6),%a0 # pass ADDR2 | |
| bsr.l _real_unlock_page # unlock page | |
| rts | |
| cas2_finish_l: | |
| mov.w EXC_CC(%a6),%cc # load old ccodes | |
| cmp.l %d0,%d2 | |
| bne.b cas2_finish_l_save | |
| cmp.l %d1,%d3 | |
| cas2_finish_l_save: | |
| mov.w %cc,EXC_CC(%a6) # save new ccodes | |
| tst.b %d4 # update compare reg? | |
| bne.b cas2_finish_l_done # no | |
| mov.w DC2(%a6),%d3 # fetch Dc2 | |
| mov.l %d1,(EXC_DREGS,%a6,%d3.w*4) # store new Compare2 Op | |
| mov.w DC1(%a6),%d2 # fetch Dc1 | |
| mov.l %d0,(EXC_DREGS,%a6,%d2.w*4) # store new Compare1 Op | |
| cas2_finish_l_done: | |
| btst &0x5,EXC_ISR(%a6) | |
| sne %d2 | |
| mov.l %d2,%d0 # pass mode | |
| st %d1 # pass size | |
| mov.l ADDR1(%a6),%a0 # pass ADDR1 | |
| bsr.l _real_unlock_page # unlock page | |
| mov.l %d2,%d0 # pass mode | |
| st %d1 # pass size | |
| mov.l ADDR2(%a6),%a0 # pass ADDR2 | |
| bsr.l _real_unlock_page # unlock page | |
| rts | |
| ######## | |
| global cr_cas2 | |
| cr_cas2: | |
| mov.l EXC_TEMP+0x4(%a6),%d0 | |
| bra.w _compandset2 | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _compandset(): routine to emulate cas w/ misaligned <ea> # | |
| # (internal to package) # | |
| # _isp_cas_finish(): routine called when cas emulation completes # | |
| # (external and internal to package) # | |
| # _isp_cas_restart(): restart cas emulation after a fault # | |
| # (external to package) # | |
| # _isp_cas_terminate(): create access error stack frame on fault # | |
| # (external and internal to package) # | |
| # _isp_cas_inrange(): checks whether instr addess is within range # | |
| # of core cas/cas2emulation code # | |
| # (external to package) # | |
| # # | |
| # XREF **************************************************************** # | |
| # _calc_ea(): calculate effective address # | |
| # # | |
| # INPUT *************************************************************** # | |
| # compandset(): # | |
| # none # | |
| # _isp_cas_restart(): # | |
| # d6 = previous sfc/dfc # | |
| # _isp_cas_finish(): # | |
| # _isp_cas_terminate(): # | |
| # a0 = failing address # | |
| # d0 = FSLW # | |
| # d6 = previous sfc/dfc # | |
| # _isp_cas_inrange(): # | |
| # a0 = instruction address to be checked # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # compandset(): # | |
| # none # | |
| # _isp_cas_restart(): # | |
| # a0 = effective address # | |
| # d7 = word or longword flag # | |
| # _isp_cas_finish(): # | |
| # a0 = effective address # | |
| # _isp_cas_terminate(): # | |
| # initial register set before emulation exception # | |
| # _isp_cas_inrange(): # | |
| # d0 = 0 => in range; -1 => out of range # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # # | |
| # compandset(): # | |
| # First, calculate the effective address. Then, decode the # | |
| # instruction word and fetch the "compare" (DC) and "update" (Du) # | |
| # operands. # | |
| # Next, call the external routine _real_lock_page() so that the # | |
| # operating system can keep this page from being paged out while we're # | |
| # in this routine. If this call fails, jump to _cas_terminate2(). # | |
| # The routine then branches to _real_cas(). This external routine # | |
| # that actually emulates cas can be supplied by the external os or # | |
| # made to point directly back into the 060ISP which has a routine for # | |
| # this purpose. # | |
| # # | |
| # _isp_cas_finish(): # | |
| # Either way, after emulation, the package is re-entered at # | |
| # _isp_cas_finish(). This routine re-compares the operands in order to # | |
| # set the condition codes. Finally, these routines will call # | |
| # _real_unlock_page() in order to unlock the pages that were previously # | |
| # locked. # | |
| # # | |
| # _isp_cas_restart(): # | |
| # This routine can be entered from an access error handler where # | |
| # the emulation sequence should be re-started from the beginning. # | |
| # # | |
| # _isp_cas_terminate(): # | |
| # This routine can be entered from an access error handler where # | |
| # an emulation operand access failed and the operating system would # | |
| # like an access error stack frame created instead of the current # | |
| # unimplemented integer instruction frame. # | |
| # Also, the package enters here if a call to _real_lock_page() # | |
| # fails. # | |
| # # | |
| # _isp_cas_inrange(): # | |
| # Checks to see whether the instruction address passed to it in # | |
| # a0 is within the software package cas/cas2 emulation routines. This # | |
| # can be helpful for an operating system to determine whether an access # | |
| # error during emulation was due to a cas/cas2 emulation access. # | |
| # # | |
| ######################################################################### | |
| set DC, EXC_TEMP+0x8 | |
| set ADDR, EXC_TEMP+0x4 | |
| global _compandset | |
| _compandset: | |
| btst &0x1,EXC_OPWORD(%a6) # word or long operation? | |
| bne.b compandsetl # long | |
| compandsetw: | |
| movq.l &0x2,%d0 # size = 2 bytes | |
| bsr.l _calc_ea # a0 = calculated <ea> | |
| mov.l %a0,ADDR(%a6) # save <ea> for possible restart | |
| sf %d7 # clear d7 for word size | |
| bra.b compandsetfetch | |
| compandsetl: | |
| movq.l &0x4,%d0 # size = 4 bytes | |
| bsr.l _calc_ea # a0 = calculated <ea> | |
| mov.l %a0,ADDR(%a6) # save <ea> for possible restart | |
| st %d7 # set d7 for longword size | |
| compandsetfetch: | |
| mov.w EXC_EXTWORD(%a6),%d0 # fetch cas extension word | |
| mov.l %d0,%d1 # make a copy | |
| lsr.w &0x6,%d0 | |
| andi.w &0x7,%d0 # extract Du | |
| mov.l (EXC_DREGS,%a6,%d0.w*4),%d2 # get update operand | |
| andi.w &0x7,%d1 # extract Dc | |
| mov.l (EXC_DREGS,%a6,%d1.w*4),%d4 # get compare operand | |
| mov.w %d1,DC(%a6) # save Dc | |
| btst &0x5,EXC_ISR(%a6) # which mode for exception? | |
| sne %d6 # set on supervisor mode | |
| mov.l %a0,%a2 # save temporarily | |
| mov.l %d7,%d1 # pass size | |
| mov.l %d6,%d0 # pass mode | |
| bsr.l _real_lock_page # lock page | |
| tst.l %d0 # did error occur? | |
| bne.w _cas_terminate2 # yes, clean up the mess | |
| mov.l %a2,%a0 # pass addr in a0 | |
| bra.l _real_cas | |
| ######## | |
| global _isp_cas_finish | |
| _isp_cas_finish: | |
| btst &0x1,EXC_OPWORD(%a6) | |
| bne.b cas_finish_l | |
| # just do the compare again since it's faster than saving the ccodes | |
| # from the locked routine... | |
| cas_finish_w: | |
| mov.w EXC_CC(%a6),%cc # restore cc | |
| cmp.w %d0,%d4 # do word compare | |
| mov.w %cc,EXC_CC(%a6) # save cc | |
| tst.b %d1 # update compare reg? | |
| bne.b cas_finish_w_done # no | |
| mov.w DC(%a6),%d3 | |
| mov.w %d0,(EXC_DREGS+2,%a6,%d3.w*4) # Dc = destination | |
| cas_finish_w_done: | |
| mov.l ADDR(%a6),%a0 # pass addr | |
| sf %d1 # pass size | |
| btst &0x5,EXC_ISR(%a6) | |
| sne %d0 # pass mode | |
| bsr.l _real_unlock_page # unlock page | |
| rts | |
| # just do the compare again since it's faster than saving the ccodes | |
| # from the locked routine... | |
| cas_finish_l: | |
| mov.w EXC_CC(%a6),%cc # restore cc | |
| cmp.l %d0,%d4 # do longword compare | |
| mov.w %cc,EXC_CC(%a6) # save cc | |
| tst.b %d1 # update compare reg? | |
| bne.b cas_finish_l_done # no | |
| mov.w DC(%a6),%d3 | |
| mov.l %d0,(EXC_DREGS,%a6,%d3.w*4) # Dc = destination | |
| cas_finish_l_done: | |
| mov.l ADDR(%a6),%a0 # pass addr | |
| st %d1 # pass size | |
| btst &0x5,EXC_ISR(%a6) | |
| sne %d0 # pass mode | |
| bsr.l _real_unlock_page # unlock page | |
| rts | |
| ######## | |
| global _isp_cas_restart | |
| _isp_cas_restart: | |
| mov.l %d6,%sfc # restore previous sfc | |
| mov.l %d6,%dfc # restore previous dfc | |
| cmpi.b EXC_OPWORD+1(%a6),&0xfc # cas or cas2? | |
| beq.l cr_cas2 # cas2 | |
| cr_cas: | |
| mov.l ADDR(%a6),%a0 # load <ea> | |
| btst &0x1,EXC_OPWORD(%a6) # word or long operation? | |
| sne %d7 # set d7 accordingly | |
| bra.w compandsetfetch | |
| ######## | |
| # At this stage, it would be nice if d0 held the FSLW. | |
| global _isp_cas_terminate | |
| _isp_cas_terminate: | |
| mov.l %d6,%sfc # restore previous sfc | |
| mov.l %d6,%dfc # restore previous dfc | |
| global _cas_terminate2 | |
| _cas_terminate2: | |
| mov.l %a0,%a2 # copy failing addr to a2 | |
| mov.l %d0,-(%sp) | |
| bsr.l isp_restore # restore An (if ()+ or -()) | |
| mov.l (%sp)+,%d0 | |
| addq.l &0x4,%sp # remove sub return addr | |
| subq.l &0x8,%sp # make room for bigger stack | |
| subq.l &0x8,%a6 # shift frame ptr down, too | |
| mov.l &26,%d1 # want to move 51 longwords | |
| lea 0x8(%sp),%a0 # get address of old stack | |
| lea 0x0(%sp),%a1 # get address of new stack | |
| cas_term_cont: | |
| mov.l (%a0)+,(%a1)+ # move a longword | |
| dbra.w %d1,cas_term_cont # keep going | |
| mov.w &0x4008,EXC_IVOFF(%a6) # put new stk fmt, voff | |
| mov.l %a2,EXC_IVOFF+0x2(%a6) # put faulting addr on stack | |
| mov.l %d0,EXC_IVOFF+0x6(%a6) # put FSLW on stack | |
| movm.l EXC_DREGS(%a6),&0x3fff # restore user regs | |
| unlk %a6 # unlink stack frame | |
| bra.l _real_access | |
| ######## | |
| global _isp_cas_inrange | |
| _isp_cas_inrange: | |
| clr.l %d0 # clear return result | |
| lea _CASHI(%pc),%a1 # load end of CAS core code | |
| cmp.l %a1,%a0 # is PC in range? | |
| blt.b cin_no # no | |
| lea _CASLO(%pc),%a1 # load begin of CAS core code | |
| cmp.l %a0,%a1 # is PC in range? | |
| blt.b cin_no # no | |
| rts # yes; return d0 = 0 | |
| cin_no: | |
| mov.l &-0x1,%d0 # out of range; return d0 = -1 | |
| rts | |
| ################################################################# | |
| ################################################################# | |
| ################################################################# | |
| # This is the start of the cas and cas2 "core" emulation code. # | |
| # This is the section that may need to be replaced by the host # | |
| # OS if it is too operating system-specific. # | |
| # Please refer to the package documentation to see how to # | |
| # "replace" this section, if necessary. # | |
| ################################################################# | |
| ################################################################# | |
| ################################################################# | |
| # ###### ## ###### #### | |
| # # # # # # # | |
| # # ###### ###### # | |
| # # # # # # | |
| # ###### # # ###### ###### | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _isp_cas2(): "core" emulation code for the cas2 instruction # | |
| # # | |
| # XREF **************************************************************** # | |
| # _isp_cas2_finish() - only exit point for this emulation code; # | |
| # do clean-up; calculate ccodes; store # | |
| # Compare Ops if appropriate. # | |
| # # | |
| # INPUT *************************************************************** # | |
| # *see chart below* # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # *see chart below* # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # (1) Make several copies of the effective address. # | |
| # (2) Save current SR; Then mask off all maskable interrupts. # | |
| # (3) Save current SFC/DFC (ASSUMED TO BE EQUAL!!!); Then set # | |
| # according to whether exception occurred in user or # | |
| # supervisor mode. # | |
| # (4) Use "plpaw" instruction to pre-load ATC with effective # | |
| # address pages(s). THIS SHOULD NOT FAULT!!! The relevant # | |
| # page(s) should have already been made resident prior to # | |
| # entering this routine. # | |
| # (5) Push the operand lines from the cache w/ "cpushl". # | |
| # In the 68040, this was done within the locked region. In # | |
| # the 68060, it is done outside of the locked region. # | |
| # (6) Use "plpar" instruction to do a re-load of ATC entries for # | |
| # ADDR1 since ADDR2 entries may have pushed ADDR1 out of the # | |
| # ATC. # | |
| # (7) Pre-fetch the core emulation instructions by executing # | |
| # one branch within each physical line (16 bytes) of the code # | |
| # before actually executing the code. # | |
| # (8) Load the BUSCR w/ the bus lock value. # | |
| # (9) Fetch the source operands using "moves". # | |
| # (10)Do the compares. If both equal, go to step (13). # | |
| # (11)Unequal. No update occurs. But, we do write the DST1 op # | |
| # back to itself (as w/ the '040) so we can gracefully unlock # | |
| # the bus (and assert LOCKE*) using BUSCR and the final move. # | |
| # (12)Exit. # | |
| # (13)Write update operand to the DST locations. Use BUSCR to # | |
| # assert LOCKE* for the final write operation. # | |
| # (14)Exit. # | |
| # # | |
| # The algorithm is actually implemented slightly differently # | |
| # depending on the size of the operation and the misalignment of the # | |
| # operands. A misaligned operand must be written in aligned chunks or # | |
| # else the BUSCR register control gets confused. # | |
| # # | |
| ######################################################################### | |
| ################################################################# | |
| # THIS IS THE STATE OF THE INTEGER REGISTER FILE UPON # | |
| # ENTERING _isp_cas2(). # | |
| # # | |
| # D0 = xxxxxxxx # | |
| # D1 = xxxxxxxx # | |
| # D2 = cmp operand 1 # | |
| # D3 = cmp operand 2 # | |
| # D4 = update oper 1 # | |
| # D5 = update oper 2 # | |
| # D6 = 'xxxxxxff if supervisor mode; 'xxxxxx00 if user mode # | |
| # D7 = 'xxxxxxff if longword operation; 'xxxxxx00 if word # | |
| # A0 = ADDR1 # | |
| # A1 = ADDR2 # | |
| # A2 = xxxxxxxx # | |
| # A3 = xxxxxxxx # | |
| # A4 = xxxxxxxx # | |
| # A5 = xxxxxxxx # | |
| # A6 = frame pointer # | |
| # A7 = stack pointer # | |
| ################################################################# | |
| # align 0x1000 | |
| # beginning label used by _isp_cas_inrange() | |
| global _CASLO | |
| _CASLO: | |
| global _isp_cas2 | |
| _isp_cas2: | |
| tst.b %d6 # user or supervisor mode? | |
| bne.b cas2_supervisor # supervisor | |
| cas2_user: | |
| movq.l &0x1,%d0 # load user data fc | |
| bra.b cas2_cont | |
| cas2_supervisor: | |
| movq.l &0x5,%d0 # load supervisor data fc | |
| cas2_cont: | |
| tst.b %d7 # word or longword? | |
| beq.w cas2w # word | |
| #### | |
| cas2l: | |
| mov.l %a0,%a2 # copy ADDR1 | |
| mov.l %a1,%a3 # copy ADDR2 | |
| mov.l %a0,%a4 # copy ADDR1 | |
| mov.l %a1,%a5 # copy ADDR2 | |
| addq.l &0x3,%a4 # ADDR1+3 | |
| addq.l &0x3,%a5 # ADDR2+3 | |
| mov.l %a2,%d1 # ADDR1 | |
| # mask interrupts levels 0-6. save old mask value. | |
| mov.w %sr,%d7 # save current SR | |
| ori.w &0x0700,%sr # inhibit interrupts | |
| # load the SFC and DFC with the appropriate mode. | |
| movc %sfc,%d6 # save old SFC/DFC | |
| movc %d0,%sfc # store new SFC | |
| movc %d0,%dfc # store new DFC | |
| # pre-load the operand ATC. no page faults should occur here because | |
| # _real_lock_page() should have taken care of this. | |
| plpaw (%a2) # load atc for ADDR1 | |
| plpaw (%a4) # load atc for ADDR1+3 | |
| plpaw (%a3) # load atc for ADDR2 | |
| plpaw (%a5) # load atc for ADDR2+3 | |
| # push the operand lines from the cache if they exist. | |
| cpushl %dc,(%a2) # push line for ADDR1 | |
| cpushl %dc,(%a4) # push line for ADDR1+3 | |
| cpushl %dc,(%a3) # push line for ADDR2 | |
| cpushl %dc,(%a5) # push line for ADDR2+2 | |
| mov.l %d1,%a2 # ADDR1 | |
| addq.l &0x3,%d1 | |
| mov.l %d1,%a4 # ADDR1+3 | |
| # if ADDR1 was ATC resident before the above "plpaw" and was executed | |
| # and it was the next entry scheduled for replacement and ADDR2 | |
| # shares the same set, then the "plpaw" for ADDR2 can push the ADDR1 | |
| # entries from the ATC. so, we do a second set of "plpa"s. | |
| plpar (%a2) # load atc for ADDR1 | |
| plpar (%a4) # load atc for ADDR1+3 | |
| # load the BUSCR values. | |
| mov.l &0x80000000,%a2 # assert LOCK* buscr value | |
| mov.l &0xa0000000,%a3 # assert LOCKE* buscr value | |
| mov.l &0x00000000,%a4 # buscr unlock value | |
| # there are three possible mis-aligned cases for longword cas. they | |
| # are separated because the final write which asserts LOCKE* must | |
| # be aligned. | |
| mov.l %a0,%d0 # is ADDR1 misaligned? | |
| andi.b &0x3,%d0 | |
| beq.b CAS2L_ENTER # no | |
| cmpi.b %d0,&0x2 | |
| beq.w CAS2L2_ENTER # yes; word misaligned | |
| bra.w CAS2L3_ENTER # yes; byte misaligned | |
| # | |
| # D0 = dst operand 1 <- | |
| # D1 = dst operand 2 <- | |
| # D2 = cmp operand 1 | |
| # D3 = cmp operand 2 | |
| # D4 = update oper 1 | |
| # D5 = update oper 2 | |
| # D6 = old SFC/DFC | |
| # D7 = old SR | |
| # A0 = ADDR1 | |
| # A1 = ADDR2 | |
| # A2 = bus LOCK* value | |
| # A3 = bus LOCKE* value | |
| # A4 = bus unlock value | |
| # A5 = xxxxxxxx | |
| # | |
| align 0x10 | |
| CAS2L_START: | |
| movc %a2,%buscr # assert LOCK* | |
| movs.l (%a1),%d1 # fetch Dest2[31:0] | |
| movs.l (%a0),%d0 # fetch Dest1[31:0] | |
| bra.b CAS2L_CONT | |
| CAS2L_ENTER: | |
| bra.b ~+16 | |
| CAS2L_CONT: | |
| cmp.l %d0,%d2 # Dest1 - Compare1 | |
| bne.b CAS2L_NOUPDATE | |
| cmp.l %d1,%d3 # Dest2 - Compare2 | |
| bne.b CAS2L_NOUPDATE | |
| movs.l %d5,(%a1) # Update2[31:0] -> DEST2 | |
| bra.b CAS2L_UPDATE | |
| bra.b ~+16 | |
| CAS2L_UPDATE: | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.l %d4,(%a0) # Update1[31:0] -> DEST1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.b cas2l_update_done | |
| bra.b ~+16 | |
| CAS2L_NOUPDATE: | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.l %d0,(%a0) # Dest1[31:0] -> DEST1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.b cas2l_noupdate_done | |
| bra.b ~+16 | |
| CAS2L_FILLER: | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| bra.b CAS2L_START | |
| #### | |
| ################################################################# | |
| # THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON # | |
| # ENTERING _isp_cas2(). # | |
| # # | |
| # D0 = destination[31:0] operand 1 # | |
| # D1 = destination[31:0] operand 2 # | |
| # D2 = cmp[31:0] operand 1 # | |
| # D3 = cmp[31:0] operand 2 # | |
| # D4 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required # | |
| # D5 = xxxxxxxx # | |
| # D6 = xxxxxxxx # | |
| # D7 = xxxxxxxx # | |
| # A0 = xxxxxxxx # | |
| # A1 = xxxxxxxx # | |
| # A2 = xxxxxxxx # | |
| # A3 = xxxxxxxx # | |
| # A4 = xxxxxxxx # | |
| # A5 = xxxxxxxx # | |
| # A6 = frame pointer # | |
| # A7 = stack pointer # | |
| ################################################################# | |
| cas2l_noupdate_done: | |
| # restore previous SFC/DFC value. | |
| movc %d6,%sfc # restore old SFC | |
| movc %d6,%dfc # restore old DFC | |
| # restore previous interrupt mask level. | |
| mov.w %d7,%sr # restore old SR | |
| sf %d4 # indicate no update was done | |
| bra.l _isp_cas2_finish | |
| cas2l_update_done: | |
| # restore previous SFC/DFC value. | |
| movc %d6,%sfc # restore old SFC | |
| movc %d6,%dfc # restore old DFC | |
| # restore previous interrupt mask level. | |
| mov.w %d7,%sr # restore old SR | |
| st %d4 # indicate update was done | |
| bra.l _isp_cas2_finish | |
| #### | |
| align 0x10 | |
| CAS2L2_START: | |
| movc %a2,%buscr # assert LOCK* | |
| movs.l (%a1),%d1 # fetch Dest2[31:0] | |
| movs.l (%a0),%d0 # fetch Dest1[31:0] | |
| bra.b CAS2L2_CONT | |
| CAS2L2_ENTER: | |
| bra.b ~+16 | |
| CAS2L2_CONT: | |
| cmp.l %d0,%d2 # Dest1 - Compare1 | |
| bne.b CAS2L2_NOUPDATE | |
| cmp.l %d1,%d3 # Dest2 - Compare2 | |
| bne.b CAS2L2_NOUPDATE | |
| movs.l %d5,(%a1) # Update2[31:0] -> Dest2 | |
| bra.b CAS2L2_UPDATE | |
| bra.b ~+16 | |
| CAS2L2_UPDATE: | |
| swap %d4 # get Update1[31:16] | |
| movs.w %d4,(%a0)+ # Update1[31:16] -> DEST1 | |
| movc %a3,%buscr # assert LOCKE* | |
| swap %d4 # get Update1[15:0] | |
| bra.b CAS2L2_UPDATE2 | |
| bra.b ~+16 | |
| CAS2L2_UPDATE2: | |
| movs.w %d4,(%a0) # Update1[15:0] -> DEST1+0x2 | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2l_update_done | |
| nop | |
| bra.b ~+16 | |
| CAS2L2_NOUPDATE: | |
| swap %d0 # get Dest1[31:16] | |
| movs.w %d0,(%a0)+ # Dest1[31:16] -> DEST1 | |
| movc %a3,%buscr # assert LOCKE* | |
| swap %d0 # get Dest1[15:0] | |
| bra.b CAS2L2_NOUPDATE2 | |
| bra.b ~+16 | |
| CAS2L2_NOUPDATE2: | |
| movs.w %d0,(%a0) # Dest1[15:0] -> DEST1+0x2 | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2l_noupdate_done | |
| nop | |
| bra.b ~+16 | |
| CAS2L2_FILLER: | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| bra.b CAS2L2_START | |
| ################################# | |
| align 0x10 | |
| CAS2L3_START: | |
| movc %a2,%buscr # assert LOCK* | |
| movs.l (%a1),%d1 # fetch Dest2[31:0] | |
| movs.l (%a0),%d0 # fetch Dest1[31:0] | |
| bra.b CAS2L3_CONT | |
| CAS2L3_ENTER: | |
| bra.b ~+16 | |
| CAS2L3_CONT: | |
| cmp.l %d0,%d2 # Dest1 - Compare1 | |
| bne.b CAS2L3_NOUPDATE | |
| cmp.l %d1,%d3 # Dest2 - Compare2 | |
| bne.b CAS2L3_NOUPDATE | |
| movs.l %d5,(%a1) # Update2[31:0] -> DEST2 | |
| bra.b CAS2L3_UPDATE | |
| bra.b ~+16 | |
| CAS2L3_UPDATE: | |
| rol.l &0x8,%d4 # get Update1[31:24] | |
| movs.b %d4,(%a0)+ # Update1[31:24] -> DEST1 | |
| swap %d4 # get Update1[23:8] | |
| movs.w %d4,(%a0)+ # Update1[23:8] -> DEST1+0x1 | |
| bra.b CAS2L3_UPDATE2 | |
| bra.b ~+16 | |
| CAS2L3_UPDATE2: | |
| rol.l &0x8,%d4 # get Update1[7:0] | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.b %d4,(%a0) # Update1[7:0] -> DEST1+0x3 | |
| bra.b CAS2L3_UPDATE3 | |
| nop | |
| bra.b ~+16 | |
| CAS2L3_UPDATE3: | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2l_update_done | |
| nop | |
| nop | |
| nop | |
| bra.b ~+16 | |
| CAS2L3_NOUPDATE: | |
| rol.l &0x8,%d0 # get Dest1[31:24] | |
| movs.b %d0,(%a0)+ # Dest1[31:24] -> DEST1 | |
| swap %d0 # get Dest1[23:8] | |
| movs.w %d0,(%a0)+ # Dest1[23:8] -> DEST1+0x1 | |
| bra.b CAS2L3_NOUPDATE2 | |
| bra.b ~+16 | |
| CAS2L3_NOUPDATE2: | |
| rol.l &0x8,%d0 # get Dest1[7:0] | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.b %d0,(%a0) # Update1[7:0] -> DEST1+0x3 | |
| bra.b CAS2L3_NOUPDATE3 | |
| nop | |
| bra.b ~+16 | |
| CAS2L3_NOUPDATE3: | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2l_noupdate_done | |
| nop | |
| nop | |
| nop | |
| bra.b ~+14 | |
| CAS2L3_FILLER: | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| bra.w CAS2L3_START | |
| ############################################################# | |
| ############################################################# | |
| cas2w: | |
| mov.l %a0,%a2 # copy ADDR1 | |
| mov.l %a1,%a3 # copy ADDR2 | |
| mov.l %a0,%a4 # copy ADDR1 | |
| mov.l %a1,%a5 # copy ADDR2 | |
| addq.l &0x1,%a4 # ADDR1+1 | |
| addq.l &0x1,%a5 # ADDR2+1 | |
| mov.l %a2,%d1 # ADDR1 | |
| # mask interrupt levels 0-6. save old mask value. | |
| mov.w %sr,%d7 # save current SR | |
| ori.w &0x0700,%sr # inhibit interrupts | |
| # load the SFC and DFC with the appropriate mode. | |
| movc %sfc,%d6 # save old SFC/DFC | |
| movc %d0,%sfc # store new SFC | |
| movc %d0,%dfc # store new DFC | |
| # pre-load the operand ATC. no page faults should occur because | |
| # _real_lock_page() should have taken care of this. | |
| plpaw (%a2) # load atc for ADDR1 | |
| plpaw (%a4) # load atc for ADDR1+1 | |
| plpaw (%a3) # load atc for ADDR2 | |
| plpaw (%a5) # load atc for ADDR2+1 | |
| # push the operand cache lines from the cache if they exist. | |
| cpushl %dc,(%a2) # push line for ADDR1 | |
| cpushl %dc,(%a4) # push line for ADDR1+1 | |
| cpushl %dc,(%a3) # push line for ADDR2 | |
| cpushl %dc,(%a5) # push line for ADDR2+1 | |
| mov.l %d1,%a2 # ADDR1 | |
| addq.l &0x3,%d1 | |
| mov.l %d1,%a4 # ADDR1+3 | |
| # if ADDR1 was ATC resident before the above "plpaw" and was executed | |
| # and it was the next entry scheduled for replacement and ADDR2 | |
| # shares the same set, then the "plpaw" for ADDR2 can push the ADDR1 | |
| # entries from the ATC. so, we do a second set of "plpa"s. | |
| plpar (%a2) # load atc for ADDR1 | |
| plpar (%a4) # load atc for ADDR1+3 | |
| # load the BUSCR values. | |
| mov.l &0x80000000,%a2 # assert LOCK* buscr value | |
| mov.l &0xa0000000,%a3 # assert LOCKE* buscr value | |
| mov.l &0x00000000,%a4 # buscr unlock value | |
| # there are two possible mis-aligned cases for word cas. they | |
| # are separated because the final write which asserts LOCKE* must | |
| # be aligned. | |
| mov.l %a0,%d0 # is ADDR1 misaligned? | |
| btst &0x0,%d0 | |
| bne.w CAS2W2_ENTER # yes | |
| bra.b CAS2W_ENTER # no | |
| # | |
| # D0 = dst operand 1 <- | |
| # D1 = dst operand 2 <- | |
| # D2 = cmp operand 1 | |
| # D3 = cmp operand 2 | |
| # D4 = update oper 1 | |
| # D5 = update oper 2 | |
| # D6 = old SFC/DFC | |
| # D7 = old SR | |
| # A0 = ADDR1 | |
| # A1 = ADDR2 | |
| # A2 = bus LOCK* value | |
| # A3 = bus LOCKE* value | |
| # A4 = bus unlock value | |
| # A5 = xxxxxxxx | |
| # | |
| align 0x10 | |
| CAS2W_START: | |
| movc %a2,%buscr # assert LOCK* | |
| movs.w (%a1),%d1 # fetch Dest2[15:0] | |
| movs.w (%a0),%d0 # fetch Dest1[15:0] | |
| bra.b CAS2W_CONT2 | |
| CAS2W_ENTER: | |
| bra.b ~+16 | |
| CAS2W_CONT2: | |
| cmp.w %d0,%d2 # Dest1 - Compare1 | |
| bne.b CAS2W_NOUPDATE | |
| cmp.w %d1,%d3 # Dest2 - Compare2 | |
| bne.b CAS2W_NOUPDATE | |
| movs.w %d5,(%a1) # Update2[15:0] -> DEST2 | |
| bra.b CAS2W_UPDATE | |
| bra.b ~+16 | |
| CAS2W_UPDATE: | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.w %d4,(%a0) # Update1[15:0] -> DEST1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.b cas2w_update_done | |
| bra.b ~+16 | |
| CAS2W_NOUPDATE: | |
| movc %a3,%buscr # assert LOCKE* | |
| movs.w %d0,(%a0) # Dest1[15:0] -> DEST1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.b cas2w_noupdate_done | |
| bra.b ~+16 | |
| CAS2W_FILLER: | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| bra.b CAS2W_START | |
| #### | |
| ################################################################# | |
| # THIS MUST BE THE STATE OF THE INTEGER REGISTER FILE UPON # | |
| # ENTERING _isp_cas2(). # | |
| # # | |
| # D0 = destination[15:0] operand 1 # | |
| # D1 = destination[15:0] operand 2 # | |
| # D2 = cmp[15:0] operand 1 # | |
| # D3 = cmp[15:0] operand 2 # | |
| # D4 = 'xxxxxx11 -> no reg update; 'xxxxxx00 -> update required # | |
| # D5 = xxxxxxxx # | |
| # D6 = xxxxxxxx # | |
| # D7 = xxxxxxxx # | |
| # A0 = xxxxxxxx # | |
| # A1 = xxxxxxxx # | |
| # A2 = xxxxxxxx # | |
| # A3 = xxxxxxxx # | |
| # A4 = xxxxxxxx # | |
| # A5 = xxxxxxxx # | |
| # A6 = frame pointer # | |
| # A7 = stack pointer # | |
| ################################################################# | |
| cas2w_noupdate_done: | |
| # restore previous SFC/DFC value. | |
| movc %d6,%sfc # restore old SFC | |
| movc %d6,%dfc # restore old DFC | |
| # restore previous interrupt mask level. | |
| mov.w %d7,%sr # restore old SR | |
| sf %d4 # indicate no update was done | |
| bra.l _isp_cas2_finish | |
| cas2w_update_done: | |
| # restore previous SFC/DFC value. | |
| movc %d6,%sfc # restore old SFC | |
| movc %d6,%dfc # restore old DFC | |
| # restore previous interrupt mask level. | |
| mov.w %d7,%sr # restore old SR | |
| st %d4 # indicate update was done | |
| bra.l _isp_cas2_finish | |
| #### | |
| align 0x10 | |
| CAS2W2_START: | |
| movc %a2,%buscr # assert LOCK* | |
| movs.w (%a1),%d1 # fetch Dest2[15:0] | |
| movs.w (%a0),%d0 # fetch Dest1[15:0] | |
| bra.b CAS2W2_CONT2 | |
| CAS2W2_ENTER: | |
| bra.b ~+16 | |
| CAS2W2_CONT2: | |
| cmp.w %d0,%d2 # Dest1 - Compare1 | |
| bne.b CAS2W2_NOUPDATE | |
| cmp.w %d1,%d3 # Dest2 - Compare2 | |
| bne.b CAS2W2_NOUPDATE | |
| movs.w %d5,(%a1) # Update2[15:0] -> DEST2 | |
| bra.b CAS2W2_UPDATE | |
| bra.b ~+16 | |
| CAS2W2_UPDATE: | |
| ror.l &0x8,%d4 # get Update1[15:8] | |
| movs.b %d4,(%a0)+ # Update1[15:8] -> DEST1 | |
| movc %a3,%buscr # assert LOCKE* | |
| rol.l &0x8,%d4 # get Update1[7:0] | |
| bra.b CAS2W2_UPDATE2 | |
| bra.b ~+16 | |
| CAS2W2_UPDATE2: | |
| movs.b %d4,(%a0) # Update1[7:0] -> DEST1+0x1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2w_update_done | |
| nop | |
| bra.b ~+16 | |
| CAS2W2_NOUPDATE: | |
| ror.l &0x8,%d0 # get Dest1[15:8] | |
| movs.b %d0,(%a0)+ # Dest1[15:8] -> DEST1 | |
| movc %a3,%buscr # assert LOCKE* | |
| rol.l &0x8,%d0 # get Dest1[7:0] | |
| bra.b CAS2W2_NOUPDATE2 | |
| bra.b ~+16 | |
| CAS2W2_NOUPDATE2: | |
| movs.b %d0,(%a0) # Dest1[7:0] -> DEST1+0x1 | |
| movc %a4,%buscr # unlock the bus | |
| bra.w cas2w_noupdate_done | |
| nop | |
| bra.b ~+16 | |
| CAS2W2_FILLER: | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| nop | |
| bra.b CAS2W2_START | |
| # ###### ## ###### | |
| # # # # # | |
| # # ###### ###### | |
| # # # # # | |
| # ###### # # ###### | |
| ######################################################################### | |
| # XDEF **************************************************************** # | |
| # _isp_cas(): "core" emulation code for the cas instruction # | |
| # # | |
| # XREF **************************************************************** # | |
| # _isp_cas_finish() - only exit point for this emulation code; # | |
| # do clean-up # | |
| # # | |
| # INPUT *************************************************************** # | |
| # *see entry chart below* # | |
| # # | |
| # OUTPUT ************************************************************** # | |
| # *see exit chart below* # | |
| # # | |
| # ALGORITHM *********************************************************** # | |
| # (1) Make several copies of the effective address. # | |
| # (2) Save current SR; Then mask off all maskable interrupts. # | |
| # (3) Save current DFC/SFC (ASSUMED TO BE EQUAL!!!); Then set # | |
| # SFC/DFC according to whether exception occurred in user or # | |
| # supervisor mode. # | |
| # (4) Use "plpaw" instruction to pre-load ATC with efective # | |
| # address page(s). THIS SHOULD NOT FAULT!!! The relevant # | |
| # page(s) should have been made resident prior to entering # | |
| # this routine. # | |
| # (5) Push the operand lines from the cache w/ "cpushl". # | |
| # In the 68040, this was done within the locked region. In # | |
| # the 68060, it is done outside of the locked region. # | |
| # (6) Pre-fetch the core emulation instructions by executing one # | |
| # branch within each physical line (16 bytes) of the code # | |
| # before actually executing the code. # | |
| # (7) Load the BUSCR with the bus lock value. # | |
| # (8) Fetch the source operand. # | |
| # (9) Do the compare. If equal, go to step (12). # | |
| # (10)Unequal. No update occurs. But, we do write the DST op back # | |
| # to itself (as w/ the '040) so we can gracefully unlock # | |
| # the bus (and assert LOCKE*) using BUSCR and the final move. # | |
| # (11)Exit. # | |
| # (12)Write update operand to the DST location. Use BUSCR to # | |
| # assert LOCKE* for the final write operation. # | |
| # (13)Exit. # | |
| # # | |
| # The algorithm is actually implemented slightly differently # | |
| # 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: |